Skip to content
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

WIP custom value interface-based proposal #2

Closed
wants to merge 1 commit into from
Closed

Conversation

artych
Copy link
Contributor

@artych artych commented Jun 28, 2021

No description provided.

SaveInt64To(dest *int64) error
}

type CustomValue interface {
json.Unmarshaler
Copy link
Member

@na-- na-- Jun 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is somewhat problematic - it will force users to implement the json.Unmarshaler interface for custom values, even if they don't use JSON for configuring their application (e.g. they only rely on env vars and/or CLI flags).

That's why I went with encoding.TextUnmarshaler when I started experimenting with custom types - it is the more generic interface. Basically, if a value can be parsed from a textual form, it should be able to implement TextUnmarshaler. And, by definition, CLI flags and environment variables are text, while a lot of custom variables in JSON are also represented as strings. I assume that's why the default json.Unmarshal() behavior also supports TextUnmarshaler as a fallback (emphasis mine):

To unmarshal JSON into a value implementing the Unmarshaler interface, Unmarshal calls that value's UnmarshalJSON method, including when the input is a JSON null. Otherwise, if the value implements encoding.TextUnmarshaler and the input is a JSON quoted string, Unmarshal calls that value's UnmarshalText method with the unquoted form of the string.

So, requiring custom types to implement TextUnmarshaler seems much less onerous than requiring them to implement json.Unmarshaler 🤷‍♂️ The vast majority of custom types should be fine, and the few that aren't should be able to implement the croconf.Field interface directly, I think...

The problem is how do we "upgrade" to json.Unmarshaler for the types that support it, and the best way I can think of right now is to just type assert it 😞 That is, adopt an approach reminiscent of progressive enhancement - require custom types to at least implement TextUnmarshaler, but the JSON source will check if they also implement json.Unmarshaler and use it if they do. What do you think? cc @codebien

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I like the idea to use interfaces because it makes the extension of the API more idiomatic, following something already used by developers with the encoding package.

I agree to reduce the dependency from the specific implementations but I don't like to require having the interface check directly in the code implementation that it would potentially open to a good number of bugs. But I haven't a concrete better alternative.

}

func (dcv defaultCustomValue) SaveCustomValueTo(dest CustomValue) error {
dest = dcv.value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work, since I think dest won't be treated as a pointer here 😕 That's why json.Unmarshaler and encoding.TextUnmarshaler are implemented with pointer receivers, so they have an internal pointer to the value they're supposed to "fill" with the data they're unmarshaling.

@@ -80,3 +81,14 @@ func (d *Duration) UnmarshalJSON(data []byte) error {

return nil
}

// ParseFromString implements croconf.CustomValue interface
func (d *Duration) ParseFromString(data string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move to UnmarshalText

SaveInt64To(dest *int64) error
}

type CustomValue interface {
json.Unmarshaler
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I like the idea to use interfaces because it makes the extension of the API more idiomatic, following something already used by developers with the encoding package.

I agree to reduce the dependency from the specific implementations but I don't like to require having the interface check directly in the code implementation that it would potentially open to a good number of bugs. But I haven't a concrete better alternative.

@artych artych changed the title [feat] custom value interface-based implementation WIP custom value interface-based proposal Jun 29, 2021
@artych artych closed this Jun 29, 2021
@na-- na-- deleted the custom-types branch July 2, 2021 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants