-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
feat: add TextFlag #2055
base: main
Are you sure you want to change the base?
feat: add TextFlag #2055
Conversation
* 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]>
Signed-off-by: Eng Zer Jun <[email protected]>
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.
flag_text_test.go
Outdated
if err := tt.flag.Apply(set); err != nil { | ||
t.Fatalf("Apply(%v) failed: %v", tt.args, err) | ||
} | ||
|
||
err := set.Parse(tt.args) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) | ||
|
||
return | ||
} else if (err != nil) == tt.wantErr { | ||
// Expected error. | ||
return | ||
} | ||
|
||
if got := tt.flag.GetValue(); got != tt.want { | ||
t.Errorf("Value = %v, want %v", got, tt.want) | ||
} |
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.
Use the testify package for assertions
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.
I don't use testify and I don't see it as idiomatic Go.
But I've added it and I hope that I'm using it the correct way.
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.
The tests should now have full coverage.
flag_text.go
Outdated
return v.Value | ||
} | ||
|
||
func (v TextValue) Create(t TextMarshalUnMarshaller, _ *TextMarshalUnMarshaller, c StringConfig) Value { |
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.
func (v TextValue) Create(t TextMarshalUnMarshaller, _ *TextMarshalUnMarshaller, c StringConfig) Value { | |
func (v TextValue) Create(t TextMarshalUnMarshaller, p *TextMarshalUnMarshaller, c StringConfig) Value { | |
b, _ := p.MarshalText() | |
t.UnmarshalText(b) |
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.
I've made the necessary changes.
flag_text.go
Outdated
type TextFlag = FlagBase[TextMarshalUnMarshaller, StringConfig, TextValue] | ||
|
||
type TextValue struct { | ||
Value TextMarshalUnMarshaller |
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.
Value TextMarshalUnMarshaller | |
Value *TextMarshalUnMarshaller |
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.
I've made the necessary changes.
flag_text.go
Outdated
|
||
func (v TextValue) Create(t TextMarshalUnMarshaller, _ *TextMarshalUnMarshaller, c StringConfig) Value { | ||
return &TextValue{ | ||
Value: t, |
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.
Value: t, | |
Value: p, |
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.
I've made the necessary changes.
flag_text.go
Outdated
|
||
func (v TextValue) Set(s string) error { | ||
if v.Config.TrimSpace { | ||
return v.Value.UnmarshalText([]byte(strings.TrimSpace(s))) |
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.
return v.Value.UnmarshalText([]byte(strings.TrimSpace(s))) | |
s = strings.TrimSpace(s) |
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.
I've made the necessary changes.
flag_text_test.go
Outdated
name: "empty", | ||
flag: TextFlag{ | ||
Name: "log-level", | ||
Value: &slog.LevelVar{}, |
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.
Note that Value is used to initialize the flag. The actual value of field should be in Destination
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.
See the flag_generic.go as an example.
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.
I've addressed this in the latest version of the PR.
0876e6b
to
33d1db5
Compare
d9552c8
to
35656a1
Compare
Signed-off-by: Eng Zer Jun <[email protected]>
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. |
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