mod codegen;
mod generator;
mod parser;
mod utils;
use heck::ToPascalCase;
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use syn::parse_macro_input;
use generator::*;
use parser::*;
#[proc_macro]
pub fn openapi_client(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as OpenApiInput);
match generate_client(&input) {
Ok(tokens) => tokens.into(),
Err(e) => syn::Error::new(Span::call_site(), e)
.to_compile_error()
.into(),
}
}
fn generate_client(input: &OpenApiInput) -> Result<TokenStream2, String> {
let spec = load_openapi_spec(input)?;
let client_name = if let Some(name) = &input.client_name {
format_ident!("{}", name)
} else {
let title = spec.info.title.clone();
let sanitized_title = title
.chars()
.filter(|c| c.is_alphanumeric() || c.is_whitespace())
.collect::<String>()
.to_pascal_case();
format_ident!("{}Api", sanitized_title)
};
let structs = generate_structs(&spec)?;
let client_impl = generate_client_impl(&spec, &client_name)?;
let error_types = generate_error_types();
let client_doc = generate_client_doc_comment(&spec, &client_name.to_string());
Ok(quote! {
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#error_types
#structs
#client_doc
#[derive(Clone)]
pub struct #client_name {
base_url: String,
client: reqwest::Client,
}
#client_impl
})
}