go-arg icon indicating copy to clipboard operation
go-arg copied to clipboard

Inconsistency in default value override

Open gobsej opened this issue 3 years ago • 3 comments

Reference

I am building a bunch of independent microservices of which everyone uses the same set of Arguments (some with default values). I then override some of the default values in the services (because some services need different default values than the majority) before parsing the arguments, to get an idea:

var args Args

func main() {
	// override defaults
	args.SomeBool = false
	args.SomeString = "override"
	
	arg.MustParse(&args)

The Args struct is defined globally for everyone:

type Args struct {
	SomeString string `arg:"--some-string" default:"test"`
	SomeBool   bool   `arg:"--some-bool" default:"true"`
	.
	.
	.
}

The actual Issue

While overriding bool values I noticed that values that have a default value of "true" in the struct, can't be overridden to "false" but overriding a "false" default value to "true" works. Seems a bit strange to handle this differently, is this on purpose?

gobsej avatar Nov 24 '22 15:11 gobsej

Oh, very interesting. This might a bug due to false being the zero value for bool. The way go-arg determines whether a value has been overridden is by checking whether it is different from the zero value. One way you could work around this would be by using *bool rather than bool. This would be a bit ugly, I know, but might be a short-term solution for you. In the mean time I will consider how to resolve this more thoroughly.

alexflint avatar Nov 25 '22 15:11 alexflint

Okay thanks, I actually just removed the default tags and only used the override by accessing the fields.

But i have another question, I just saw, that default values for slices and maps aren't supported, is this planned for the future or not possible? I can imagine that it would be tricky via tags, but it seems possible via assigning the default value by accessing the field.

gobsej avatar Nov 29 '22 11:11 gobsej

The best way to do it at the moment is to implement TextUnmarshaler, as in:

type Map map[string]string

func (m *Map) UnmarshalText(b []byte) error {
  // decode the string into a map using whatever encoding you prefer -- e.g. json
}

var args struct {
  ArgumentName Map
}

The problem I ran into was that there was no canonical way to deserialize a default string into a map or list. I might just pick one and implement it in v2 of this library (see #197)

alexflint avatar Nov 29 '22 15:11 alexflint