tamasfe/aide

[Community poll] Hellish dependencies and a possible breaking change to fix it all

Closed this issue · 4 comments

Wicpar commented

Due to the structure of Rust, and the still missing feture in stable of default impls that would allow to fix this issue entirely, this crate is constrained on many levels.

In theory for everything to be nice and easy, users should be able to implement OperationInput and OperationOutput on types as needed. This is no issue for types in one's own crate, however as soon as a dependency like axum and its many utility crates like axum-sqlx-tx need one of those traits it needs to be implemented either in this crate, or their crate.

Since only one version of crates can be implemented at once i am forced to use a fork of this library. It's possible quite a few of you do too.

Many of those types simply have no implementation for generating docs, and those who do need one must be implementable by the user's crate even on foreign types

I am proposing two new structs: NoDoc<T>(pub T) and WithDoc<T, D>(pub T, PhantomData<D>).
NoDoc<T>(pub T) simpy wraps a type and implements the traits like IntoResponse or FromRequestParts, etc... as is if T implements them and adds no doc, so it would be very useful for axum-sqlx-tx

async fn get_axum_request(NoDoc(mut tx): NoDoc<Tx<Postgres>>)  {}

WithDoc<T, D>(pub T, PhantomData<D>) is the same, but has D which is a user provided struct that proxies OperationInput and OperationOutput for T.

struct MyDoc;

impl OperationInput for Mydoc {
 ...
}

async fn get_axum_request(WithDoc(mut tx, _): WithDoc<Tx<Postgres>, MyDoc>)  {}

Other utilities like Deref Into AsRef, etc... will be provided, please tell me if there are any you with to see.

This will allow the separation and or deletion of the many unmaintainable feature gates into separate crates that can allow for better multi-version support

Thanks to @tamasfe i can make these changes, however such a fundamental change in how the dependencies are managed this is completely breaking for everyone and as such i would like the input of as many people that use this crate as possible.

@tamasfe i know you don't have the time, but since this is your crate i don't want to make big changes without your approval.

Wicpar commented

Other possible naming scheme: NoAPI and WithAPI
I tend to prefer this one, but seems less correct of a name.

I'm in favor of this change

Thank you @Wicpar! I'm perfectly fine with this, it definitely helps with libraries that are not aware of aide and makes overriding/hiding parameters easier in some cases.

Other possible naming scheme: NoAPI and WithAPI

I prefer these as well, since we have api_route, if we're fine with them not being symmetric, we could also go with something like Hidden<T>.

One more thing to note, I'd prefer following Rust's naming convention (e.g. NoApi).

Wicpar commented

All that remains now with the merge done is determine what to do with dependencies.

for axum-sqlx-tx i successfully used it like this:

use aide::NoApi;
use axum_sqlx_tx::Tx as BaseTx;

pub type Tx<T> = NoApi<BaseTx<T>>;
#[debug_handler(state = ServerState)]
async fn get_api_keys(
    mut tx: Tx<Postgres>, // previously defined tx
    session: Session,
) -> Result<Json<Vec<ApiKey>>, StatusError> {
    Ok(Json(
        query!("blablabla")
        .fetch_all(&mut *tx) // deref mut
        .await?,
    ))
}

the type definition could be made a crate, one for each version of axum-sqlx-tx as not all are compatible with the latest sqlx versions.