xmelt 0.2.0

A serialization/deserialization framework for XML
Documentation
#![allow(non_camel_case_types)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(unused_imports)]

#![allow(private_bounds)]
#![allow(private_interfaces)]

#[cfg(feature = "xmelt_derive")]
#[doc(hidden)]
pub use xmelt_derive::*;

mod qname; pub use qname::*;
mod context; pub use context::*;
mod item; pub use item::*;
mod container; pub use container::*;
mod from_value; pub use from_value::*;
mod freeze; pub use freeze::*;

#[cfg(feature = "xmelt_derive")]
extern crate self as xmelt;

#[derive(Debug)]
pub enum Error {
	MissingValue(String),
	DuplicateValue(String),
	UnexpectedValue(String),
	ParseError(String),
	Message(String),
	InvalidVariant(String),
	InvalidVariants(String, Vec<Error>),
}

impl From<String> for Error {
	fn from(a: String) -> Self {
		Self::Message(a)
	}
}

impl From<&str> for Error {
	fn from(a: &str) -> Self {
		Self::Message(a.to_owned())
	}
}

#[derive(Debug, Clone, Copy)]
pub enum Value<'a> {
	Text(&'a str),
	Node(roxmltree::Node<'a, 'a>),
}

impl<'a> Value<'a> {
	pub fn as_node(&self) -> Option<roxmltree::Node<'a, 'a>> {
		if let Self::Node(value) = self {
			if let roxmltree::NodeType::Element = value.node_type() {
				Some(*value)
			} else {
				None
			}
		} else {
			None
		}
	}

	pub fn as_text(&self) -> Option<&'a str> {
		match self {
			Self::Text(text) => Some(text),
			Self::Node(node) => {
				if let roxmltree::NodeType::Text = node.node_type() {
					node.text()
				} else {
					None
				}
			},
		}
	}

	pub fn normalise(&mut self) {
		if let Self::Node(node) = self {
			if let roxmltree::NodeType::Text = node.node_type() {
				let mut text_value = Self::Text(node.text().unwrap());
				std::mem::swap(self, &mut text_value);
			}
		}
	}
}

impl<'a> From<&'a str> for Value<'a> {
	fn from(text: &'a str) -> Self {
		Self::Text(text)
	}
}

impl<'a> From<roxmltree::Node<'a, 'a>> for Value<'a> {
	fn from(node: roxmltree::Node<'a, 'a>) -> Self {
		Self::Node(node)
	}
}

#[derive(Debug)]
pub struct Node {
	attributes: Vec<(String, String)>,
	children: Vec<Any>,
}

impl Node {
	fn new(node: roxmltree::Node<'_, '_>) -> Result<Self, Error> {
		let attributes: Vec<(String, String)> = node
			.attributes()
			.map(|attribute| {
				let name = node.document().input_text()[attribute.range_qname()].to_owned();
				let value = attribute.value().to_owned();
				(name, value)
			})
			.collect();

		let children: Vec<Any> = node
			.children()
			.map(|child| {
				match child.node_type() {
					roxmltree::NodeType::Element => Ok(Any::Node(Node::new(child)?)),
					roxmltree::NodeType::Text => Ok(Any::Text(child.text().ok_or_else(|| "Text node has to contain text.")?.to_owned())),
					_ => Err(Error::Message(format!("Node of type `{:?}` cannot be made into `xmelt::Node`.", child.node_type())))
				}
			})
			.collect::<Result<_, Error>>()?;

		Ok(Self {
			attributes,
			children,
		})
	}
}

impl<'freeze> Freeze<'freeze> for Node {

	type Kind = ComplexType;

	fn de<I>(value: I, restrictions: &Vec<DynRestriction<'freeze, Self>>, context: Context) -> Result<Self, Error>
		where
			Self: Sized + 'freeze,
			I: Into<Value<'freeze>>
	{

		let node = value.into().as_node().ok_or_else(|| Error::Message(format!("Struct `xmelt::Node` can only be built from a node in {}.", context.path())))?;

		Node::new(node)
	}
}

#[derive(Debug)]
pub enum Any {
	Node(Node),
	Text(String),
}

impl<'freeze> Freeze<'freeze> for Any {

	type Kind = ComplexType;

