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

Add encoder/decoder interface to configure encoding/decoding #47

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

atticus-sullivan
Copy link

@atticus-sullivan atticus-sullivan commented Apr 20, 2023

Like discussed in #40 . There are still things to do:

  • document the interface/variables properly
  • Backwards compatibility
  • Not quite satisfied with Decoder as name for the interface in the decoder.go file where actually Decode is the core component (with Encoder this is much better as there the term responder is being used)

On the second point: As DecodeJSON etc were public functions, to be fully backwards compatible, they need to be preserved, right? So maybe the best shot is to keep those functions and in the default implementation of Decoder/Encoder for e.g. JSON just delegate the calls to these old functions, right?


As this is not finished yet, it's just a draft PR. Nevertheless I already stated TODOs so if you already have any comment on the current state, don't hesitate.

@atticus-sullivan
Copy link
Author

To be backwards compatible, I added back the old functions. Removing them would be a breaking change as they are exported (capital letter) and someone might be using them directly.
Sadly this is a huge code duplication as we can't simply call the new implementations (the req parameter is unknown) and calling the legacy implementations in the new implementation feels bad.
I marked these old functions as deprecated so maybe on a major version change, we can remove them eventually, cleaning up the code base.

If you have any opinions on how to do this in a cleaner way, just let me know. I myself am not completely satisfied with this right now.

@atticus-sullivan atticus-sullivan marked this pull request as ready for review April 22, 2023 16:56
@atticus-sullivan
Copy link
Author

Any comments @VojtechVitek ?

Copy link
Contributor

@VojtechVitek VojtechVitek left a comment

Choose a reason for hiding this comment

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

Good job on the PR.

However, there are still some design choices (as suggested in #40) that don't feel right when looking at the clean code diff.

Can we simplify this further?

// a value `v`.
type Decoder interface {
// Decodes a given reader into an interface
Decode(r io.Reader, req *http.Request, v interface{}) error
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'll need to revisit the discussion at #40.

I see a r io.Reader -- but req.Body is also an io.Reader. Do we need both?

Copy link
Author

Choose a reason for hiding this comment

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

Oh well that's right r will be req.Body. But see the comment below for the discussion of the function signature.

// our default implementations. By setting render.Decode{JSON,XML,Form} you can
// customize Decoding (e.g. you might want to configure the JSON-decoder)
var (
DecoderJSON Decoder = DecodeJSONInter{}
Copy link
Contributor

Choose a reason for hiding this comment

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

What does Inter stand for?

Copy link
Author

Choose a reason for hiding this comment

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

Well as I needed to keep the DecodeJSON function (see below), I needed to call the struct which implements the interface differently and decided to go with DecodeJSONInter to signal that this is the version using/conforming to the interface.
Maybe Impl or so would be the more typical choice.

// XML marshals 'v' to XML, setting the Content-Type as application/xml. It
// will automatically prepend a generic XML header (see encoding/xml.Header) if
// one is not found in the first 100 bytes of 'v'.
//
// Deprecated: EncoderXML.Encode() should be used.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we want to "deprecate" these functions.

I thought we would just let users overwrite them with a custom implementation.

Copy link
Author

Choose a reason for hiding this comment

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

Well this issue is linked with your next annotation. Because we wanted to add the Request object, the Interface of the function changes and there was no way of keeping the old function and the new function, with the old function calling simply redirecting to the new one (the request object has to come from somewhere).

So in the end it's all about changing the function signature (deprecating and in a future breaking change remove the old functions) or not. Personally I'd be satisfied with the current signature as well, right now I don't have a use-case where I need the Request object in the en-/decoder implementation.

Comment on lines +45 to +49
err = DecoderJSON.Decode(r.Body, r, v)
case ContentTypeXML:
err = DecodeXML(r.Body, v)
err = DecoderXML.Decode(r.Body, r, v)
case ContentTypeForm:
err = DecodeForm(r.Body, v)
err = DecoderForm.Decode(r.Body, r, v)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this change, I think I was wrong to suggest adding a request object to this func signature.

@atticus-sullivan
Copy link
Author

As the discussion of the function signatures is a bigger thing and I find larger discussions within a review rather hard to keep track of, can we please continue the discussion here in the "main thread"?

@atticus-sullivan
Copy link
Author

So are we back at

type Decoder interface {
	Decode(w io.Reader, v any) (error)
}
type Encoder interface {
        Encode(w io.Writer, v any) (error)
}

? (this way we also can stick to the current functions which then simply call the encode/decode functions of the encoder/decoder being set)

@atticus-sullivan
Copy link
Author

@VojtechVitek Any thoughts on this basic interface?

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

2 participants