-
|
I try to implement a system that provides ability to configure plugins. So far I have a definition like: this allows me to pass options as following: Now I need to parse the I was wondering if I could create a Where Each module has an Thanks in advance. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
|
After a few trial and error, I came up with that solution. Let me know if this is the way to do or if I did it completely wrong. import (
"fmt"
"strconv"
"strings"
"github.com/alecthomas/kong"
)
// Set stores per-module configuration overrides.
//
// The first-level map key is the module name.
// The second-level map contains the module options, where each key/value pair
// represents a configuration entry for that module.
//
// Values are parsed from CLI arguments and automatically converted to native
// Go types when possible (int64, float64, bool). When no conversion applies,
// the value is stored as a string.
//
// Example usage:
//
// --set sqlite.database=/path/to/file.sqlite
// --set api.url=https://example.com
// --set api.user=user
// --set api.password=secret
//
// After parsing, Set will contain:
//
// map[string]map[string]any{
// "sqlite": {
// "database": "/path/to/file.sqlite",
// },
// "api": {
// "url": "https://example.com",
// "user": "user",
// "password": "secret",
// },
// }
type Set map[string]map[string]any
// parseValue attempts to convert the string value v into a native Go type.
//
// The conversion is attempted in the following order:
// - int64
// - float64
// - bool
//
// If none of the conversions succeed, the original string value is returned.
//
// This allows CLI-provided configuration values to be consumed naturally
// by modules without requiring manual string parsing.
func (s Set) parseValue(v string) any {
// int
if i, err := strconv.ParseInt(v, 10, 64); err == nil {
return i
}
// float
if f, err := strconv.ParseFloat(v, 64); err == nil {
return f
}
// bool
if b, err := strconv.ParseBool(v); err == nil {
return b
}
// fallback
return v
}
// Decode implements kong.Value and is called once for each Set-related flag
// encountered on the command line.
//
// The expected input format is:
//
// module.key=value
//
// Where:
// - module is the module name
// - key is the module configuration option
// - value is the configuration value
//
// The decoded value is stored in the Set map, creating intermediate maps
// as needed. If the value can be converted to a native Go type, it will be
// stored as such; otherwise, it is stored as a string.
//
// Invalid input formats result in an error and stop CLI parsing.
func (s Set) Decode(ctx *kong.DecodeContext) error {
// Retrieve the raw value as provided by the CLI.
var val string
err := ctx.Scan.PopValueInto("string", &val)
if err != nil {
return err
}
// Split the value on '=' into "module.key" (kv[0])and "value" (kv[1]).
kv := strings.Split(val, "=")
if len(kv) != 2 {
return fmt.Errorf("invalid Set value %q, expected module.key=value", val)
}
// Split the key part on '.' into module (keys[0]) and option (keys[1])
// key.
keys := strings.Split(kv[0], ".")
if len(keys) != 2 {
return fmt.Errorf("invalid Set key %q, expected module.key", kv[0])
}
// Initialize the module entry if needed.n
if _, ok := s[keys[0]]; !ok {
s[keys[0]] = map[string]any{}
}
// Store the parsed value.
s[keys[0]][keys[1]] = s.parseValue(kv[1])
return nil
} |
Beta Was this translation helpful? Give feedback.
-
|
Yep, that's a reasonable approach. Custom parsing is the solution. |
Beta Was this translation helpful? Give feedback.
After a few trial and error, I came up with that solution.
Let me know if this is the way to do or if I did it completely wrong.