oasgen-core 0.5.0

Dependency of oasgen. Generates OpenAPI 3.0 spec based on Rust code. Works with actix-web, but architected to easily extend to other frameworks (or no framework).
Documentation
#![allow(unused)]

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use openapiv3::{MediaType, Operation, ReferenceOr, RequestBody, Response, Responses, StatusCode};
use pin_project_lite::pin_project;
use openapiv3 as oa;
use crate::OaSchema;


fn type_name_to_operation_id(type_name: &str) -> Option<String> {
    Some(type_name.split("::").skip(1).collect::<Vec<_>>().join("_"))
}

pub trait OaOperation<Signature> {
    /// Allows looking up the schema itself of a referenced schema
    fn referenced_schema(schema: &str) -> oa::Schema;
    /// List of all schemas referenced by this operation
    fn references() -> Vec<&'static str>;
    /// Constructs the operation
    fn operation() -> Operation;
}

/// Not meant for public use. This is autogenerated for any function annotated with #[openapi]
/// It allows function metadata (docstring, arg schemas.) to be defined at runtime (instead of lost during compilation)
pub trait FunctionMetadata {
    /// This is Option<static str> because it basically just comes from function annotation attributes.
    /// If that isn't defined, and this is none, then we compute it from the function name at runtime.
    fn operation_id() -> Option<&'static str>;

    fn summary() -> Option<&'static str>;

    fn description() -> Option<&'static str>;
}

pin_project! {
    /// Every openapi function has its return type wrapped in this return type.
    /// That gives us a PhantomData marker which stores the function metadata to make it available at runtime.
    #[repr(transparent)]
    pub struct TypedResponseFuture<F, Signature> {
        #[pin]
        inner: F,
        _marker: std::marker::PhantomData<Signature>,
    }
}

impl<F, Signature> TypedResponseFuture<F, Signature>
{
    pub fn new(inner: F) -> Self {
        Self { inner, _marker: Default::default() }
    }
}

impl<F, Signature> Future for TypedResponseFuture<F, Signature>
    where
        F: Future,
{
    type Output = F::Output;

    #[inline]
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.as_mut().project();
        this.inner.poll(cx)
    }
}

macro_rules! construct_operation {
    ($($arg:ident),+) => {
        impl<F, $($arg),+ , Fut, FuncMetadata> OaOperation<( $($arg),+ , Fut, FuncMetadata)> for F
            where
                F: Fn($($arg),+) -> TypedResponseFuture<Fut, FuncMetadata>,
                Fut: Future,
                $($arg: OaSchema),+,
                Fut::Output: OaSchema,
                FuncMetadata: FunctionMetadata,
        {
            fn referenced_schema(schema: &str) -> oa::Schema {
                $(
                    if $arg::schema_name().map(|s| s == schema).unwrap_or(false) {
                        return $arg::schema().unwrap();
                    }
                )+
                if Fut::Output::schema_name().map(|s| s == schema).unwrap_or(false) {
                    Fut::Output::schema().unwrap()
                } else {
                    panic!("Unknown schema: {}", schema)
                }
            }

            fn references() -> Vec<&'static str> {
                vec![$($arg::schema_name()),+, Fut::Output::schema_name()].into_iter().flatten().collect()
            }

            fn operation() -> Operation {
                let parameters = vec![
                    $( $arg::parameters(), )+
                ]
                    .into_iter()
                    .flatten()
                    .flatten()
                    .collect::<Vec<_>>();

                let body = vec![
                    $( $arg::schema_ref(), )+
                ].into_iter().flatten().next();

                let mut operation = Operation {
                    operation_id: type_name_to_operation_id(std::any::type_name::<F>()),
                    summary: FuncMetadata::summary().map(str::to_string),
                    parameters,
                    ..Operation::default()
                };
                operation.add_request_body_json(body);
                operation.add_response_success_json(Fut::Output::schema_ref());
                operation
            }
        }
    }
}

construct_operation!(A1);
construct_operation!(A1, A2);
construct_operation!(A1, A2, A3);
construct_operation!(A1, A2, A3, A4);
construct_operation!(A1, A2, A3, A4, A5);
construct_operation!(A1, A2, A3, A4, A5, A6);
construct_operation!(A1, A2, A3, A4, A5, A6, A7);
construct_operation!(A1, A2, A3, A4, A5, A6, A7, A8);