use std::borrow::Cow;
use std::collections::BTreeSet;
use proc_macro2::TokenStream;
use crate::{Typespace, TypespaceRenderer, TypespaceTrait, build};
pub struct Type<'a, Id> {
pub(crate) typespace: &'a Typespace<Id>,
pub(crate) id: &'a Id,
pub(crate) typ: &'a build::Type<Id>,
}
impl<'a, Id: Clone + Ord + std::fmt::Debug + std::fmt::Display> Type<'a, Id> {
pub fn name(&self) -> Cow<'a, str> {
match self.typ {
build::Type::Enum(e) => Cow::Borrowed(e.common.built_name()),
build::Type::Struct(s) => Cow::Borrowed(s.common.built_name()),
build::Type::UnitStruct(u) => Cow::Borrowed(u.common.built_name()),
build::Type::TupleStruct(t) => Cow::Borrowed(t.common.built_name()),
build::Type::NewtypeStruct(n) => Cow::Borrowed(n.common.built_name()),
build::Type::TypeAlias(a) => Cow::Borrowed(a.common.built_name()),
_ => Cow::Owned(self.ident().to_string()),
}
}
pub fn ident(&self) -> TokenStream {
TypespaceRenderer::new(&self.typespace.types, &self.typespace.settings)
.render_ident(self.id)
}
pub fn ident_in(&self, scope: &str) -> TokenStream {
TypespaceRenderer::new(&self.typespace.types, &self.typespace.settings)
.render_ident_with_scope(self.id, Some(scope))
}
pub fn parameter_ident(&self, scope: Option<&str>, lifetime: Option<&str>) -> TokenStream {
self.renderer()
.render_parameter_ident(self.id, scope, lifetime)
}
pub fn builder_ident(&self, scope: Option<&str>) -> Option<TokenStream> {
self.renderer().render_builder_ident(self.id, scope)
}
fn renderer(&self) -> TypespaceRenderer<'_, Id> {
TypespaceRenderer::new(&self.typespace.types, &self.typespace.settings)
}
pub fn description(&self) -> Option<&str> {
let common = match self.typ {
build::Type::Enum(e) => &e.common,
build::Type::Struct(s) => &s.common,
build::Type::UnitStruct(u) => &u.common,
build::Type::TupleStruct(t) => &t.common,
build::Type::NewtypeStruct(n) => &n.common,
build::Type::TypeAlias(a) => &a.common,
_ => return None,
};
common.description.as_deref()
}
pub fn details(&self) -> TypeDetails<'a, Id> {
match self.typ {
build::Type::Enum(e) => TypeDetails::Enum(Enum { inner: e }),
build::Type::Struct(s) => TypeDetails::Struct(Struct { inner: s }),
build::Type::NewtypeStruct(n) => TypeDetails::Newtype(NewtypeStruct { inner: n }),
build::Type::Option(id) => TypeDetails::Option(id.clone()),
build::Type::Vec(id) => TypeDetails::Vec(id.clone()),
build::Type::Map(k, v) => TypeDetails::Map(k.clone(), v.clone()),
build::Type::Set(id) => TypeDetails::Set(id.clone()),
build::Type::Box(id) => TypeDetails::Box(id.clone()),
build::Type::Array(id, n) => TypeDetails::Array(id.clone(), *n),
build::Type::Tuple(ids) => TypeDetails::Tuple(Box::new(ids.clone().into_iter())),
build::Type::Unit => TypeDetails::Unit,
build::Type::String => TypeDetails::String,
build::Type::Boolean => TypeDetails::Builtin(Cow::Borrowed("bool")),
build::Type::Integer(s) => TypeDetails::Builtin(Cow::Borrowed(s.as_str())),
build::Type::Float(s) => TypeDetails::Builtin(Cow::Borrowed(s.as_str())),
build::Type::JsonValue => TypeDetails::Builtin(Cow::Borrowed("::serde_json::Value")),
build::Type::Never => TypeDetails::Builtin(Cow::Borrowed("::json_serde::Absent")),
build::Type::Native(n) => {
TypeDetails::Builtin(Cow::Owned(crate::settings::path_text(n.path())))
}
build::Type::UnitStruct(_)
| build::Type::TupleStruct(_)
| build::Type::TypeAlias(_) => TypeDetails::Builtin(Cow::Borrowed(self.name_str())),
}
}
fn name_str(&self) -> &'a str {
match self.typ {
build::Type::UnitStruct(build::UnitStruct { common, .. })
| build::Type::TupleStruct(build::TupleStruct { common, .. }) => common.built_name(),
build::Type::TypeAlias(build::TypeAlias { common, .. }) => common.built_name(),
_ => "",
}
}
pub fn has_impl(&self, trait_: TypespaceTrait) -> bool {
crate::has_trait(
&self.typespace.types,
&self.typespace.settings,
self.id,
trait_,
&mut BTreeSet::new(),
)
}
}
#[non_exhaustive]
pub enum TypeDetails<'a, Id> {
Enum(Enum<'a, Id>),
Struct(Struct<'a, Id>),
Newtype(NewtypeStruct<'a, Id>),
Option(Id),
Vec(Id),
Map(Id, Id),
Set(Id),
Box(Id),
Tuple(Box<dyn Iterator<Item = Id> + 'a>),
Array(Id, usize),
Builtin(Cow<'a, str>),
Unit,
String,
}
pub struct Struct<'a, Id> {
inner: &'a build::Struct<Id>,
}
impl<'a, Id: Clone> Struct<'a, Id> {
pub fn properties(&self) -> impl Iterator<Item = (&'a str, Id)> + 'a {
self.inner
.properties
.iter()
.map(|p| (p.rust_name.as_str(), p.type_id.clone()))
}
pub fn properties_info(&self) -> impl Iterator<Item = StructProperty<'a, Id>> + 'a {
self.inner.properties.iter().map(|p| StructProperty {
name: p.rust_name.as_str(),
description: p.description.as_deref(),
required: matches!(p.state, build::StructPropertyState::Required),
type_id: p.type_id.clone(),
})
}
}
pub struct StructProperty<'a, Id> {
pub name: &'a str,
pub description: Option<&'a str>,
pub required: bool,
pub type_id: Id,
}
pub struct Enum<'a, Id> {
inner: &'a build::Enum<Id>,
}
impl<'a, Id: Clone> Enum<'a, Id> {
pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, VariantDetails<Id>)> {
self.inner
.variants
.iter()
.map(|v| (v.rust_name.as_str(), variant_details_to_info(&v.details)))
}
pub fn variants_info(&'a self) -> impl Iterator<Item = EnumVariant<'a, Id>> {
self.inner.variants.iter().map(|v| EnumVariant {
name: v.rust_name.as_str(),
description: v.description.as_deref(),
details: variant_details_to_info(&v.details),
})
}
}
fn variant_details_to_info<Id: Clone>(details: &build::VariantDetails<Id>) -> VariantDetails<Id> {
match details {
build::VariantDetails::Unit => VariantDetails::Unit,
build::VariantDetails::Item(id) => VariantDetails::Tuple(vec![id.clone()]),
build::VariantDetails::Tuple(ids) => VariantDetails::Tuple(ids.clone()),
build::VariantDetails::Struct(props) => VariantDetails::Struct(
props
.iter()
.map(|p| (p.rust_name.to_string(), p.type_id.clone()))
.collect(),
),
}
}
pub struct EnumVariant<'a, Id> {
pub name: &'a str,
pub description: Option<&'a str>,
pub details: VariantDetails<Id>,
}
#[non_exhaustive]
pub enum VariantDetails<Id> {
Unit,
Tuple(Vec<Id>),
Struct(Vec<(String, Id)>),
}
pub struct NewtypeStruct<'a, Id> {
inner: &'a build::NewtypeStruct<Id>,
}
impl<'a, Id: Clone> NewtypeStruct<'a, Id> {
pub fn inner(&self) -> Id {
self.inner.inner.clone()
}
}