mod context;
mod custom;
mod data;
mod error;
mod meta;
mod state;
use std::collections::btree_map::{Entry, VacantEntry};
use std::collections::{BTreeMap, VecDeque};
use std::str::FromStr;
use quote::format_ident;
use tracing::instrument;
use crate::config::{BoxFlags, GeneratorFlags, TypedefMode};
use crate::models::{
code::{IdentPath, ModuleIdent, ModulePath},
data::{DataType, DataTypes, PathData},
meta::{MetaTypeVariant, MetaTypes},
IdentType, TypeIdent,
};
use crate::traits::Naming;
pub use self::context::Context;
pub use self::custom::{ValueGenerator, ValueGeneratorBox, ValueGeneratorMode};
pub use self::error::Error;
pub use self::meta::MetaData;
use self::state::{LoopDetection, PendingType, State, TraitInfos, TypeRef};
#[must_use]
#[derive(Debug)]
pub struct Generator<'types> {
meta: MetaData<'types>,
state: State<'types>,
}
#[must_use]
#[derive(Debug)]
pub struct GeneratorFixed<'types> {
state: State<'types>,
data_types: DataTypes<'types>,
}
impl<'types> Generator<'types> {
pub fn new(types: &'types MetaTypes) -> Self {
let meta = MetaData {
types,
flags: GeneratorFlags::empty(),
postfixes: [
String::from("Type"), String::new(), String::from("ElementType"), String::new(), String::new(), String::new(), String::new(), String::new(), String::from("NotNil"), String::from("Dyn"), ],
box_flags: BoxFlags::AUTO,
typedef_mode: TypedefMode::Auto,
text_type: IdentPath::from_str("::xsd_parser_types::xml::Text").unwrap(),
mixed_type: IdentPath::from_str("::xsd_parser_types::xml::Mixed").unwrap(),
nillable_type: IdentPath::from_str("::xsd_parser_types::xml::Nillable").unwrap(),
any_type: IdentPath::from_str("::xsd_parser_types::xml::AnyElement").unwrap(),
any_attributes_type: IdentPath::from_str("::xsd_parser_types::xml::AnyAttributes")
.unwrap(),
};
let state = State {
cache: BTreeMap::new(),
pending: VecDeque::new(),
trait_infos: None,
loop_detection: LoopDetection::default(),
};
Self { meta, state }
}
pub fn box_flags(mut self, value: BoxFlags) -> Self {
self.meta.box_flags = value;
self
}
pub fn typedef_mode(mut self, value: TypedefMode) -> Self {
self.meta.typedef_mode = value;
self
}
pub fn flags(mut self, value: GeneratorFlags) -> Self {
self.meta.flags = value;
self
}
pub fn text_type<P>(mut self, path: P) -> Result<Self, P::Error>
where
P: TryInto<IdentPath>,
{
self.meta.text_type = path.try_into()?;
Ok(self)
}
pub fn mixed_type<P>(mut self, path: P) -> Result<Self, P::Error>
where
P: TryInto<IdentPath>,
{
self.meta.mixed_type = path.try_into()?;
Ok(self)
}
pub fn nillable_type<P>(mut self, path: P) -> Result<Self, P::Error>
where
P: TryInto<IdentPath>,
{
self.meta.nillable_type = path.try_into()?;
Ok(self)
}
pub fn any_type<P>(mut self, path: P) -> Result<Self, P::Error>
where
P: TryInto<IdentPath>,
{
self.meta.any_type = path.try_into()?;
Ok(self)
}
pub fn any_attributes_type<P>(mut self, path: P) -> Result<Self, P::Error>
where
P: TryInto<IdentPath>,
{
self.meta.any_attributes_type = path.try_into()?;
Ok(self)
}
pub fn with_flags(mut self, value: GeneratorFlags) -> Self {
self.meta.flags |= value;
self
}
pub fn with_type_postfix<S: Into<String>>(mut self, type_: IdentType, postfix: S) -> Self {
self.meta.postfixes[type_ as usize] = postfix.into();
self
}
pub fn with_type(mut self, ident: TypeIdent) -> Result<Self, Error> {
let module_ident = self
.meta
.types
.naming
.format_module(self.meta.types, Some(ident.ns));
let type_ident = format_ident!("{}", ident.name.to_string());
let path = PathData::from_path(IdentPath::from_parts(module_ident, type_ident));
let id = self.state.loop_detection.next_id(ident.clone());
let type_ref = TypeRef::new_fixed(id, path);
self.state.cache.insert(ident, type_ref);
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn generate_type(self, ident: TypeIdent) -> Result<GeneratorFixed<'types>, Error> {
self.into_fixed().generate_type(ident)
}
#[instrument(err, level = "trace", skip(self))]
pub fn generate_named_types(self) -> Result<GeneratorFixed<'types>, Error> {
self.into_fixed().generate_named_types()
}
pub fn generate_all_types(self) -> Result<GeneratorFixed<'types>, Error> {
self.into_fixed().generate_all_types()
}
pub fn into_fixed(self) -> GeneratorFixed<'types> {
let Self { meta, state } = self;
let data_types = DataTypes::new(meta);
GeneratorFixed { state, data_types }
}
}
impl<'types> GeneratorFixed<'types> {
#[instrument(err, level = "trace", skip(self))]
pub fn generate_type(mut self, ident: TypeIdent) -> Result<GeneratorFixed<'types>, Error> {
self.state
.get_or_create_type_ref_mut(&self.data_types.meta, &ident)?;
self.generate_pending()?;
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn generate_all_types(mut self) -> Result<Self, Error> {
for ident in self.data_types.meta.types.items.keys() {
self.state
.get_or_create_type_ref_mut(&self.data_types.meta, ident)?;
}
self.generate_pending()?;
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn generate_named_types(mut self) -> Result<Self, Error> {
for ident in self.data_types.meta.types.items.keys() {
if ident.name.is_named() {
self.state
.get_or_create_type_ref_mut(&self.data_types.meta, ident)?;
}
}
self.generate_pending()?;
Ok(self)
}
#[instrument(level = "trace", skip(self))]
pub fn finish(self) -> DataTypes<'types> {
self.data_types
}
#[instrument(err, level = "trace", skip(self))]
fn generate_pending(&mut self) -> Result<(), Error> {
while let Some(args) = self.state.pending.pop_front() {
self.generate_type_intern(args)?;
}
Ok(())
}
#[instrument(err, level = "trace", skip(self))]
fn generate_type_intern(&mut self, data: PendingType<'types>) -> Result<(), Error> {
let PendingType { ty, ident } = data;
let Self { state, data_types } = self;
let mut context = Context::new(&data_types.meta, &ident, state);
let ty = DataType::new(ty, &mut context)?;
data_types.items.insert(ident, ty);
Ok(())
}
}
impl<'types> State<'types> {
#[instrument(level = "trace", skip(self, meta))]
fn get_or_create_type_ref_mut(
&mut self,
meta: &MetaData<'types>,
ident: &TypeIdent,
) -> Result<&mut TypeRef, Error> {
match self.cache.entry(ident.clone()) {
Entry::Occupied(e) => Ok(e.into_mut()),
Entry::Vacant(e) => {
let id = self.loop_detection.next_id(e.key().clone());
Self::create_type_ref(id, &*meta.naming, &mut self.pending, e, meta, ident)
}
}
}
fn create_type_ref<'a>(
id: usize,
naming: &dyn Naming,
pending: &mut VecDeque<PendingType<'types>>,
entry: VacantEntry<'a, TypeIdent, TypeRef>,
meta: &MetaData<'types>,
ident: &TypeIdent,
) -> Result<&'a mut TypeRef, Error> {
let ty = meta
.types
.items
.get(ident)
.ok_or_else(|| Error::UnknownType(ident.clone()))?;
let name = naming.make_type_name(&meta.postfixes, ty, ident);
let path = match &ty.variant {
MetaTypeVariant::BuildIn(x) => {
let path = if meta.check_generator_flags(GeneratorFlags::BUILD_IN_ABSOLUTE_PATHS) {
x.absolute_ident_path()
} else {
x.ident_path()
};
PathData::from_path(path)
}
MetaTypeVariant::Custom(x) => {
if let Some(using) = x.include() {
if meta.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS) {
let path = IdentPath::from_str(using)?;
PathData::from_path(path)
} else {
let path = IdentPath::from_ident(format_ident!("{}", x.name()));
PathData::from_path(path).with_using(using)
}
} else {
let path = IdentPath::from_ident(format_ident!("{}", x.name())).with_path(None);
PathData::from_path(path)
}
}
_ => {
let module_ident = ModuleIdent::new(
meta.types,
ident,
meta.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
meta.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
);
let module_path = ModulePath::from_ident(meta.types, module_ident);
let type_ident = naming.format_type_ident(&name, ty.display_name.as_deref());
let path = IdentPath::from_parts(module_path.0, type_ident);
PathData::from_path(path)
}
};
tracing::debug!("Queue new type generation: {ident}");
pending.push_back(PendingType {
ty,
ident: ident.clone(),
});
Ok(entry.insert(TypeRef::new_pending(id, path)))
}
}