#![deny(unsafe_code)]
#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications
)]
#[macro_use]
mod macros;
mod api;
mod core;
mod fx;
mod physics;
mod url;
use std::convert::{TryFrom, TryInto};
use std::fmt::{Debug, Display};
use std::io::{BufRead, BufReader, Write};
use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;
pub use crate::{api::*, core::*, fx::*, physics::*};
use minidom::quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
pub use minidom::Element;
pub use url::Url;
type XReader<R> = minidom::quick_xml::Reader<R>;
type XWriter<R> = minidom::quick_xml::Writer<R>;
#[derive(Debug)]
pub enum Error {
Minidom(minidom::Error),
Other(&'static str),
Str(String),
}
impl From<std::io::Error> for Error {
fn from(v: std::io::Error) -> Self {
Self::Minidom(v.into())
}
}
impl From<minidom::Error> for Error {
fn from(v: minidom::Error) -> Self {
Self::Minidom(v)
}
}
impl From<minidom::quick_xml::Error> for Error {
fn from(v: minidom::quick_xml::Error) -> Self {
Self::Minidom(v.into())
}
}
impl From<&'static str> for Error {
fn from(v: &'static str) -> Self {
Self::Other(v)
}
}
impl From<String> for Error {
fn from(v: String) -> Self {
Self::Str(v)
}
}
type Result<T, E = Error> = std::result::Result<T, E>;
type ElementIter<'a> = std::iter::Peekable<minidom::Children<'a>>;
fn get_text(element: &Element) -> Option<&str> {
let mut it = element.nodes();
let text = match it.next() {
None => "",
Some(s) => s.as_text()?,
};
if it.next().is_some() {
return None;
}
Some(text)
}
fn parse_text(element: &Element) -> Result<String> {
Ok(get_text(element).ok_or("expecting a text node")?.to_owned())
}
fn parse_array<T: FromStr>(e: &Element) -> Result<Box<[T]>> {
get_text(e)
.ok_or("expected text node")?
.split_ascii_whitespace()
.map(|s| s.parse())
.collect::<Result<_, _>>()
.map_err(|_| "parse error".into())
}
fn parse_array_n<T: FromStr, const N: usize>(element: &Element) -> Result<Box<[T; N]>> {
Ok(parse_array(element)?
.try_into()
.map_err(|_| "unexpected number of elements")?)
}
fn parse_elem<T: FromStr>(e: &Element) -> Result<T> {
get_text(e)
.ok_or("expected text node")?
.parse()
.map_err(|_| "parse error".into())
}
fn parse_attr<T: FromStr>(attr: Option<&str>) -> Result<Option<T>> {
Ok(match attr {
None => None,
Some(s) => Some(s.parse().map_err(|_| "parse failure")?),
})
}
fn parse_one<'a, T>(
name: &str,
it: &mut impl Iterator<Item = &'a Element>,
f: impl FnOnce(&'a Element) -> Result<T>,
) -> Result<T> {
let e = it.next().ok_or_else(|| format!("expected <{}>", name))?;
if e.name() != name {
return Err(format!("expected <{}>", name).into());
}
f(e)
}
fn parse_one_many<'a, T>(
it: &mut impl Iterator<Item = &'a Element>,
f: impl FnOnce(&'a Element) -> Result<Option<T>>,
) -> Result<T> {
let e = it.next().ok_or("expected element")?;
Ok(f(e)?.ok_or("expected element")?)
}
fn parse_opt<'a, T>(
name: &str,
it: &mut ElementIter<'a>,
f: impl FnOnce(&'a Element) -> Result<T>,
) -> Result<Option<T>> {
let mut res = None;
if let Some(&e) = it.peek() {
if e.name() == name {
res = Some(f(e)?);
it.next();
}
}
Ok(res)
}
fn parse_opt_many<'a, T>(
it: &mut ElementIter<'a>,
f: impl FnOnce(&'a Element) -> Result<Option<T>>,
) -> Result<Option<T>> {
let res = match it.peek() {
None => None,
Some(&e) => f(e)?,
};
if res.is_some() {
it.next();
}
Ok(res)
}
fn parse_list<'a, T>(
name: &str,
it: &mut ElementIter<'a>,
mut f: impl FnMut(&'a Element) -> Result<T>,
) -> Result<Vec<T>> {
parse_list_many(it, |e| {
Ok(if e.name() == name { Some(f(e)?) } else { None })
})
}
fn finish<'a, T>(t: T, mut it: impl Iterator<Item = &'a Element>) -> Result<T> {
if let Some(e) = it.next() {
return Err(format!("unexpected node <{}>", e.name()).into());
}
Ok(t)
}
fn parse_list_many<'a, T>(
it: &mut ElementIter<'a>,
mut f: impl FnMut(&'a Element) -> Result<Option<T>>,
) -> Result<Vec<T>> {
let mut res = vec![];
while let Some(&e) = it.peek() {
match f(e)? {
Some(t) => res.push(t),
None => break,
}
it.next();
}
Ok(res)
}
fn print_str(s: &str, w: &mut XWriter<impl Write>) -> Result<()> {
Ok(w.write_event(Event::Text(BytesText::new(s)))?)
}
fn print_elem<T: Display>(elem: &T, w: &mut XWriter<impl Write>) -> Result<()> {
print_str(&format!("{}", elem), w)
}
#[inline]
fn opt<'a, T, E>(elem: &'a Option<T>, f: impl FnOnce(&'a T) -> Result<(), E>) -> Result<(), E> {
if let Some(elem) = elem {
f(elem)?
}
Ok(())
}
#[inline]
fn many<'a, T, E>(elem: &'a [T], f: impl FnMut(&'a T) -> Result<(), E>) -> Result<(), E> {
elem.iter().try_for_each(f)
}
fn arr_to_string<T: Display>(elem: &[T]) -> Option<String> {
let (e1, rest) = elem.split_first()?;
let mut s = format!("{}", e1);
for e in rest {
use std::fmt::Write;
write!(s, " {}", e).expect("can't fail")
}
Some(s)
}
fn print_arr<T: Display>(elem: &[T], w: &mut XWriter<impl Write>) -> Result<()> {
opt(&arr_to_string(elem), |s| print_str(s, w))
}
#[doc(hidden)]
#[derive(Debug)]
pub struct ElemBuilder<'a>(BytesStart<'a>);
struct ElemEnd<'a>(BytesEnd<'a>);
impl<'a> ElemBuilder<'a> {
#[inline]
fn new(name: &'a str) -> Self {
Self(BytesStart::new(name))
}
fn print_str(name: &'a str, elem: &str, w: &mut XWriter<impl Write>) -> Result<()> {
let e = Self::new(name).start(w)?;
print_str(elem, w)?;
e.end(w)
}
fn print<T: Display>(name: &'a str, elem: &T, w: &mut XWriter<impl Write>) -> Result<()> {
let e = Self::new(name).start(w)?;
print_elem(elem, w)?;
e.end(w)
}
fn opt_print<T: Display>(
name: &'a str,
elem: &Option<T>,
w: &mut XWriter<impl Write>,
) -> Result<()> {
opt(elem, |e| Self::print(name, e, w))
}
fn def_print<T: Display + PartialEq>(
name: &'a str,
value: T,
def: T,
w: &mut XWriter<impl Write>,
) -> Result<()> {
if value != def {
Self::print(name, &value, w)?
}
Ok(())
}
fn print_arr<T: Display>(name: &'a str, elem: &[T], w: &mut XWriter<impl Write>) -> Result<()> {
let e = Self::new(name).start(w)?;
print_arr(elem, w)?;
e.end(w)
}
#[inline]
fn raw_attr(&mut self, key: &str, value: &[u8]) {
self.0.push_attribute((key.as_bytes(), value));
}
#[inline]
fn attr(&mut self, key: &str, value: &str) {
self.0.push_attribute((key, value));
}
fn opt_attr(&mut self, key: &str, value: &Option<String>) {
if let Some(value) = value {
self.attr(key, value)
}
}
#[inline]
fn print_attr(&mut self, key: &str, value: impl Display) {
self.0.push_attribute((key, &*format!("{}", value)));
}
fn opt_print_attr(&mut self, key: &str, value: &Option<impl Display>) {
if let Some(value) = value {
self.0.push_attribute((key, &*format!("{}", value)));
}
}
fn def_print_attr<T: Display + PartialEq>(&mut self, key: &str, value: T, def: T) {
if value != def {
self.print_attr(key, value)
}
}
fn start(self, w: &mut XWriter<impl Write>) -> Result<ElemEnd<'static>> {
let end = self.0.to_end().into_owned();
w.write_event(Event::Start(self.0))?;
Ok(ElemEnd(end))
}
fn end(self, w: &mut XWriter<impl Write>) -> Result<()> {
Ok(w.write_event(Event::Empty(self.0))?)
}
}
impl<'a> ElemEnd<'a> {
fn end(self, w: &mut XWriter<impl Write>) -> Result<()> {
Ok(w.write_event(Event::End(self.0))?)
}
}
use private::{XNode, XNodeWrite};
pub(crate) mod private {
use super::*;
pub trait XNode: XNodeWrite + Sized {
const NAME: &'static str;
fn parse(element: &Element) -> Result<Self>;
fn parse_box(element: &Element) -> Result<Box<Self>> {
Self::parse(element).map(Box::new)
}
fn parse_one<'a>(it: &mut impl Iterator<Item = &'a Element>) -> Result<Self> {
parse_one(Self::NAME, it, Self::parse)
}
fn parse_opt(it: &mut ElementIter<'_>) -> Result<Option<Self>> {
parse_opt(Self::NAME, it, Self::parse)
}
fn parse_opt_box(it: &mut ElementIter<'_>) -> Result<Option<Box<Self>>> {
parse_opt(Self::NAME, it, Self::parse_box)
}
fn parse_list(it: &mut ElementIter<'_>) -> Result<Vec<Self>> {
parse_list(Self::NAME, it, Self::parse)
}
fn parse_list_n<const N: usize>(it: &mut ElementIter<'_>) -> Result<Vec<Self>> {
let arr = parse_list(Self::NAME, it, Self::parse)?;
if arr.len() < N {
return Err(format!("parse error: expected {} {} elements", N, Self::NAME).into());
}
Ok(arr)
}
#[doc(hidden)]
fn elem<'a>() -> ElemBuilder<'a> {
ElemBuilder::new(Self::NAME)
}
}
pub trait XNodeWrite {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()>;
}
}
impl<T: XNodeWrite> XNodeWrite for Box<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
(**self).write_to(w)
}
}
impl<T: XNodeWrite> XNodeWrite for Option<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
opt(self, |e| e.write_to(w))
}
}
impl<T: XNodeWrite> XNodeWrite for Vec<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
many(self, |e| e.write_to(w))
}
}
impl XNodeWrite for Element {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
use std::{cell::RefCell, collections::BTreeMap};
thread_local! {
static COLLADA_PREFIX: RefCell<BTreeMap<Option<String>, String>> =
const { RefCell::new(BTreeMap::new()) };
}
COLLADA_PREFIX.with(|pfxs| {
let mut pfxs = pfxs.borrow_mut();
if pfxs.is_empty() {
pfxs.insert(None, "http://www.collada.org/2005/11/COLLADASchema".into());
}
Ok(self.write_to_inner(w, &mut pfxs)?)
})
}
}
impl XNodeWrite for () {
fn write_to<W: Write>(&self, _: &mut XWriter<W>) -> Result<()> {
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Document {
pub asset: Asset,
pub library: Vec<LibraryElement>,
pub scene: Option<Scene>,
pub extra: Vec<Extra>,
}
impl FromStr for Document {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::try_from(s.as_bytes())
}
}
impl TryFrom<&str> for Document {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<&[u8]> for Document {
type Error = Error;
fn try_from(s: &[u8]) -> Result<Self> {
Self::from_reader(std::io::Cursor::new(s))
}
}
impl Document {
pub fn new(asset: Asset) -> Self {
Self {
asset,
library: vec![],
scene: None,
extra: vec![],
}
}
pub fn create_now() -> Self {
Self::new(Asset::create_now())
}
pub fn push_library<T: ParseLibrary>(&mut self, items: Vec<T>) {
self.library.push(T::mk_element(Library::new(items)))
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
Self::from_reader(BufReader::new(std::fs::File::open(path)?))
}
pub fn from_reader<R: BufRead>(reader: R) -> Result<Self> {
Self::from_xml_reader(&mut XReader::from_reader(reader))
}
pub fn from_xml_reader<R: BufRead>(reader: &mut XReader<R>) -> Result<Self> {
let root = Element::from_reader(reader)?;
Self::parse(&root)
}
pub fn write_to<W: Write>(&self, w: W) -> Result<()> {
XNodeWrite::write_to(self, &mut XWriter::new_with_indent(w, b' ', 2))
}
}
impl XNode for Document {
const NAME: &'static str = "COLLADA";
fn parse(element: &Element) -> Result<Self> {
if element.name() != Self::NAME {
return Err("Expected COLLADA root node".into());
}
if element.attr("version") != Some("1.4.1") {
return Err("Unsupported COLLADA version".into());
}
let mut it = element.children().peekable();
Ok(Document {
asset: Asset::parse_one(&mut it)?,
library: parse_list_many(&mut it, LibraryElement::parse)?,
scene: Scene::parse_opt(&mut it)?,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Document {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
w.write_event(Event::Decl(BytesDecl::new("1.0", Some("utf-8"), None)))?;
let mut e = Self::elem();
e.raw_attr("xmlns", b"http://www.collada.org/2005/11/COLLADASchema");
e.raw_attr("version", b"1.4.1");
e.raw_attr("xmlns:xsi", b"http://www.w3.org/2001/XMLSchema-instance");
let e = e.start(w)?;
self.asset.write_to(w)?;
self.library.write_to(w)?;
self.scene.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl CollectLocalMaps for Document {
fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
self.library.collect_local_maps(maps);
}
}