249 lines
5.3 KiB
Go
249 lines
5.3 KiB
Go
package cgpcli
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// getMapAny — получение словаря (map[string]any) от сервера.
|
|
func (cli *Cli) getMapAny(cmd string, args ...any) (map[string]any, error) {
|
|
res, err := cli.Query(cmd, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m, ok := res.(map[string]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s: expected map, got %T", cmd, res)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// getMapString — получение словаря (map[string]string) от сервера.
|
|
func (cli *Cli) getMapString(cmd string, args ...any) (map[string]string, error) {
|
|
res, err := cli.getMapAny(cmd, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return toStringMap(cmd, res)
|
|
}
|
|
|
|
// getSliceAny — получение слайса ([]any) от сервера.
|
|
func (cli *Cli) getSliceAny(cmd string, args ...any) ([]any, error) {
|
|
res, err := cli.Query(cmd, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s, ok := res.([]any)
|
|
if !ok {
|
|
if res == nil {
|
|
return []any{}, nil
|
|
}
|
|
return nil, fmt.Errorf("%s: expected []any, got %T", cmd, res)
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// getSliceByte — получение слайса ([]byte) от сервера.
|
|
func (cli *Cli) getSliceByte(cmd string, args ...any) ([]byte, error) {
|
|
res, err := cli.Query(cmd, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s, ok := res.([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s: expected []byte, got %T", cmd, res)
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// getSliceString — получение слайса ([]string) от сервера.
|
|
func (cli *Cli) getSliceString(cmd string, args ...any) ([]string, error) {
|
|
raw, err := cli.getSliceAny(cmd, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := make([]string, len(raw))
|
|
for i, v := range raw {
|
|
s, err := toString(cmd, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res[i] = s
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// getString — получение строки (string) от сервера.
|
|
func (cli *Cli) getString(cmd string, args ...any) (string, error) {
|
|
res, err := cli.Query(cmd, args...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
s, ok := res.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("%s: response is %T, expected string", cmd, res)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// parseFileInfoMap converts a raw map from CGP (with ST* keys) into a map of FileInfo structures.
|
|
func parseFileInfoMap(raw map[string]any) map[string]*FileInfo {
|
|
res := make(map[string]*FileInfo, len(raw))
|
|
for name, attrs := range raw {
|
|
attrMap, ok := attrs.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
res[name] = &FileInfo{
|
|
Name: name,
|
|
Size: toInt64(attrMap["STFileSize"]),
|
|
Created: toTime(attrMap["STCreated"]),
|
|
Modified: toTime(attrMap["STModified"]),
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func replaceSpecChars(s string) string {
|
|
var sb strings.Builder
|
|
sb.Grow(len(s) + 64)
|
|
|
|
for _, symbol := range s {
|
|
switch symbol {
|
|
case '\\':
|
|
// replace \ -> \\ (CGP backslash)
|
|
sb.WriteString(`\\`)
|
|
|
|
case '"':
|
|
// replace \" -> \\" (CGP quote)
|
|
sb.WriteString(`\"`)
|
|
|
|
case '\n':
|
|
// replace \n -> \\e (CGP End-of-Line)
|
|
sb.WriteString(`\e`)
|
|
|
|
case '\t':
|
|
// replace \t -> \\t (CGP Tab)
|
|
sb.WriteString(`\t`)
|
|
|
|
case '\r', 0x0000:
|
|
continue
|
|
|
|
default:
|
|
sb.WriteRune(symbol)
|
|
}
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// toAnySlice — приведение к слайсу []any.
|
|
func toAnySlice(v any) []any {
|
|
if s, ok := v.([]any); ok {
|
|
return s
|
|
}
|
|
return []any{}
|
|
}
|
|
|
|
// toInt — приведение различных числовых типов к int.
|
|
func toInt(v any) int {
|
|
switch val := v.(type) {
|
|
case int:
|
|
return val
|
|
case int64:
|
|
return int(val)
|
|
case float64:
|
|
return int(val)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// toInt64 приведение различных числовых типов к int64.
|
|
func toInt64(v any) int64 {
|
|
switch val := v.(type) {
|
|
case int64:
|
|
return val
|
|
case int:
|
|
return int64(val)
|
|
case int32:
|
|
return int64(val)
|
|
case float64:
|
|
return int64(val)
|
|
case float32:
|
|
return int64(val)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func toString(cmd string, v any) (string, error) {
|
|
switch val := v.(type) {
|
|
case string:
|
|
return val, nil
|
|
case time.Time:
|
|
return val.Format("02-Jan-2006 15:04:05"), nil
|
|
case int:
|
|
return strconv.Itoa(val), nil
|
|
case int64:
|
|
return strconv.FormatInt(val, 10), nil
|
|
case float64:
|
|
return strconv.FormatFloat(val, 'f', -1, 64), nil
|
|
case bool:
|
|
return strconv.FormatBool(val), nil
|
|
case nil:
|
|
return "", nil
|
|
default:
|
|
return "", fmt.Errorf("%s: unexpected value type %T (value: %v)", cmd, v, v)
|
|
}
|
|
}
|
|
|
|
// toStringMap — конвертирует map[string]any в map[string]string.
|
|
func toStringMap(cmd string, data map[string]any) (map[string]string, error) {
|
|
res := make(map[string]string, len(data))
|
|
for k, v := range data {
|
|
s, err := toString(cmd, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res[k] = s
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// toStringSlice - конвертирует []any в []string
|
|
func toStringSlice(cmd string, v any) ([]string, error) {
|
|
arr, ok := v.([]any)
|
|
if !ok {
|
|
if v == nil {
|
|
return []string{}, nil
|
|
}
|
|
return nil, fmt.Errorf("%s: expected []any, got %T", cmd, v)
|
|
}
|
|
res := make([]string, len(arr))
|
|
for i, val := range arr {
|
|
s, err := toString(cmd, val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res[i] = s
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func toTime(v any) time.Time {
|
|
if t, ok := v.(time.Time); ok {
|
|
return t
|
|
}
|
|
return time.Time{}
|
|
}
|