1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-05-09 08:00:41 +00:00

basic structure for openapi_type crate

This commit is contained in:
Dominic 2021-03-07 19:05:25 +01:00
parent 2251c29d7b
commit 90870e3b6a
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
9 changed files with 261 additions and 1 deletions

View file

@ -0,0 +1,22 @@
# -*- eval: (cargo-minor-mode 1) -*-
[package]
workspace = ".."
name = "openapi_type_derive"
version = "0.1.0-dev"
authors = ["Dominic Meiser <git@msrd0.de>"]
edition = "2018"
description = "Implementation detail of the openapi_type crate"
license = "Apache-2.0"
repository = "https://gitlab.com/msrd0/gotham-restful/-/tree/master/openapi_type_derive"
# tests are done using trybuild exclusively
autotests = false
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0"

View file

@ -0,0 +1,93 @@
#![warn(missing_debug_implementations, rust_2018_idioms)]
#![deny(broken_intra_doc_links)]
#![forbid(unsafe_code)]
//! This crate defines the macros for `#[derive(OpenapiType)]`.
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
parse_macro_input, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, TraitBound,
TraitBoundModifier, TypeParamBound
};
#[proc_macro_derive(OpenapiType)]
pub fn derive_openapi_type(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
expand_openapi_type(input).unwrap_or_else(|err| err.to_compile_error()).into()
}
macro_rules! path {
(:: $($segment:ident)::*) => {
path!(@private Some(Default::default()), $($segment),*)
};
($($segment:ident)::*) => {
path!(@private None, $($segment),*)
};
(@private $leading_colon:expr, $($segment:ident),*) => {
{
#[allow(unused_mut)]
let mut segments: ::syn::punctuated::Punctuated<::syn::PathSegment, _> = Default::default();
$(
segments.push(::syn::PathSegment {
ident: ::proc_macro2::Ident::new(stringify!($segment), ::proc_macro2::Span::call_site()),
arguments: Default::default()
});
)*
::syn::Path {
leading_colon: $leading_colon,
segments
}
}
};
}
fn expand_openapi_type(mut input: DeriveInput) -> syn::Result<TokenStream2> {
let ident = &input.ident;
// prepare the generics - all impl generics will get `OpenapiType` requirement
let (impl_generics, ty_generics, where_clause) = {
let generics = &mut input.generics;
generics.type_params_mut().for_each(|param| {
param.colon_token.get_or_insert_with(Default::default);
param.bounds.push(TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: path!(::openapi_type::OpenapiType)
}))
});
generics.split_for_impl()
};
// parse the input data
match &input.data {
Data::Struct(strukt) => parse_struct(strukt)?,
Data::Enum(inum) => parse_enum(inum)?,
Data::Union(union) => parse_union(union)?
};
// generate the impl code
Ok(quote! {
impl #impl_generics ::openapi_type::OpenapiType for #ident #ty_generics #where_clause {
fn schema() -> ::openapi_type::OpenapiSchema {
unimplemented!()
}
}
})
}
fn parse_struct(_strukt: &DataStruct) -> syn::Result<()> {
Ok(())
}
fn parse_enum(_inum: &DataEnum) -> syn::Result<()> {
unimplemented!()
}
fn parse_union(union: &DataUnion) -> syn::Result<()> {
Err(syn::Error::new(
union.union_token.span(),
"#[derive(OpenapiType)] cannot be used on unions"
))
}