package utils import ( "bubble/types" "errors" "fmt" "github.com/expr-lang/expr" "io" "io/fs" "net/http" "os" "os/exec" "regexp" "strconv" "strings" ) // CalculateSelectOptionsIndex рассчитывает индекс первой и последней опции для отображения в поле выбора func CalculateSelectOptionsIndex(focus, visibleCount, optionsCount int) (int, int) { //Если количество отображаемых больше, чем есть количество опций на самом деле, //то меняем количество опций для отображения на количество опций. if visibleCount > optionsCount { visibleCount = optionsCount } //Количество объектов сверху и снизу half := visibleCount / 2 //Индекс первой опции для показа в списке start := focus - half //Если индекс меньше 0, то фиксируем его на первой if start < 0 { start = 0 } //Индекс последней опции для показа в списке end := start + visibleCount //Если индекс больше, чем количество опций для вывода, //то фиксируем его на последней опции. //Производим перерасчет индекса первой опции if end > optionsCount { end = optionsCount start = end - visibleCount if start < 0 { start = 0 } } return start, end } // CopyFile копирование файла в новое место func CopyFile(src, dst string) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer out.Close() _, err = io.Copy(out, in) return err } // ParamsForCompute получение словаря переменных без приведения типов, для вычисления func ParamsForCompute(app *types.PackageApplication) map[string]interface{} { params := make(map[string]interface{}) for i := range app.Variables { v := &app.Variables[i] value := v.Value if value == nil { value = v.Default } params[v.Name] = value } // Добавляем значения из активных полей for _, section := range app.Sections { for _, field := range section.Fields { if field.Variable == nil || !field.Active { continue } value := field.Variable.Value if value == nil { value = field.Variable.Default } params[field.VariableName] = value } } return params } // ParamsForCondition получение словаря переменных с приведением типов для проверки условий func ParamsForCondition(app *types.PackageApplication) map[string]interface{} { params := make(map[string]interface{}) for _, variable := range app.Variables { value := variable.Value if value == nil { value = variable.Default } // Приводим к нужному типу, как в твоём старом коде switch variable.Type { case "bool": if s, ok := value.(string); ok { value = strings.ToLower(s) == "true" } case "number", "int", "float": if s, ok := value.(string); ok { if n, err := strconv.ParseFloat(s, 64); err == nil { value = n } } } params[variable.Name] = value } // Также добавляем значения из полей секций (на случай, если переменная используется только в поле) for _, section := range app.Sections { for _, field := range section.Fields { if field.Variable == nil || !field.Active { continue } value := field.Variable.Value if value == nil { value = field.Variable.Default } switch field.Variable.Type { case "bool": if s, ok := value.(string); ok { value = strings.ToLower(s) == "true" } case "number", "int", "float": if s, ok := value.(string); ok { if n, err := strconv.ParseFloat(s, 64); err == nil { value = n } } } params[field.VariableName] = value } } return params } // DefineExprCustomFunctions возвращает собственные функции expr func DefineExprCustomFunctions() []expr.Option { return []expr.Option{ //вычисляет значение по заданному регулярному выражению expr.Function("regex", func(args ...interface{}) (interface{}, error) { if len(args) != 3 { return "", fmt.Errorf("regex() ожидает 3 аргумента") } input := fmt.Sprintf("%v", args[0]) pattern := fmt.Sprintf("%v", args[1]) repl := fmt.Sprintf("%v", args[2]) re, err := regexp.Compile(pattern) if err != nil { return input, nil } return re.ReplaceAllString(input, repl), nil }, new(func(string, string, string) string), ), //проверяет совпадение переданного значения и указанного регулярного выражения expr.Function("regex_match", func(args ...interface{}) (interface{}, error) { if len(args) != 2 { return false, fmt.Errorf("regex_match() ожидает 2 аргумента") } input := fmt.Sprintf("%v", args[0]) pattern := fmt.Sprintf("%v", args[1]) re, err := regexp.Compile(pattern) if err != nil { return false, fmt.Errorf("некоректный regex %s, err:%v", pattern, err) } return re.MatchString(input), nil }, new(func(string, string) bool), ), //возвращает собственный текст ошибки expr.Function("error", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return nil, fmt.Errorf("error() требует сообщения") } return nil, fmt.Errorf("%v", args[0]) }, new(func(string) error), ), //возвращает тело ответа по указанному адресу expr.Function("http_get", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return "", nil } url := fmt.Sprintf("%v", args[0]) resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) return string(body), nil }, new(func(string) string), ), //возвращает результат выполнения команды из оболочки expr.Function("shell", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return "", nil } cmd := fmt.Sprintf("%v", args[0]) parts := strings.Fields(cmd) if len(parts) == 0 { return "", nil } out, err := exec.Command(parts[0], parts[1:]...).CombinedOutput() if err != nil { return "", err } return strings.TrimSpace(string(out)), nil }, new(func(string) error), ), //возвращает значение указанной переменной из env expr.Function("env", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return "", nil } return os.Getenv(fmt.Sprintf("%v", args[0])), nil }, new(func(string) error), ), //проверяет наличие файла в системе expr.Function("file_exists", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return false, nil } path := fmt.Sprintf("%v", args[0]) fileInfo, err := os.Stat(path) if err == nil && !fileInfo.IsDir() { return true, nil } else if err == nil && fileInfo.IsDir() { return false, fmt.Errorf("file is dir") } if errors.Is(err, fs.ErrNotExist) { return false, nil } else { return false, err } }, new(func(string) bool), ), //проверяет наличие директории в системе expr.Function("dir_exists", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return false, nil } path := fmt.Sprintf("%v", args[0]) fileInfo, err := os.Stat(path) if err == nil && fileInfo.IsDir() { return true, nil } else if err == nil && !fileInfo.IsDir() { return false, fmt.Errorf("file is not a dir") } if errors.Is(err, fs.ErrNotExist) { return false, nil } else { return false, err } }, new(func(string) bool), ), //проверяет доступность порта expr.Function("port_free", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return false, nil } port := fmt.Sprintf("%v", args[0]) cmd := exec.Command("ss", "-ltn", "sport =: "+port) return cmd.Run() != nil, nil }, new(func(string) error), ), //проверяет наличие пользователя в системе expr.Function("user_exists", func(args ...interface{}) (interface{}, error) { if len(args) == 0 { return false, nil } name := fmt.Sprintf("%v", args[0]) cmd := exec.Command("id", "-u", name) return cmd.Run() == nil, nil }, new(func(string) bool), ), } }