// Package params — слияние, фильтрация, анализ параметров и ForceNew. // // Основные операции: // - Merge — объединяет параметры из нескольких операций по Code // - ComputeCreateOnly — поля, которые есть в create, но отсутствуют в modify // - BuildSubresourceForceNewCodes — поля, требующие пересоздания subresource // - Analyze — определяет используемые типы (bool/int64/string) для импортов // - AnalyzePlanModifiers — ForceNew-поля, требующие planmodifier.*.RequiresReplace() package params import ( "sort" "strings" "resource-generator/internal/types" ) // Merge объединяет несколько наборов параметров, убирая дубликаты по Code. func Merge(sets ...[]types.Param) []types.Param { seen := map[string]types.Param{} for _, s := range sets { for _, p := range s { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if _, ok := seen[key]; !ok { seen[key] = p } } } out := make([]types.Param, 0, len(seen)) for _, p := range seen { out = append(out, p) } sort.Slice(out, func(i, j int) bool { return out[i].Code < out[j].Code }) return out } // AlignParamTypes выравнивает типы параметров по схеме. func AlignParamTypes(params []types.Param, schema []types.Param) []types.Param { if len(params) == 0 || len(schema) == 0 { return params } codeType := make(map[string]string, len(schema)) for _, p := range schema { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if strings.TrimSpace(p.Type) != "" { codeType[key] = p.Type } } if len(codeType) == 0 { return params } for i, p := range params { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if t, ok := codeType[key]; ok && strings.TrimSpace(t) != "" { params[i].Type = t } } return params } // ComputeCreateOnly вычисляет набор полей, которые есть только в create (не в modify). func ComputeCreateOnly(createParams []types.Param, modifyParams []types.Param) map[string]struct{} { modifyCodes := make(map[string]struct{}, len(modifyParams)) for _, p := range modifyParams { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } modifyCodes[key] = struct{}{} } createOnly := make(map[string]struct{}) for _, p := range createParams { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if _, ok := modifyCodes[key]; !ok { createOnly[key] = struct{}{} } } return createOnly } // MarkCreateOnly проставляет CreateOnly=true для полей, которых нет в modify. func MarkCreateOnly(schema []types.Param, createOnly map[string]struct{}) []types.Param { if len(schema) == 0 || len(createOnly) == 0 { return schema } for i, p := range schema { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if _, ok := createOnly[key]; ok { schema[i].CreateOnly = true } } return schema } // FilterCreateOnly возвращает только CreateOnly-параметры. func FilterCreateOnly(schema []types.Param) []types.Param { if len(schema) == 0 { return []types.Param{} } result := make([]types.Param, 0, len(schema)) for _, p := range schema { if p.CreateOnly { result = append(result, p) } } return result } // FilterCreateOnlyRequired возвращает обязательные CreateOnly-параметры. func FilterCreateOnlyRequired(schema []types.Param) []types.Param { if len(schema) == 0 { return []types.Param{} } result := make([]types.Param, 0, len(schema)) for _, p := range schema { if p.CreateOnly && p.Required { result = append(result, p) } } return result } // Analyze анализирует типы параметров и проставляет флаги uses/needs. func Analyze(usesBool *bool, usesInt64 *bool, usesString *bool, hasDefaults *bool, needsBoolDefault *bool, needsInt64Default *bool, needsStringDefault *bool, params []types.Param) { for _, p := range params { switch strings.ToLower(p.Type) { case "bool": *usesBool = true if p.Default != "" { *needsBoolDefault = true *hasDefaults = true } case "int", "int64", "number": *usesInt64 = true if p.Default != "" { *needsInt64Default = true *hasDefaults = true } default: *usesString = true if p.Default != "" { *needsStringDefault = true *hasDefaults = true } } } } // BuildSubresourceForceNewCodes строит карту параметров, требующих ForceNew. func BuildSubresourceForceNewCodes(sr *types.GenSubresource, createOnly map[string]struct{}) map[string]struct{} { forceNew := map[string]struct{}{} if sr.ModifyOpName == "" { for _, p := range sr.SchemaParams { key := strings.ToLower(strings.TrimSpace(p.Code)) if key != "" { forceNew[key] = struct{}{} } } return forceNew } for key := range createOnly { forceNew[key] = struct{}{} } for _, p := range sr.DeleteParams { key := strings.ToLower(strings.TrimSpace(p.Code)) if key != "" { forceNew[key] = struct{}{} } } return forceNew } // MarkForceNew проставляет ForceNew=true для указанных параметров. func MarkForceNew(params []types.Param, forceNew map[string]struct{}) []types.Param { if len(params) == 0 || len(forceNew) == 0 { return params } for i, p := range params { key := strings.ToLower(strings.TrimSpace(p.Code)) if key == "" { continue } if _, ok := forceNew[key]; ok { params[i].ForceNew = true } } return params } // FilterForceNew возвращает только ForceNew-параметры. func FilterForceNew(params []types.Param) []types.Param { result := []types.Param{} for _, p := range params { if p.ForceNew { result = append(result, p) } } return result } // AnalyzeJsonPlanMod возвращает true, если хотя бы один параметр — JSON. func AnalyzeJsonPlanMod(params []types.Param) bool { for _, p := range params { if p.IsJson { return true } } return false } // HasRestoreCasingParams возвращает true, если есть ref_svc-параметры (кроме S3). func HasRestoreCasingParams(params []types.Param) bool { for _, p := range params { if p.RefSvcId > 0 && p.RefSvcId != 12 && strings.EqualFold(strings.TrimSpace(p.Type), "string") { return true } } return false } // AnalyzeNeedsStrings возвращает true, если есть строковые параметры. func AnalyzeNeedsStrings(params []types.Param) bool { for _, p := range params { t := strings.ToLower(strings.TrimSpace(p.Type)) if t != "bool" && t != "int" && t != "int64" && t != "number" { return true } } return false } // AnalyzePlanModifiers анализирует ForceNew-параметры для добавления planmodifier-импортов. func AnalyzePlanModifiers(needsBool *bool, needsInt64 *bool, needsString *bool, params []types.Param) { for _, p := range params { if !p.ForceNew { continue } switch strings.ToLower(strings.TrimSpace(p.Type)) { case "bool": *needsBool = true case "int", "int64", "number": *needsInt64 = true default: *needsString = true } } }