feature request: camelCase flag to snake_case env
lmittmann opened this issue · comments
Great package. It would be nice if a camelCase flag names could be set by an env variable in snake_case.
Example:
fs := flag.NewFlagSet("my-program", flag.ContinueOnError)
rpcURL = fs.String("rpcURL", ...)
This works: $ RPCURL="https://..." my-program
This does not work (would be nice though): $ RPC_URL="https://..." my-program
I'm not aware of a way to reliably tokenize camel case identifiers in this way. If you know of one, please re-open with a link!
@peterbourgon you could e.g. this:
https://go.dev/play/p/6C-kW-qMWQT
func camelToSnake(camel string) string {
snake := make([]byte, 0, len(camel))
for i, c := range []byte(camel) {
if isCapital(c) {
if i > 0 && (!isCapital(camel[i-1]) || i+1 < len(camel) && !isCapital(camel[i+1])) {
snake = append(snake, '_')
}
c += 'a' - 'A'
}
snake = append(snake, c)
}
return string(snake)
}
func isCapital(c byte) bool {
return 'A' <= c && c <= 'Z'
}
func TestCamelToSnake(t *testing.T) {
tests := []struct {
Camel string
Want string
}{
{"camel", "camel"},
{"Camel", "camel"},
{"CamelCase", "camel_case"},
{"camelCase", "camel_case"},
{"URL", "url"},
{"rpcURL", "rpc_url"},
{"aaaBBBCcc", "aaa_bbb_ccc"},
{"aaaAAAAaa", "aaa_aaa_aaa"},
{"a1", "a1"},
{"a1B2", "a1_b2"},
}
for _, test := range tests {
t.Run(test.Camel, func(t *testing.T) {
got := camelToSnake(test.Camel)
if test.Want != got {
t.Errorf("want %q, got %q", test.Want, got)
}
})
}
}
Ehhh not sure it's OK to assume camel can be interpreted as raw bytes, and not convinced that some of those test cases necessarily tokenize in the way which is asserted. I appreciate the effort! But this is in my judgment too fragile.