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

Added support for generics in server fns #3008

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

rakshith-ravi
Copy link
Collaborator

I'm still getting the following error when compiling server_fn_axum:

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'_>`
   --> src/app.rs:787:14
    |
787 | pub async fn test_fn<S>(
    |              ^^^^^^^^^^
    |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'_>` found
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
...
792 |     S: Display + Send + DeserializeOwned + Serialize + 'static,
    |                         ^^^^^^^^^^^^^^^^
note: required for `TestFn<S>` to implement `app::_::_serde::Deserialize<'de>`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^--------
    | |
    | unsatisfied trait bound introduced here
787 | pub async fn test_fn<S>(
    |              ^^^^^^^^^^
    = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` which comes from the expansion of the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'de>`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
    |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'de>` found
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
...
792 |     S: Display + Send + DeserializeOwned + Serialize + 'static,
    |                         ^^^^^^^^^^^^^^^^
    = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'_>`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
    |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'_>` found
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
...
792 |     S: Display + Send + DeserializeOwned + Serialize + 'static,
    |                         ^^^^^^^^^^^^^^^^
note: required for `app::_::<impl app::_::_serde::Deserialize<'de> for TestFn<S>>::deserialize::__Visitor<'de, S>` to implement `Visitor<'de>`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'_>`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
    |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'_>` found
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^
...
792 |     S: Display + Send + DeserializeOwned + Serialize + 'static,
    |                         ^^^^^^^^^^^^^^^^
note: required by a bound in `app::_::<impl app::_::_serde::Deserialize<'de> for TestFn<S>>::deserialize::__Visitor`
   --> src/app.rs:786:1
    |
786 | #[server]
    | ^^^^^^^^^ required by this bound in `__Visitor`
    = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'_>`
    --> src/app.rs:788:12
     |
788  |     first: S,
     |            ^
     |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'_>` found
    --> src/app.rs:786:1
     |
786  | #[server]
     | ^^^^^^^^^
...
792  |     S: Display + Send + DeserializeOwned + Serialize + 'static,
     |                         ^^^^^^^^^^^^^^^^
note: required by a bound in `next_element`
    --> /Users/rakshith/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.209/src/de/mod.rs:1733:12
     |
1731 |     fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
     |        ------------ required by a bound in this associated function
1732 |     where
1733 |         T: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element`
     = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed: cannot satisfy `S: app::_::_serde::Deserialize<'_>`
    --> src/app.rs:788:12
     |
788  |     first: S,
     |            ^
     |
note: multiple `impl`s or `where` clauses satisfying `S: app::_::_serde::Deserialize<'_>` found
    --> src/app.rs:786:1
     |
786  | #[server]
     | ^^^^^^^^^
...
792  |     S: Display + Send + DeserializeOwned + Serialize + 'static,
     |                         ^^^^^^^^^^^^^^^^
note: required by a bound in `next_value`
    --> /Users/rakshith/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.209/src/de/mod.rs:1872:12
     |
1870 |     fn next_value<V>(&mut self) -> Result<V, Self::Error>
     |        ---------- required by a bound in this associated function
1871 |     where
1872 |         V: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value`
     = note: this error originates in the derive macro `::leptos::server_fn::serde::Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0283`.
error: could not compile `server_fns_axum` (lib) due to 9 previous errors
Error: Failed to build server_fns_axum

Stack backtrace:
   0: std::backtrace::Backtrace::create
   1: anyhow::error::<impl anyhow::Error>::msg
   2: cargo_leptos::run::{{closure}}
   3: tokio::runtime::park::CachedParkThread::block_on
   4: tokio::runtime::context::runtime::enter_runtime
   5: cargo_leptos::main
   6: std::sys::backtrace::__rust_begin_short_backtrace
   7: std::rt::lang_start::{{closure}}
   8: std::rt::lang_start_internal
   9: _main
Error while executing command, exit code: 1

For context, test_fn expands to this:

#[doc = "Serialized arguments for the [`test_fn`] server function.\n\n"]
#[derive(
    Debug,
    Clone,
    ::leptos::server_fn::serde::Serialize,
    ::leptos::server_fn::serde::Deserialize,
)]
#[serde(crate = "leptos::server_fn::serde")]
pub struct TestFn<S>
where
    S: Display + Send + DeserializeOwned + Serialize + 'static,
{
    pub first: S,
    pub second: S,
}
impl<S> ::leptos::server_fn::ServerFn for TestFn<S>
where
    S: Display + Send + DeserializeOwned + Serialize + 'static,
{
    const PATH: &'static str = if "".is_empty() {
        ::leptos::server_fn::const_format::concatcp!(
            "/api",
            "/",
            "test_fn",
            ::leptos::server_fn::xxhash_rust::const_xxh64::xxh64(
                concat!(
                    env!("CARGO_MANIFEST_DIR"),
                    ":",
                    file!(),
                    ":",
                    line!(),
                    ":",
                    column!()
                )
                .as_bytes(),
                0
            )
        )
    } else {
        ::leptos::server_fn::const_format::concatcp!("/api", "")
    };
    type Client = ::leptos::server_fn::client::browser::BrowserClient;
    type ServerRequest = ::leptos::server_fn::request::BrowserMockReq;
    type ServerResponse = ::leptos::server_fn::response::BrowserMockRes;
    type Output = WhyNotResult;
    type InputEncoding = ::leptos::server_fn::codec::PostUrl;
    type OutputEncoding = ::leptos::server_fn::codec::Json;
    type Error = ::leptos::server_fn::error::NoCustomError;
    fn middlewares() -> Vec<
        std::sync::Arc<
            dyn ::leptos::server_fn::middleware::Layer<
                ::leptos::server_fn::request::BrowserMockReq,
                ::leptos::server_fn::response::BrowserMockRes,
            >,
        >,
    > {
        vec![]
    }
    #[allow(unused_variables)]
    async fn run_body(self) -> Result<WhyNotResult, ServerFnError> {
        unreachable!()
    }
}
#[allow(unused_variables)]
pub async fn test_fn<S>(
    first: S,
    second: S,
) -> Result<WhyNotResult, ServerFnError>
where
    S: Display + Send + DeserializeOwned + Serialize + 'static,
{
    use ::leptos::server_fn::ServerFn;
    let data = TestFn::<S> { first, second };
    data.run_on_client().await
}

Not sure why this is happening. Any help on this would be appreciated

@rakshith-ravi rakshith-ravi changed the title WIP added generic types for server fns WIP added support for generics in server fns Sep 22, 2024
@rakshith-ravi
Copy link
Collaborator Author

I am aware of the bad example namings and the debug prints that are currently present. Once I am able to get this working, I'll remove those and mark this PR as ready to review

@rakshith-ravi
Copy link
Collaborator Author

rakshith-ravi commented Sep 22, 2024

Oh my god I'm so stupid. I think I found the issue. Serde derives add a where clause of their own. Let me fix it and put an update to this PR.

@rakshith-ravi
Copy link
Collaborator Author

Okay, almost everything works now. The only part that's left is the inventory::submit! invocation that doesn't have a generic parameter that is defined on it

@benwis
Copy link
Contributor

benwis commented Sep 22, 2024

@rakshith-ravi Are you trying to enable having generics as inputs to a server function?

@rakshith-ravi
Copy link
Collaborator Author

Yes, sir.

Basically, trying to enable this:

#[server]
async fn test<S>(input: S) -> Result<(), ServerFnError>
where
    S: Display {
// body
}

In my work (the parts that I haven't pushed yet) I also have made the code a bit more flexible to eventually allow us to implement #2938

@rakshith-ravi rakshith-ravi changed the title WIP added support for generics in server fns Added support for generics in server fns Oct 1, 2024
@rakshith-ravi rakshith-ravi marked this pull request as ready for review October 1, 2024 11:18
@rakshith-ravi rakshith-ravi requested review from gbj and benwis October 1, 2024 11:18
@gbj
Copy link
Collaborator

gbj commented Oct 3, 2024

I added a commit that fixes the middleware-related issues on the examples here.

One small note: this is broken with one-argument server functions right now.

EDIT: I just added this in e2f096d because it was a one-liner

If I do something like this

#[server]
pub async fn test_fn<S>(input: S) -> Result<String, ServerFnError>
where
    S: Display,
{
    // insert a simulated wait
    tokio::time::sleep(std::time::Duration::from_millis(250)).await;
    Ok(input.to_string())
}

I get the error:

error[E0210]: type parameter `S` must be covered by another type when it appears before the first local type (`TestFn<_>`)
  --> src/app.rs:80:22
   |
80 | pub async fn test_fn<S>(input: S) -> Result<String, ServerFnError>
   |                      ^ type parameter `S` must be covered by another type when it appears before the first local type (`TestFn<_>`)
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last

Expanding the macro, I see that this is the issue:

impl<S> From<TestFn<S>> for S
where
    S: Display
        + ::leptos::server_fn::serde::Serialize
        + for<'leptos_param_lifetime> ::leptos::server_fn::serde::Deserialize<
            'leptos_param_lifetime,
        > + Send
        + 'static,
{
    fn from(value: TestFn<S>) -> Self {
        let TestFn::<S> { input } = value;
        input
    }
}

We add a From<_> argument for server fns with only one argument to convert from that argument to the server fn type easily. I think you most likely just need to suppress that implementation in the case that there's a generic.

@gbj
Copy link
Collaborator

gbj commented Oct 3, 2024

Okay, weirdly I now get a linker error with my generic example. Could someone else try building examples/server_fns_axum and see if it works for you? (It may need explicit registration for that test_fn too)

@rakshith-ravi
Copy link
Collaborator Author

Yeah I'm getting the same error too. Not sure what's going on. Will look into this further

@rakshith-ravi
Copy link
Collaborator Author

Slept on this and had an idea:

The inventory crate uses static linking to register the objects. Perhaps we aren't removing the auto registering properly? That could possibly cause problems with the linker

@benwis
Copy link
Contributor

benwis commented Nov 4, 2024

@rakshith-ravi Any update on this?

@rakshith-ravi
Copy link
Collaborator Author

No update yet. There seems to be this odd linker issue. If that is figured out then we're pretty much good to go

@rakshith-ravi rakshith-ravi force-pushed the feature/server-fn-generics branch from f9731e3 to 2d8466f Compare November 19, 2024 18:18
server_fn/src/lib.rs Outdated Show resolved Hide resolved
@rakshith-ravi
Copy link
Collaborator Author

I have no clue why CI is green now. All I did was a rebase.

Programming at it's peak: My code doesn't work and I don't know why; My code works and I don't know why

@benwis
Copy link
Contributor

benwis commented Nov 19, 2024

The autofix CI fixed some spacing too. Is this ready now?

@benwis
Copy link
Contributor

benwis commented Nov 19, 2024

Speaking of, it seems like the caveat here is that endpoint and prefix won't work here due to the multiple endpoints?

@rakshith-ravi
Copy link
Collaborator Author

I don't think that should make too much of a difference. The way this works is that each route will have to be explicitly registered. But apart from that, the prefix and endpoint routes shouldn't change much

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