-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add TextFlag #2055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add TextFlag #2055
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution! 😊
Just a few comments regarding the tests. If possible, please add a few more test cases to cover edge cases and improve coverage.
d9552c8
to
35656a1
Compare
I've discovered a limitation of the way the So the interface that I created does not equate to those two to the extent that it allows the user of the package to just put their implementation in like you can with text := &MockMarshaller{}
cmd := &Command{
Name: "foo",
Flags: []Flag{
&TextFlag{
Name: "mytext",
Value: text,
Destination: &text,
},
},
} This is not valid. Because So I would either have to not use the |
@somebadcode can you send me the test code you are using for this ? I think it should be like this.
|
Test code: type MockMarshaller struct {
text []byte
}
var _ encoding.TextMarshaler = (*MockMarshaller)(nil)
var _ encoding.TextUnmarshaler = (*MockMarshaller)(nil)
func (m *MockMarshaller) MarshalText() ([]byte, error) {
return m.text, nil
}
func (m *MockMarshaller) UnmarshalText(text []byte) error {
m.text = text
return nil
}
func TestTextFlagValueFromCommand(t *testing.T) {
text := &MockMarshaller{}
cmd := &Command{
Name: "foo",
Flags: []Flag{
&TextFlag{
Name: "mytext",
Value: text,
Destination: &text,
},
},
}
} Code that I'm trying to test: func (cmd *Command) Text(name string) TextMarshalUnmarshaler {
if v, ok := cmd.Value(name).(TextMarshalUnmarshaler); ok {
tracef("text available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
return v
}
tracef("text NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name)
return nil
} This is what I've added but not committed yet. |
@somebadcode I think the problem is you are trying to use the same instance for both Value and Destination. Generally this distinction is present when you have "simple" types
You should test your code like this
where text1 is a pointer to marshaller/unmarshaller and text2 is a simple reference |
Why would pointing back to the same one matter? In your example Both The type Because of the constraints of So Separating the two would make this implementation less complicated and those who want to add their own custom implementations for their special flags would have an easier time if it wouldn't be this constrained. So if type FlagBase[T any, T2 any, C any, VC ValueCreator[T, T2, C]] struct {
// ...
Value T `json:"defaultValue"`
Destination T2 `json:"-"`
// ...
} This would enable us to do:
This would make the implementation cleaner, more explicit and easier to use. Simple types such as type StringFlag = FlagBase[string, *string, StringConfig, StringValue] More explicit typing reduces the constraints and increases the versatility at minimal cost (no increased runtime cost). Look at the standard library's |
@somebadcode I like that idea. Do you want include the FlagBase changes in this PR ? or should I make a separate PR for that ? |
That would be a big change not directly related to this PR but an improvement on So this PR can be rebased on top of that once the changes are merged. Also, what would the generic type be? |
@somebadcode I started working on adding a new Destination Type and ran into some issues. I dont think its that trivial to add. I dont have cycles to spare this week so I'm not sure when I will get around to it. For now I would suggest if you use GenericValue as your base or do this particular flag as a custom flag satisfying all the necessary interfaces. When we get around to making the Destination Type change I can look into compacting TextFlag into this. |
I tried to do the same but ran into issues with I'll look into changing the |
@somebadcode Thinking about this a bit more. Cant you achieve the same behavior using a GenericValueFlag ? Say you used
Would that fit the bill itself ? I am wary of adding more code into the cli library |
What is |
* Added `TextFlag` which supports setting values for types that satisfies the `encoding.TextMarshaller` and `encoding.TextUnmarshaller` which is very handy when you want to set log levels or string-like types that satifies the interfaces. Fixes: urfave#2051 Signed-off-by: Tobias Dahlberg <[email protected]>
@somebadcode Here is the test code I posted in the issue #2051 (comment) |
If you want to remove the GenericFlag templating of FlagBase and have both GenericFlag.Value and GenericFlag.Destination be set to cli.Value type it might be better suited for your use case ? |
TextFlag
which supports setting values for types that satisfies bothencoding.TextMarshaller
andencoding.TextUnmarshaller
which is very handy when you want to set log levels or string-like types that satisfies the interfaces.Fixes: #2051
What type of PR is this?
What this PR does / why we need it:
Making it easier to set types that satisfies
encoding.TextMarshaller
andencoding.TextUnmarshaller
such aslog/slog.LogLevelVar
, using flags as see in the standard library'sflag
.Which issue(s) this PR fixes:
Fixes #2051
Release Notes