mod error;
mod name_builder;
mod schema;
mod state;
mod variant_builder;
use std::fmt::Debug;
use crate::config::Namespace;
use crate::schema::xs::ProcessContentsType;
use crate::schema::{MaxOccurs, Schemas};
use crate::types::{
AnyAttributeInfo, AnyInfo, BuildInInfo, ComplexInfo, GroupInfo, Ident, Module, Name,
ReferenceInfo, Type, TypeVariant, Types,
};
pub use error::Error;
use tracing::instrument;
use self::schema::SchemaInterpreter;
use self::state::{Node, State};
use self::variant_builder::VariantBuilder;
#[must_use]
#[derive(Debug)]
pub struct Interpreter<'a> {
state: State<'a>,
schemas: &'a Schemas,
}
impl<'a> Interpreter<'a> {
pub fn new(schemas: &'a Schemas) -> Self {
let state = State::default();
Self { state, schemas }
}
#[instrument(err, level = "trace", skip(self))]
pub fn with_type<I, T>(mut self, ident: I, type_: T) -> Result<Self, Error>
where
I: Into<Ident> + Debug,
T: Into<Type> + Debug,
{
self.state.add_type(ident, type_, true)?;
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn with_typedef<I, T>(mut self, ident: I, type_: T) -> Result<Self, Error>
where
I: Into<Ident> + Debug,
T: Into<Ident> + Debug,
{
self.state
.add_type(ident, ReferenceInfo::new(type_), true)?;
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn with_buildin_types(mut self) -> Result<Self, Error> {
macro_rules! add {
($ident:ident, $type:ident) => {
self.state
.add_type(Ident::$ident, BuildInInfo::$type, true)?;
};
}
add!(U8, U8);
add!(U16, U16);
add!(U32, U32);
add!(U64, U64);
add!(U128, U128);
add!(USIZE, Usize);
add!(I8, I8);
add!(I16, I16);
add!(I32, I32);
add!(I64, I64);
add!(I128, I128);
add!(ISIZE, Isize);
add!(F32, F32);
add!(F64, F64);
add!(BOOL, Bool);
add!(STRING, String);
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn with_default_typedefs(mut self) -> Result<Self, Error> {
let xs = self
.schemas
.resolve_namespace(&Some(Namespace::XS))
.ok_or_else(|| Error::UnknownNamespace(Namespace::XS.clone()))?;
macro_rules! add {
($ns:ident, $src:expr, $dst:ident) => {
self.state.add_type(
Ident::type_($src).with_ns(Some($ns)),
ReferenceInfo::new(Ident::$dst),
true,
)?;
};
}
macro_rules! add_list {
($ns:ident, $src:expr, $dst:ident) => {
self.state.add_type(
Ident::type_($src).with_ns(Some($ns)),
ReferenceInfo::new(Ident::$dst)
.min_occurs(0)
.max_occurs(MaxOccurs::Unbounded),
true,
)?;
};
}
add!(xs, "string", STRING);
add!(xs, "boolean", BOOL);
add!(xs, "decimal", F64);
add!(xs, "float", F32);
add!(xs, "double", F64);
add!(xs, "duration", STRING);
add!(xs, "dateTime", STRING);
add!(xs, "time", STRING);
add!(xs, "date", STRING);
add!(xs, "gYearMonth", STRING);
add!(xs, "gYear", STRING);
add!(xs, "gMonthDay", STRING);
add!(xs, "gMonth", STRING);
add!(xs, "gDay", STRING);
add!(xs, "hexBinary", STRING);
add!(xs, "base64Binary", STRING);
add!(xs, "anyURI", STRING);
add!(xs, "QName", STRING);
add!(xs, "NOTATION", STRING);
add!(xs, "long", I64);
add!(xs, "int", I32);
add!(xs, "integer", I32);
add!(xs, "short", I16);
add!(xs, "byte", I8);
add!(xs, "negativeInteger", ISIZE);
add!(xs, "nonPositiveInteger", ISIZE);
add!(xs, "unsignedLong", U64);
add!(xs, "unsignedInt", U32);
add!(xs, "unsignedShort", U16);
add!(xs, "unsignedByte", U8);
add!(xs, "positiveInteger", USIZE);
add!(xs, "nonNegativeInteger", USIZE);
add!(xs, "normalizedString", STRING);
add!(xs, "token", STRING);
add!(xs, "language", STRING);
add!(xs, "NMTOKEN", STRING);
add!(xs, "Name", STRING);
add!(xs, "NCName", STRING);
add!(xs, "ID", STRING);
add!(xs, "IDREF", STRING);
add_list!(xs, "NMTOKENS", STRING);
add_list!(xs, "IDREFS", STRING);
add_list!(xs, "ENTITY", STRING);
add_list!(xs, "ENTITIES", STRING);
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn with_xs_any_type(mut self) -> Result<Self, Error> {
let xs = self
.schemas
.resolve_namespace(&Some(Namespace::XS))
.ok_or_else(|| Error::UnknownNamespace(Namespace::XS.clone()))?;
let content_name = self.state.name_builder().shared_name("Content").finish();
let content_ident = Ident::new(content_name).with_ns(Some(xs));
let content_variant = TypeVariant::Sequence(GroupInfo {
any: Some(AnyInfo {
min_occurs: Some(0),
max_occurs: Some(MaxOccurs::Unbounded),
process_contents: Some(ProcessContentsType::Lax),
..Default::default()
}),
..Default::default()
});
let content_type = Type::new(content_variant);
self.state
.add_type(content_ident.clone(), content_type, true)?;
let ident = Ident::type_("anyType").with_ns(Some(xs));
let variant = TypeVariant::ComplexType(ComplexInfo {
content: Some(content_ident),
min_occurs: 1,
max_occurs: MaxOccurs::Bounded(1),
any_attribute: Some(AnyAttributeInfo {
process_contents: Some(ProcessContentsType::Lax),
..Default::default()
}),
..Default::default()
});
let type_ = Type::new(variant);
self.state.add_type(ident, type_, true)?;
Ok(self)
}
#[instrument(err, level = "trace", skip(self))]
pub fn finish(mut self) -> Result<Types, Error> {
for (id, info) in self.schemas.namespaces() {
let module = Module {
name: info
.prefix
.as_ref()
.map(|prefix| Name::new_named(prefix.to_string())),
namespace: info.namespace.clone(),
};
self.state.types.modules.insert(*id, module);
}
for (_id, schema) in self.schemas.schemas() {
SchemaInterpreter::process(&mut self.state, schema, self.schemas)?;
}
Ok(self.state.types)
}
}