You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
blobfox/automate-rs/src/emote.rs

118 lines
3.1 KiB

use super::{AssetDecl, Variant};
use xmltree::{XMLNode, Element};
use std::collections::HashMap;
use std::fs::File;
#[derive(Debug, Clone)]
pub struct Emote {
pub assets: HashMap<String, Asset>,
// TODO: metadata (contributors, etc.)
}
#[derive(Debug, Clone)]
pub struct Asset {
element: Element,
}
impl Emote {
pub fn empty() -> Self {
Self {
assets: HashMap::new(),
}
}
pub fn from_decl(variant_decl: Variant, emotes: &HashMap<String, Emote>) -> Option<Self> {
// Load base or src
let mut res = if let Some(src) = variant_decl.src {
let file = File::open(src).ok()?;
let xml = Element::parse(file).ok()?;
Self::from_xml(xml)?
} else if let Some(base) = variant_decl.base {
emotes.get(&base)?.clone()
} else {
Emote::empty()
};
// Load individual assets
for asset_decl in variant_decl.assets {
let name = asset_decl.name.clone();
res.assets.insert(name, Asset::from_decl(asset_decl)?);
}
// Apply overwrites (TODO)
Some(res)
}
/// Loads an emote from an svg file.
/// Top-level `<g>` elements are ignored
pub fn from_xml(root: Element) -> Option<Self> {
let iter = root.children.into_iter()
.map(|elem| -> Box<dyn Iterator<Item=XMLNode>> {
if let XMLNode::Element(elem2) = elem {
if elem2.name == "g" {
Box::new(elem2.children.into_iter())
} else {
Box::new(Some(XMLNode::Element(elem2)).into_iter())
}
} else {
Box::new(None.into_iter())
}
})
.flatten()
.filter_map(|elem| {
match elem {
XMLNode::Element(elem) => Some(elem),
_ => None
}
})
.filter(filter_drawable);
let mut assets = HashMap::new();
for elem in iter {
if let Some(name) = elem.attributes.get("label").or(elem.attributes.get("id")).cloned() {
let asset = Asset::new(elem);
assets.insert(name, asset);
}
}
Some(Self {
assets
})
}
}
impl Asset {
pub fn new(element: Element) -> Self {
Self {
element
}
}
// TODO: allow loading assets from other variants (requires a more thorough dependency management)
// TODO: allow searching for a specific element
pub fn from_decl(declaration: AssetDecl) -> Option<Self> {
let mut element = if let Some(src) = declaration.src {
let file = File::open(src).ok()?;
Element::parse(file).ok()?
} else {
unimplemented!()
};
Some(Self {
element
})
}
}
// NOTE: This isn't really viable,
fn filter_drawable(elem: &Element) -> bool {
match elem.name.as_str() {
"title" | "sodipodi:namedview" | "image" | "metadata" => false,
_ => true,
}
}