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 Json::erased constructor #509

Closed
wants to merge 1 commit into from
Closed

Add Json::erased constructor #509

wants to merge 1 commit into from

Conversation

jplatte
Copy link
Member

@jplatte jplatte commented Nov 13, 2021

So I couldn't actually do the copy-free hack I wanted because Box<RawValue> can't be converted to Box<str> with serde_jsons current API (though the impl would be easy to add there). The idea there way to use the castaway crate or implement the same logic manually, to "specialize" the Box<RawValue> body in the IntoResponse impl.

Might still be worth having this.

To do:

  • Improve documentation
  • Create an error type to wrap the serde_json::Error in that implements IntoResponse? This would allow returning Json::Erased from a handler, letting IntoResponse "handle" the error the same way as serialization errors in Json<OwnType> are handled.

@jplatte jplatte requested a review from davidpdrsn November 13, 2021 19:15
@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

@davidpdrsn ignoring todos and broken doctest, wdyt about the general idea?

@davidpdrsn
Copy link
Member

I like it!

@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

Docs could certainly still be better, but I'm not feeling very inspired. Are you okay with merging with the foos & bars?

Copy link
Member

@davidpdrsn davidpdrsn left a comment

Choose a reason for hiding this comment

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

Besides the missing docs it looks good to me!

@@ -196,6 +221,39 @@ where
}
}

pub struct JsonSerializationError(serde_json::Error);
Copy link
Member

Choose a reason for hiding this comment

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

I guess we only warn for missing docs on CI. Should probably be an error.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, right. This type also needs docs 😅

@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

Just realized that the JsonSerializationError was not nameable outside the crate. I decided to now re-export it from the responses module since it is only relevant for the use of Json as a response, not as an extractor.

@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

Just thought of something else: As currently implemented, this makes serde_json a public dependency, which I don't think it is right now.

An alternative way of implementing this, which also wouldn't expose that the erased function really does a fallible operation and returns Result<Json, _>, would be returning struct ErasedJson(Result<_, _>); instead.

How do you feel about that?

@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

And, another thing: Maybe there's a better name for this than erased?

@jplatte
Copy link
Member Author

jplatte commented Nov 13, 2021

I guess if there's a separate ErasedJson type, the function might as well be moved to it, and then any reuse of code between it and Json becomes an implementation detail, leading back to the start of this whole idea 😅

Maybe axum could have (as a separate crate?) some non-generic IntoResponse types that work like that

@davidpdrsn
Copy link
Member

Just thought of something else: As currently implemented, this makes serde_json a public dependency, which I don't think it is right now.

I think thats okay. Realistically we're unlikely to move away from serde_json. So I don't we have to introduce some kind of struct ErasedJson(Result<_, _>);.

And, another thing: Maybe there's a better name for this than erased?

I agree its not great 🤔

Could we do

impl<T> TryFrom<T> for Json<Box<serde_json::value::RawValue>>
where
    T: Serialize,
{
    type Error = JsonSerializationError;

    fn try_from(value: T) -> Result<Self, Self::Error> {
        // ...
    }
}

and sidestep the naming issue that way?

@jplatte
Copy link
Member Author

jplatte commented Nov 15, 2021

I think TryFrom wouldn't work since there is a blanket From<T> for Json<T> and a blanket From => TryFrom<Error = Infallible> in std.

@davidpdrsn
Copy link
Member

davidpdrsn commented Nov 17, 2021

Maybe we should release this as a tiny standalone crate 🤷 Then we can more easily break change things.

@davidpdrsn
Copy link
Member

Perhaps some sort of "axum-util" kinda crate could be good to have. Just as sort of a grab bag of things that seem nice but where we're not super confident about the API. This could go in there together with https://github.com/davidpdrsn/axum-resource.

I also have an extractor like this in a project of mine:

pub struct Cached<T>(pub T);

#[async_trait]
impl<B, T> FromRequest<B> for Cached<T>
where
    B: Send,
    T: FromRequest<B> + Clone + Send + Sync + 'static,
{
    type Rejection = T::Rejection;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        if let Ok(Extension(value)) = Extension::from_request(req).await {
            Ok(Self(value))
        } else {
            let value = T::from_request(req).await?;
            req.extensions_mut().unwrap().insert(value.clone());
            Ok(Self(value))
        }
    }
}

This is useful when you have a tree of extractors that might contain duplicate parts. For example I have a Layout that supports rending templates wrapped in a common layout. That requires the "current user" which my auth extractor also requires. So to avoid authorizing the request twice I can use Cached<CurrentUser>.

I'm not sure this is super useful but would be nice to put it somewhere without too much commitment.

@jplatte
Copy link
Member Author

jplatte commented Nov 17, 2021

Perhaps some sort of "axum-util" kinda crate could be good to have.

I agree. Rocket had sth. like that too (rocket-contrib) until the things in there matured more and moved into their own crate.

I'll close this PR and create a separate one for ErasedJson as a distinct type in the new crate (another possible name: axum-extra) once it exists.

@jplatte jplatte closed this Nov 17, 2021
@jplatte jplatte deleted the erased-json-ctor branch November 17, 2021 19:35
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.

2 participants