package ezconf import ( "reflect" "strings" "github.com/pkg/errors" ) // ParseConfigStruct extracts environment variable metadata from a config // struct's ezconf struct tags using reflection. // // The configPtr parameter must be a pointer to a struct. Each field with an // ezconf tag will be parsed to extract environment variable information. // // Tag format: `ezconf:"VAR_NAME,description:Description text,default:value,required"` // // Components: // - First value: environment variable name (required) // - description:...: Description of the variable // - default:...: Default value // - required: Marks the variable as required (optionally required:condition) func ParseConfigStruct(configPtr any) ([]EnvVar, error) { if configPtr == nil { return nil, errors.New("config pointer cannot be nil") } v := reflect.ValueOf(configPtr) if v.Kind() != reflect.Ptr { return nil, errors.New("config must be a pointer to a struct") } v = v.Elem() if v.Kind() != reflect.Struct { return nil, errors.New("config must be a pointer to a struct") } t := v.Type() envVars := make([]EnvVar, 0) for i := 0; i < t.NumField(); i++ { field := t.Field(i) tag := field.Tag.Get("ezconf") if tag == "" { continue } envVar, err := parseEzconfTag(tag) if err != nil { return nil, errors.Wrapf(err, "failed to parse ezconf tag on field %s", field.Name) } envVars = append(envVars, *envVar) } return envVars, nil } // parseEzconfTag parses an ezconf struct tag value to extract environment // variable information. // // Expected format: "VAR_NAME,description:Description text,default:value,required" func parseEzconfTag(tag string) (*EnvVar, error) { if tag == "" { return nil, errors.New("tag cannot be empty") } parts := strings.Split(tag, ",") if len(parts) == 0 { return nil, errors.New("tag cannot be empty") } envVar := &EnvVar{ Name: strings.TrimSpace(parts[0]), } if envVar.Name == "" { return nil, errors.New("environment variable name cannot be empty") } for _, part := range parts[1:] { part = strings.TrimSpace(part) switch { case strings.HasPrefix(part, "description:"): envVar.Description = strings.TrimSpace(strings.TrimPrefix(part, "description:")) case strings.HasPrefix(part, "default:"): envVar.Default = strings.TrimSpace(strings.TrimPrefix(part, "default:")) case part == "required": envVar.Required = true case strings.HasPrefix(part, "required:"): envVar.Required = true // Store the condition in the description if it adds context condition := strings.TrimSpace(strings.TrimPrefix(part, "required:")) if condition != "" && envVar.Description != "" { envVar.Description = envVar.Description + " (required " + condition + ")" } } } return envVar, nil }