Learn how to define and parse fields in Go applications using the Field API.
The Field API facilitates parsing and managing fields in Go applications. It's ideal for applications requiring flexible field handling.
A Definition defines a field's properties, including name, type, default value, choices, and required status.
type Definition struct {
Name string `yaml:"name"`
ShortFlag string `yaml:"shortFlag,omitempty"`
Type Type `yaml:"type"`
Help string `yaml:"help,omitempty"`
Default *interface{} `yaml:"default,omitempty"`
Choices []string `yaml:"choices,omitempty"`
Required bool `yaml:"required,omitempty"`
IsArgument bool `yaml:"-"`
}
Definitions is an ordered map of Definition instances, indexed by name.
type Definitions struct {
*orderedmap.OrderedMap[string, *Definition]
}
A FieldValue contains the parsed value, its Definition, and a log of parsing steps.
type FieldValue struct {
Value interface{}
Definition *Definition
Log []ParseStep
}
FieldValues is an ordered map of FieldValue instances, indexed by field names.
type FieldValues struct {
*orderedmap.OrderedMap[string, *FieldValue]
}
ParseStep records each step in the parsing process, including source, value, and metadata.
type ParseStep struct {
Source string
Value interface{}
Metadata map[string]interface{}
}
Follow these steps to use the Field API: define fields and parse them from user input or configuration files.
Define fields using Definition, specifying name, type, and options like default values or choices.
import "github.com/go-go-golems/glazed/pkg/cmds/fields"
// Define a string field with a default value
paramName := fields.New(
"username",
fields.TypeString,
fields.WithHelp("The name of the user"),
fields.WithDefault("guest"),
fields.WithRequired(false),
)
Parse input values (e.g., from command-line arguments) to obtain FieldValue instances.
// Assume 'inputs' is a slice of strings received from the command line
inputs := []string{"john_doe"}
parsedParam, err := paramName.ParseField(inputs)
if err != nil {
// Handle parsing error
}
fmt.Println("Parsed Value:", parsedParam.Value)
Access parsed field values via FieldValues.
// Create a collection of field definitions
paramDefs := fields.News(
fields.WithDefinitionList([]*fields.Definition{paramName}),
)
// Parse a field value
parsedParams, err := paramDefs.ParseFields(userInputs)
if err != nil {
// Handle error
}
// Access the parsed value
username := parsedParams.GetValue("username").(string)
fmt.Println("Username:", username)
Manage parsed fields using these methods: updating values, merging field sets, and cloning parsed data.
Update a parsed field's value, optionally appending a new parsing step.
parsedParam.Update("new_username", sources.WithSource("override"))
Merge another FieldValues instance, combining values and logs.
parsedParams.Merge(otherParsedParams)
Create a deep copy of FieldValues to avoid unintended mutations.
clonedParams := parsedParams.Clone()
Specify default values and enforce required fields.
// Define a required integer field without a default
ageParam := fields.New(
"age",
fields.TypeInteger,
fields.WithHelp("The age of the user"),
fields.WithRequired(true),
)
At the individual field parser level, an error is returned if a required field is parsed from no input. If an optional field is missing, its default value is used.
parsedParam, err := ageParam.ParseField([]string{})
if err != nil {
// Since 'age' is required, an error is expected
}
For Cobra-based commands, fields.WithRequired(true) is validated against the final merged value after configured sources have run. That means a required field may be satisfied by a config file, an environment variable, a positional argument, or a Cobra flag. The parser validates required values after source resolution, and it skips that final required-value validation for control/diagnostic paths such as --help, --print-parsed-fields, --print-yaml, and --print-schema.
Advanced features include logging parsing steps and file format parsing.
Each FieldValue logs parsing steps, showing how the final value was derived.
for _, step := range parsedParam.Log {
fmt.Printf("Source: %s, Value: %v, Metadata: %v\n", step.Source, step.Value, step.Metadata)
}
Define fields to accept values from files (e.g., JSON, YAML, CSV).
// Define a field that expects a JSON file
fileParam := fields.New(
"config",
fields.TypeObjectFromFile,
fields.WithHelp("Path to the configuration file"),
fields.WithRequired(true),
)
// Parse the field from file input
parsedFileParam, err := fileParam.ParseField([]string{"config.json"})
if err != nil {
// Handle error
}
configData := parsedFileParam.Value.(map[string]interface{})
fmt.Println("Config Data:", configData)