use std::collections::BTreeMap;
use proc_macro2::TokenStream;
use quote::quote;
use schemars::schema::{Schema, SchemaObject};
use typify::{Type, TypeDetails, TypeId, TypeSpace};
use super::GenerateError;
#[derive(Debug)]
pub(super) struct Names(BTreeMap<String, TokenStream>);
impl Names {
pub(super) fn read<'a>(
space: &mut TypeSpace,
schemas: impl IntoIterator<Item = &'a str>,
) -> Result<Self, GenerateError> {
let resolved: Vec<(String, TypeId)> = schemas
.into_iter()
.map(|schema| {
let id = space
.add_type(&reference(schema))
.map_err(GenerateError::Typify)?;
Ok((schema.to_owned(), id))
})
.collect::<Result<_, GenerateError>>()?;
let mut claimed: BTreeMap<String, String> = BTreeMap::new();
let mut names = BTreeMap::new();
for (schema, id) in resolved {
let ty = space.get_type(&id).map_err(GenerateError::Typify)?;
let path = path_of(&ty);
if let Some(first) = claimed.insert(path.to_string(), schema.clone()) {
return Err(GenerateError::OneType {
first,
second: schema,
rust: ty.name(),
});
}
names.insert(schema, path);
}
Ok(Self(names))
}
pub(super) fn get(&self, schema: &str) -> Result<&TokenStream, GenerateError> {
self.0.get(schema).ok_or_else(|| GenerateError::NoType {
schema: schema.to_owned(),
})
}
}
fn reference(schema: &str) -> Schema {
Schema::Object(SchemaObject {
reference: Some(format!("#/definitions/{schema}")),
..SchemaObject::default()
})
}
fn path_of(ty: &Type<'_>) -> TokenStream {
let ident = ty.ident();
if defined_by_typify(ty) {
quote!(crate::types::#ident)
} else {
ident
}
}
fn defined_by_typify(ty: &Type<'_>) -> bool {
matches!(
ty.details(),
TypeDetails::Enum(_) | TypeDetails::Struct(_) | TypeDetails::Newtype(_)
)
}