	fn de<I>(value: I, restrictions: &Vec<DynRestriction<'freeze, Self>>, context: Context) -> Result<Self, Error>
		where
			Self: Sized + 'freeze,
			I: Into<Value<'freeze>>
	{
		let mut value = value.into();
		value.normalise();

		let any = match value {
			Value::Node(node) => Any::Node(Node::new(node)?),
			Value::Text(text) => Any::Text(text.to_owned()),
		};

		Ok(any)
	}
}

#[derive(Debug)]
pub struct List<T> {
	storage: Vec<T>
}

impl<'freeze, T> Freeze<'freeze> for List<T>
	where T: 'freeze + Freeze<'freeze>
{	
	type Kind = <T as Freeze<'freeze>>::Kind;

	fn de<I>(value: I, restrictions: &Vec<DynRestriction<'freeze, T>>, context: Context) -> Result<Self, Error>
		where
			Self: Sized + 'freeze,
			I: Into<Value<'freeze>>
	{
		let text = value.into().as_text().ok_or_else(|| Error::Message(format!("Struct `List` can only be built from text, instead got node.")))?;

		let list = text
			.split(" ")
			.map(|p| {
				Freeze::de(Value::Text(p), restrictions, context.clone())
			})
			.collect::<Result<_, _>>()?;

		Ok(Self {
			storage: list
		})
	}
}

pub trait Builder<'builder> {
	type Output;
	const TYPE_NAME: &'static str;

	fn new() -> Self;

	fn visit_attribute<'qname, IQ: Into<QName<'qname>>>(&mut self, qname: IQ, value: &'builder str, context: Context) -> Result<(), crate::Error>;

	fn visit_element<'qname, IQ: Into<QName<'qname>>, IV: Into<Value<'builder>>>(&mut self, qname: IQ, value: IV, context: Context) -> Result<(), crate::Error>;
	
	fn visit_text<IV: Into<Value<'builder>>>(&mut self, value: IV, context: Context) -> Result<(), crate::Error>;

	fn build(self, context: Context) -> Result<Self::Output, crate::Error>;

	fn accept_element<'qname, IQ: Into<QName<'qname>>>(qname: IQ) -> bool;

	fn accept_attribute<'qname, IQ: Into<QName<'qname>>>(qname: IQ) -> bool;

	fn accept_text() -> bool;
}

#[derive(Debug)]
#[derive(Freeze)]
#[cfg(feature = "xmelt_derive")]
#[xmelt(namespaces = ["xsi", "http://www.w3.org/2001/XMLSchema-instance"])]
// #[xmelt(print)]
pub struct DocumentAttributes {
	#[xmelt(attribute, prefix = "xsi", rename = "schemaLocation")]
	schema_location: Option<String>,
}

pub trait Restriction {}
pub trait Storage<'c> {
	type Inner;
}

macro_rules! xsd_type {
	($type:ident, $inner:ty) => {
		pub struct $type;

		impl Restriction for $type {}

		impl<'c> Storage<'c> for $type {
			type Inner = $inner;
		}
	};
	(ref, $type:ident, $inner:ty) => {
		pub struct $type;

		impl Restriction for $type {}

		impl<'c> Storage<'c> for $type {
			type Inner = &'c $inner;
		}
	};
}

xsd_type!(ref, TextType, str);
xsd_type!(NumericType, f64);
xsd_type!(SimpleType, ());
xsd_type!(ComplexType, ());

#[derive(Debug)]
pub struct Pattern {
	pattern: String,
	regex: regexml::Regex,
}

impl Pattern {
	pub fn new(pattern: &str) -> Self {
		Self {
			regex: regexml::Regex::xsd(pattern, "").unwrap(),
			pattern: pattern.to_owned(),
		}
	}
}

pub trait Restriction2<T: for<'s> Storage<'s>> {
	fn validate<'s>(&self, item: <T as Storage<'s>>::Inner) -> Result<(), String>;
}

impl Restriction2<TextType> for Pattern {
	fn validate<'s>(&self, item: <TextType as Storage<'s>>::Inner) -> Result<(), String> {

		// println!("Checking pattern `{}` against text `{}`.", self.pattern, item);

		if self.regex.is_match(&item) {
			Ok(())
		} else {
			Err(format!("Unable to match pattern `{}` to text `{}`.", self.pattern, item))
		}
	}
}