// attributeTags computes the struct field tags. func attributeTags(parent, att *design.AttributeDefinition, name string, private bool) string { var elems []string keys := make([]string, len(att.Metadata)) i := 0 for k := range att.Metadata { keys[i] = k i++ } sort.Strings(keys) for _, key := range keys { val := att.Metadata[key] if strings.HasPrefix(key, "struct:tag:") { name := key[11:] value := strings.Join(val, ",") elems = append(elems, fmt.Sprintf("%s:\"%s\"", name, value)) } } if len(elems) > 0 { return " `" + strings.Join(elems, " ") + "`" } // Default algorithm var omit string if private || (!parent.IsRequired(name) && !parent.HasDefaultValue(name)) { omit = ",omitempty" } return fmt.Sprintf(" `form:\"%s%s\" json:\"%s%s\" xml:\"%s%s\"`", name, omit, name, omit, name, omit) }
// RecursiveFinalizer produces Go code that sets the default values for fields recursively for the // given attribute. func RecursiveFinalizer(att *design.AttributeDefinition, target string, depth int, vs ...map[string]bool) string { var assignments []string if o := att.Type.ToObject(); o != nil { if mt, ok := att.Type.(*design.MediaTypeDefinition); ok { if len(vs) == 0 { vs = []map[string]bool{make(map[string]bool)} } else if _, ok := vs[0][mt.TypeName]; ok { return "" } vs[0][mt.TypeName] = true att = mt.AttributeDefinition } else if ut, ok := att.Type.(*design.UserTypeDefinition); ok { if len(vs) == 0 { vs = []map[string]bool{make(map[string]bool)} } else if _, ok := vs[0][ut.TypeName]; ok { return "" } vs[0][ut.TypeName] = true att = ut.AttributeDefinition } o.IterateAttributes(func(n string, catt *design.AttributeDefinition) error { if att.HasDefaultValue(n) { data := map[string]interface{}{ "target": target, "field": n, "catt": catt, "depth": depth, "isDatetime": catt.Type == design.DateTime, "defaultVal": printVal(catt.Type, catt.DefaultValue), } assignments = append(assignments, RunTemplate(assignmentT, data)) } assignment := RecursiveFinalizer( catt, fmt.Sprintf("%s.%s", target, Goify(n, true)), depth+1, vs..., ) if assignment != "" { if catt.Type.IsObject() { assignment = fmt.Sprintf("%sif %s.%s != nil {\n%s\n%s}", Tabs(depth), target, Goify(n, true), assignment, Tabs(depth)) } assignments = append(assignments, assignment) } return nil }) } else if a := att.Type.ToArray(); a != nil { data := map[string]interface{}{ "elemType": a.ElemType, "target": target, "depth": 1, } assignment := RunTemplate(arrayAssignmentT, data) if assignment != "" { assignments = append(assignments, assignment) } } return strings.Join(assignments, "\n") }
// RecursiveChecker produces Go code that runs the validation checks recursively over the given // attribute. func RecursiveChecker(att *design.AttributeDefinition, nonzero, required, hasDefault bool, target, context string, depth int, private bool) string { var checks []string if o := att.Type.ToObject(); o != nil { if mt, ok := att.Type.(*design.MediaTypeDefinition); ok { att = mt.AttributeDefinition } else if ut, ok := att.Type.(*design.UserTypeDefinition); ok { att = ut.AttributeDefinition } validation := ValidationChecker(att, nonzero, required, hasDefault, target, context, depth, private) if validation != "" { checks = append(checks, validation) } o.IterateAttributes(func(n string, catt *design.AttributeDefinition) error { actualDepth := depth if catt.Type.IsObject() { actualDepth = depth + 1 } validation := RecursiveChecker( catt, att.IsNonZero(n), att.IsRequired(n), att.HasDefaultValue(n), fmt.Sprintf("%s.%s", target, Goify(n, true)), fmt.Sprintf("%s.%s", context, n), actualDepth, private, ) if validation != "" { if catt.Type.IsObject() { validation = fmt.Sprintf("%sif %s.%s != nil {\n%s\n%s}", Tabs(depth), target, Goify(n, true), validation, Tabs(depth)) } checks = append(checks, validation) } return nil }) } else if a := att.Type.ToArray(); a != nil { data := map[string]interface{}{ "elemType": a.ElemType, "context": context, "target": target, "depth": 1, "private": private, } validation := RunTemplate(arrayValT, data) if validation != "" { checks = append(checks, validation) } } else { validation := ValidationChecker(att, nonzero, required, hasDefault, target, context, depth, private) if validation != "" { checks = append(checks, validation) } } return strings.Join(checks, "\n") }
func (f *Finalizer) recurse(att *design.AttributeDefinition, target string, depth int) *bytes.Buffer { var ( buf = new(bytes.Buffer) first = true ) // Break infinite recursions switch dt := att.Type.(type) { case *design.MediaTypeDefinition: if buf, ok := f.seen[dt.TypeName]; ok { return buf } f.seen[dt.TypeName] = buf att = dt.AttributeDefinition case *design.UserTypeDefinition: if buf, ok := f.seen[dt.TypeName]; ok { return buf } f.seen[dt.TypeName] = buf att = dt.AttributeDefinition } if o := att.Type.ToObject(); o != nil { o.IterateAttributes(func(n string, catt *design.AttributeDefinition) error { if att.HasDefaultValue(n) { data := map[string]interface{}{ "target": target, "field": n, "catt": catt, "depth": depth, "isDatetime": catt.Type == design.DateTime, "defaultVal": printVal(catt.Type, catt.DefaultValue), } if !first { buf.WriteByte('\n') } else { first = false } buf.WriteString(RunTemplate(f.assignmentT, data)) } a := f.recurse(catt, fmt.Sprintf("%s.%s", target, Goify(n, true)), depth+1).String() if a != "" { if catt.Type.IsObject() { a = fmt.Sprintf("%sif %s.%s != nil {\n%s\n%s}", Tabs(depth), target, Goify(n, true), a, Tabs(depth)) } if !first { buf.WriteByte('\n') } else { first = false } buf.WriteString(a) } return nil }) } else if a := att.Type.ToArray(); a != nil { data := map[string]interface{}{ "elemType": a.ElemType, "target": target, "depth": 1, } if as := RunTemplate(f.arrayAssignmentT, data); as != "" { buf.WriteString(as) } } return buf }
func (v *Validator) recurseAttribute(att, catt *design.AttributeDefinition, n, target, context string, depth int, private bool) string { var validation string if ds, ok := catt.Type.(design.DataStructure); ok { // We need to check empirically whether there are validations to be // generated, we can't just generate and check whether something was // generated to avoid infinite recursions. hasValidations := false done := errors.New("done") ds.Walk(func(a *design.AttributeDefinition) error { if a.Validation != nil { if private { hasValidations = true return done } // For public data structures there is a case where // there is validation but no actual validation // code: if the validation is a required validation // that applies to attributes that cannot be nil or // empty string i.e. primitive types other than // string. if !a.Validation.HasRequiredOnly() { hasValidations = true return done } for _, name := range a.Validation.Required { att := a.Type.ToObject()[name] if att != nil && (!att.Type.IsPrimitive() || att.Type.Kind() == design.StringKind) { hasValidations = true return done } } } return nil }) if hasValidations { validation = RunTemplate(v.userValT, map[string]interface{}{ "depth": depth, "target": fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), }) } } else { dp := depth if catt.Type.IsObject() { dp++ } validation = v.recurse( catt, att.IsNonZero(n), att.IsRequired(n), att.HasDefaultValue(n), fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), fmt.Sprintf("%s.%s", context, n), dp, private, ).String() } if validation != "" { if catt.Type.IsObject() { validation = fmt.Sprintf("%sif %s.%s != nil {\n%s\n%s}", Tabs(depth), target, GoifyAtt(catt, n, true), validation, Tabs(depth)) } } return validation }
// RecursiveChecker produces Go code that runs the validation checks recursively over the given // attribute. func RecursiveChecker(att *design.AttributeDefinition, nonzero, required, hasDefault bool, target, context string, depth int, private bool) string { var checks []string if o := att.Type.ToObject(); o != nil { if ds, ok := att.Type.(design.DataStructure); ok { att = ds.Definition() } validation := ValidationChecker(att, nonzero, required, hasDefault, target, context, depth, private) if validation != "" { checks = append(checks, validation) } o.IterateAttributes(func(n string, catt *design.AttributeDefinition) error { var validation string if ds, ok := catt.Type.(design.DataStructure); ok { // We need to check empirically whether there are validations to be // generated, we can't just generate and check whether something was // generated to avoid infinite recursions. hasValidations := false done := errors.New("done") ds.Walk(func(a *design.AttributeDefinition) error { if a.Validation != nil { if private { hasValidations = true return done } // For public data structures there is a case where // there is validation but no actual validation // code: if the validation is a required validation // that applies to attributes that cannot be nil or // empty string i.e. primitive types other than // string. if !a.Validation.HasRequiredOnly() { hasValidations = true return done } for _, name := range a.Validation.Required { att := a.Type.ToObject()[name] if att != nil && (!att.Type.IsPrimitive() || att.Type.Kind() == design.StringKind) { hasValidations = true return done } } } return nil }) if hasValidations { validation = RunTemplate( userValT, map[string]interface{}{ "depth": depth, "target": fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), }, ) } } else { dp := depth if catt.Type.IsObject() { dp++ } validation = RecursiveChecker( catt, att.IsNonZero(n), att.IsRequired(n), att.HasDefaultValue(n), fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), fmt.Sprintf("%s.%s", context, n), dp, private, ) } if validation != "" { if catt.Type.IsObject() { validation = fmt.Sprintf("%sif %s.%s != nil {\n%s\n%s}", Tabs(depth), target, GoifyAtt(catt, n, true), validation, Tabs(depth)) } checks = append(checks, validation) } return nil }) } else if a := att.Type.ToArray(); a != nil { // Perform any validation on the array type such as MinLength, MaxLength, etc. validation := ValidationChecker(att, nonzero, required, hasDefault, target, context, depth, private) if validation != "" { checks = append(checks, validation) } data := map[string]interface{}{ "elemType": a.ElemType, "context": context, "target": target, "depth": 1, "private": private, } validation = RunTemplate(arrayValT, data) if validation != "" { checks = append(checks, validation) } } else { validation := ValidationChecker(att, nonzero, required, hasDefault, target, context, depth, private) if validation != "" { checks = append(checks, validation) } } return strings.Join(checks, "\n") }