Learn how to use sections and values in Glazed to organize and manage field definitions.
Sections in the glazed package provide a way to group and organize field definitions. They allow for better structure and modularity in command-line interfaces and other field-driven systems.
A section is a logical grouping of related field definitions. It consists of several components:
A Definition defines a field's properties, including name, type, default value, choices, and required status.
The SectionImpl struct provides a straightforward implementation of the
Section interface.
You can create a new field section using the NewSection function:
section, err := NewSection("config", "Configuration",
WithDescription("Configuration options for the application"),
WithPrefix("config-"),
WithFields(
fields.New("verbose", fields.TypeBool),
fields.New("output", fields.TypeString),
),
)
if err != nil {
// Handle error
}
You can add fields to an existing section using the AddFields method:
section.AddFields(
fields.New("log-level", fields.TypeString),
fields.New("max-retries", fields.TypeInteger),
)
You can initialize the default values of fields in a section using a struct:
type Config struct {
Verbose bool `glazed:"verbose"`
Output string `glazed:"output"`
LogLevel string `glazed:"log-level"`
MaxRetries int `glazed:"max-retries"`
}
defaultConfig := Config{
Verbose: true,
Output: "stdout",
LogLevel: "info",
MaxRetries: 3,
}
err := section.InitializeFieldDefaultsFromStruct(&defaultConfig)
if err != nil {
// Handle error
}
Alternatively, you can initialize defaults using a map:
defaultValues := map[string]interface{}{
"verbose": true,
"output": "stdout",
"log-level": "info",
"max-retries": 3,
}
err := section.InitializeFieldDefaultsFromFields(defaultValues)
if err != nil {
// Handle error
}
You can also populate a struct with the default values from the field section:
var config Config
err := section.InitializeStructFromFieldDefaults(&config)
if err != nil {
// Handle error
}
You can create a field section from a YAML definition:
yamlContent := []byte(`
name: Configuration
slug: config
description: Configuration options for the application
flags:
- name: verbose
type: bool
help: Enable verbose output
- name: output
type: string
help: Output destination
`)
section, err := NewSectionFromYAML(yamlContent)
if err != nil {
// Handle error
}
To create a deep copy of a field section:
clonedSection := section.Clone()
sections := NewSchema(
WithSections(configSection, outputSection),
)
When creating a cmds.CommandDescription, you can register sections under explicit slugs using cmds.WithSectionsMap.
// Create sections with internal slugs
cfgSection, _ := schema.NewSection("config", "Configuration")
outSection, _ := schema.NewSection("output", "Output")
// Register them under different command slugs
cmd := cmds.NewCommandDescription(
"run",
cmds.WithSectionsMap(map[string]schema.Section{
"cfg": cfgSection, // registered as "cfg"
"out": outSection, // registered as "out"
}),
)
// Later, parsed sections will be accessed by these slugs
// parsedSections.DecodeSectionInto("cfg", &myCfg)
Note: If the section is a *schema.SectionImpl and the key differs from the section's internal slug, the section is cloned and aligned to the registration key to maintain consistency at runtime.
name := section.GetName()
slug := section.GetSlug()
description := section.GetDescription()
Iterate over all field sections:
schema_.ForEach(func(key string, p Section) {
// Process each section
})
err := schema_.ForEachE(func(key string, p Section) error {
// Process each section, return error to stop iteration
return nil
})
Create a new Schema containing only the specified sections:
subset := schema_.Subset("config", "output")
Add sections to the end or beginning of the collection:
schema_.AppendSections(newSection1, newSection2)
schema_.PrependSections(newSection3, newSection4)
Merge two Schema collections:
mergedSections := schema_.Merge(otherSchema)
Create a deep copy of Schema:
clonedSchema := schema_.Clone()
Get all field definitions across all sections:
allDefinitions := schema_.GetAllDefinitions()
A SectionValues is the result of parsing input data (such as command-line arguments, configuration files, or environment variables) using a Section specification. It consists of:
Values is a collection of SectionValues objects, typically representing all the sections used in a command or application.
Values are primarily used to:
parsedSection, err := NewSectionValues(section,
WithFieldValueValue("verbose", true),
)
if err != nil {
// Handle error
}
parsedSections := NewValues(
WithSectionValues("config", parsedConfigSection),
WithSectionValues("output", parsedOutputSection),
)
value, ok := parsedSection.GetField("verbose")
if !ok {
// Field not found
}
type Config struct {
Verbose bool `glazed:"verbose"`
Output string `glazed:"output"`
}
var config Config
err := parsedSections.DecodeSectionInto("config", &config)
if err != nil {
// Handle error
}
parsedSections.GetDefaultSection().MergeFields(otherSectionValues)
allParams := parsedSections.GetAllFieldValues()
Iterate over all parsed sections:
parsedSections.ForEach(func(k string, v *SectionValues) {
// Process each section
})
err := parsedSections.ForEachE(func(k string, v *SectionValues) error {
// Process each section, return error to stop iteration
return nil
})
Get a map of all field values across all sections:
dataMap := parsedSections.GetDataMap()
Get an existing SectionValues or create a new one if it doesn't exist:
parsedSection := parsedSections.GetOrCreate(someSection)
Create a deep copy of Values:
clonedValues := parsedSections.Clone()
Middlewares in the Glazed framework provide a powerful mechanism to manage field values from various sources such as environment variables, configuration files, and command-line arguments. They allow for flexible and modular field handling in your applications.
Middleware Structure: Each middleware processes fields before and/or after calling the next handler in the chain. They work with Schema and Values to manage field definitions and values.
Order of Execution: Middlewares are executed in reverse order of how they're provided. This means the last middleware added is executed first.
SetFromDefaults: Populates fields with their default values if no value exists.
middleware := sources.FromDefaults(
sources.WithSource("defaults"),
)
UpdateFromEnv: Loads values from environment variables.
middleware := sources.FromEnv("APP",
sources.WithSource("env"),
)
LoadFieldsFromFile / LoadFieldsFromFiles: Load fields from JSON or YAML files.
// Single file
middleware := sources.FromFile("config.yaml",
middlewares.WithParseOptions(sources.WithSource("config")))
// Multiple files (low -> high precedence)
middleware2 := sources.FromFiles([]string{"base.yaml", "local.yaml"},
middlewares.WithParseOptions(sources.WithSource("config")))
ParseFromCobraCommand: Parses field values from a Cobra command, typically used for CLI applications.
middleware := sources.FromCobra(cmd,
sources.WithSource("flags"),
)
To use middlewares, chain them together and execute them with your field sections and parsed sections:
sources.Execute(sections, parsedSections,
sources.FromDefaults(),
sources.FromFiles([]string{"config.yaml", "config.local.yaml"}),
sources.FromEnv("APP"),
sources.FromCobra(cmd),
)
Chain to create a single middleware.WithParseStepSource to track where field values originate.