(ctx *AttributeContext, att *expr.AttributeExpr, put expr.UserType, target, context string, req, view bool, seen map[string]*bytes.Buffer)
| 261 | } |
| 262 | |
| 263 | func validateAttribute(ctx *AttributeContext, att *expr.AttributeExpr, put expr.UserType, target, context string, req, view bool, seen map[string]*bytes.Buffer) string { |
| 264 | ut, isUT := att.Type.(expr.UserType) |
| 265 | if !isUT { |
| 266 | code := recurseValidationCode(att, put, ctx, req, false, view, target, context, seen).String() |
| 267 | if code == "" { |
| 268 | return "" |
| 269 | } |
| 270 | if expr.IsArray(att.Type) || expr.IsMap(att.Type) || expr.IsUnion(att.Type) { |
| 271 | return code |
| 272 | } |
| 273 | if !ctx.Pointer && (req || (att.DefaultValue != nil && ctx.UseDefault)) { |
| 274 | return code |
| 275 | } |
| 276 | cond := fmt.Sprintf("if %s != nil {\n", target) |
| 277 | if strings.HasPrefix(code, cond) { |
| 278 | return code |
| 279 | } |
| 280 | return fmt.Sprintf("%s%s\n}", cond, code) |
| 281 | } |
| 282 | // Alias user types: validate underlying attribute with alias flag so that |
| 283 | // validation operates on the base value type while preserving pointer |
| 284 | // semantics from the current attribute context. |
| 285 | if expr.IsAlias(ut) { |
| 286 | // Preserve field-level attributes (e.g., DefaultValue, Required) while |
| 287 | // validating alias user types against their underlying base. Passing |
| 288 | // the original attribute with alias=true ensures validations operate |
| 289 | // on the correct value type without dropping field defaults. |
| 290 | code := recurseValidationCode(att, put, ctx, req, true, view, target, context, seen).String() |
| 291 | if code == "" { |
| 292 | return "" |
| 293 | } |
| 294 | // For optional pointer fields, wrap validation code in nil check |
| 295 | if !ctx.Pointer && (req || (att.DefaultValue != nil && ctx.UseDefault)) { |
| 296 | return code |
| 297 | } |
| 298 | cond := fmt.Sprintf("if %s != nil {\n", target) |
| 299 | if strings.HasPrefix(code, cond) { |
| 300 | return code |
| 301 | } |
| 302 | return fmt.Sprintf("%s%s\n}", cond, code) |
| 303 | } |
| 304 | if !hasValidations(ctx, ut) { |
| 305 | return "" |
| 306 | } |
| 307 | var buf bytes.Buffer |
| 308 | name := ctx.Scope.Name(att, "", ctx.Pointer, ctx.UseDefault) |
| 309 | // Use the scoped type name directly to preserve identifiers such as |
| 310 | // protocol buffer-reserved names that include a trailing underscore |
| 311 | // (e.g., Message_). Applying Goify here would drop underscores and |
| 312 | // cause mismatches between function declarations and call sites. |
| 313 | data := map[string]any{"name": name, "target": target} |
| 314 | if err := userValT.Execute(&buf, data); err != nil { |
| 315 | panic(err) // bug |
| 316 | } |
| 317 | return fmt.Sprintf("if %s != nil {\n\t%s\n}", target, buf.String()) |
| 318 | } |
| 319 | |
| 320 | // validationCode produces Go code that runs the validations defined in the |
no test coverage detected