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

Support accessing the response code as an integer #70

Merged
merged 3 commits into from
May 14, 2024

Conversation

simpkins
Copy link
Contributor

The Status enum only supports a portion of the response codes defined by RFC 9110. If the server returned a response code other than one of the ones supported by the Status enum, the code previously threw away the value. This updates the code to also store the code as an integer, so that users can handle other values.

I considered simply changing the Unknown variant to use Unknown(u16), but avoided it for now since this would break backwards compatibility, and would also prevent using status as u16 to convert Status values to integers.

The `Status` enum only supports a portion of the response codes defined
by RFC 9110.  If the server returned a response code other than one of
the ones supported by the `Status` enum, the code previously threw away
the value.  This updates the code to also store the code as an integer,
so that users can handle other values.

I considered simply changing the `Unknown` variant to use
`Unknown(u16)`, but avoided it for now since this would break backwards
compatibility, and would also prevent using `status as u16` to convert
`Status` values to integers.
@rmja
Copy link
Member

rmja commented May 12, 2024

I would probably prefer an Other variant on the Status enum to avoid having two apis to convey the same information.

@simpkins
Copy link
Contributor Author

I would probably prefer an Other variant on the Status enum to avoid having two apis to convey the same information.

Sure, I can do that instead if you prefer. I agree that it does make the API a bit nicer. That said, it does have some downsides since it breaks backwards compatibility, and makes the Status enum take more space to store and no longer convertible to u16 with the as u16 syntax. It also results in slightly weird comparison behavior (e.g., should Status::Ok and Status::Other(200) be treated as equal?).

This replaces the `Status::Unknown` variant with a `Status::Other(n)`
variant that can be used to represent arbitrary status codes.  RFC 9110
defines a number of status codes that aren't currently specified as enum
variants, and servers may return other arbitrary codes.  This change
allows `Status` to represent any arbitrary code, rather than collapsing
unknown codes to a single value.

This does break backwards compatibility, as the `Status` variant can no
longer be trivial converted to a u16 (and it now requires more space to
store).  This also results in having more than one way to represent some
status codes.  e.g., `Status::Ok` and `Status::Other(200)` are both
treated as equal now.
@rmja
Copy link
Member

rmja commented May 13, 2024

Yes, that can lead to subtle bugs, as every addition of a known status variant is effectively a breaking change. This is actually the reason why I have not already made the change:) @lulf what do you think?

Copy link
Member

@lulf lulf left a comment

Choose a reason for hiding this comment

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

It's unfortunate it requires breaking, but I also don't think there's been any promises made to not break the API, so might as well do it now. Maybe there should be a split where the StatusCode is just u16 wrapper, and used in the structs, whereas the Status enum is a convenience that can be Into in any input APIs.

@rmja
Copy link
Member

rmja commented May 13, 2024

The thing is that we will have a breaking change every time that a new known status code is added. Consider this code:

if let Other(status) = response.status && status == 123. That code could suddenly fail if 123 is later added as a known status code.

Another option would be to simply expose a status: u16 on the response replacing the current status: Status enum, and then have a get_status(): Status function that could try and convert the status: u16 to the known variants in Status. That would not cause any breaking changes later if we let the Unknown variant stay instead of introducing an Other(u16).

@lulf
Copy link
Member

lulf commented May 13, 2024

The thing is that we will have a breaking change every time that a new known status code is added. Consider this code:

if let Other(status) = response.status && status == 123. That code could suddenly fail if 123 is later added as a known status code.

Another option would be to simply expose a status: u16 on the response replacing the current status: Status enum, and then have a get_status(): Status function that could try and convert the status: u16 to the known variants in Status. That would not cause any breaking changes later if we let the Unknown variant stay instead of introducing an Other(u16).

The thing is that we will have a breaking change every time that a new known status code is added. Consider this code:

if let Other(status) = response.status && status == 123. That code could suddenly fail if 123 is later added as a known status code.

Another option would be to simply expose a status: u16 on the response replacing the current status: Status enum, and then have a get_status(): Status function that could try and convert the status: u16 to the known variants in Status. That would not cause any breaking changes later if we let the Unknown variant stay instead of introducing an Other(u16).

Sorry for being unclear, I think we agree. This is what I meant in my previous comment as well, through a newtype StatusCode (u16) instead of u16. But using u16 directly is perhaps better. Having an get_status() to attempt the conversion makes sense 👍

@rmja
Copy link
Member

rmja commented May 13, 2024

Ahh yes! StatusCode(u16) is a good idea, I like it! We can have that together with a get_status(): Status on the response where we keep the Status:Unknown variant. That should not break anything going forward. We could also implement Into<Status> for StatusCode and TryFrom<StatusCode> for Status for. @simpkins does this make sense? Sorry for the confusion here. Feel free to make this, the pr is already approved:)

@simpkins
Copy link
Contributor Author

Sure, I can make those changes.

We could also implement Into<Status> for StatusCode and TryFrom<StatusCode> for Status

Did you perhaps mean TryFrom<Status> for StatusCode and From<StatusCode> for Status?

I did initially take a stab at providing TryFrom<StatusCode> for Status rather than From<StatusCode>, but this makes the upgrade process more awkward for downstream code, since they can't just say response.status.into() to upgrade existing sites expecting a Status enum.

This changes the Response `status` field from the existing `Status` enum
to a new `StatusCode(u16)` type.  This allows arbitrary code values to
be reported, including ones beyond the limited set represented by the
`Status` enum.

This is a breaking API change.  For one common conversion case,
Code using `match request.status {...}` to match against `Status` enum
values will need to be changed to `match request.status.into() {...}`
@rmja
Copy link
Member

rmja commented May 13, 2024

Looks perfect, thank you! I think that you can merge yourself when you are ready.

@simpkins
Copy link
Contributor Author

Looks perfect, thank you! I think that you can merge yourself when you are ready.

Thanks. It looks like the CI actions are stuck somehow though, so I don't think I can merge it yet. It's reporting the CI / ci (pull_request) action as still queued. The most recent run succeeded, but I wonder if it is somehow still waiting on this old one?

@lulf lulf merged commit 12c5ab7 into drogue-iot:main May 14, 2024
1 check passed
@lulf
Copy link
Member

lulf commented May 14, 2024

I re-ran the action, seemed to do the trick!

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.

3 participants