use crate::page::Page;
use crate::parser::parse_page;
use crate::widgets::Widget;
use bevy::asset::io::Reader;
use bevy::asset::{AssetLoader, AsyncReadExt, LoadContext};
use bevy::prelude::TypePath;
use bevy::tasks::ConditionalSendFuture;
use roxmltree::{Document, ParsingOptions};
use rustc_hash::FxHashMap;
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Default, TypePath)]
pub struct PageLoader {
pub(crate) initial_read_capacity: usize,
pub(crate) widgets: FxHashMap<&'static str, Box<dyn Widget>>,
}
impl PageLoader {
#[inline(always)]
pub fn get_widget(&self, name: &str) -> Option<&dyn Widget> {
self.widgets.get(name).map(|w| w.as_ref())
}
}
impl AssetLoader for PageLoader {
type Asset = Page;
type Settings = ();
type Error = PageLoaderError;
fn load(
&self,
reader: &mut dyn Reader,
_: &Self::Settings,
_: &mut LoadContext,
) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>> {
Box::pin(async move {
let mut string = String::with_capacity(self.initial_read_capacity);
reader
.read_to_string(&mut string)
.await
.map_err(PageLoaderError::Io)?;
let doc = Document::parse_with_options(
&string,
ParsingOptions {
allow_dtd: true,
nodes_limit: u32::MAX,
entity_resolver: None,
},
)
.map_err(PageLoaderError::Xml)?;
let page = parse_page(self, doc).map_err(PageLoaderError::Parse)?;
Ok(page)
})
}
fn extensions(&self) -> &[&str] {
&["xml"]
}
}
#[derive(Debug)]
pub enum PageLoaderError {
Io(std::io::Error),
Xml(roxmltree::Error),
Parse(String),
}
impl Display for PageLoaderError {
#[cold]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PageLoaderError::Io(e) => write!(f, "IO Error: {e}"),
PageLoaderError::Xml(e) => write!(f, "XML Error: {e}"),
PageLoaderError::Parse(e) => write!(f, "Parse Error: {e}"),
}
}
}
impl Error for PageLoaderError {}