Add variant tags

feat/template
Shad Amethyst 2 years ago
parent f1fcfdd068
commit 05a5fd91f0
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -1,2 +1,25 @@
# Add options in here as needs be # Add options in here as needs be
name = "blobfox" name = "blobfox"
[variants]
base = ["body-basic", "eyes-basic", "mouth-w"]
happy = ["body-basic", "eyes-happy", "mouth-w"]
evil = ["body-basic", "eyes-evil", "mouth-w"]
owo = ["body-basic", "ear-owo", "eyes-happy", "mouth-w"]
"3c" = ["body-basic", "eyes-basic", "mouth-w", "hand-3c", "left-hand"]
"3c_evil" = ["body-basic", "eyes-evil", "mouth-w", "hand-3c", "left-hand"]
boop = ["body-basic", "boop", "eyes-basic", "mouth-w"]
boop_aww = ["body-basic", "boop", "eyes-aww", "mouth-w"]
boop_owo = ["body-basic", "ear-owo", "boop", "eyes-owo", "mouth-w"]
reach = ["body-basic", "eyes-basic", "mouth-w", "hands-reach", "left-hand", "right-hand"]
reach_aww = ["body-basic", "eyes-aww", "mouth-w", "hands-reach", "left-hand", "right-hand"]
snug = ["body-snug", "eyes-happy", "tail"]
snug_aww = ["body-snug", "eyes-aww", "tail"]
snug_owo = ["body-snug", "ear-owo", "eyes-owo", "tail"]
snug_boop_owo = ["body-snug", "ear-owo", "eyes-owo", "tail", "boop"]
stabby = ["body-basic", "prop", "eyes-evil", "mouth-w", "hand-3c", "left-hand"]

@ -20,7 +20,7 @@ fn main() {
let output_dir = args.output_dir.clone().unwrap_or(PathBuf::from("output/")); let output_dir = args.output_dir.clone().unwrap_or(PathBuf::from("output/"));
if args.names.is_empty() { if args.names.is_empty() {
for name in context.species().variants.keys() { for name in context.species().variant_paths.keys() {
generate_variant(&context, name, &output_dir, &args); generate_variant(&context, name, &output_dir, &args);
} }
} else { } else {
@ -31,9 +31,9 @@ fn main() {
} }
fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf, args: &Args) { fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf, args: &Args) {
if let Some(path) = context.species().variants.get(name) { if let Some(path) = context.species().variant_paths.get(name) {
match context.compile(path).and_then(|template| { match context.compile(path).and_then(|template| {
template.render_data_to_string(&context.get_data()) template.render_data_to_string(&context.get_data(name))
}) { }) {
Ok(svg) => { Ok(svg) => {
match export( match export(

@ -31,14 +31,17 @@ pub struct SpeciesDecl {
/// The name of the species /// The name of the species
pub name: String, pub name: String,
#[serde(default)]
pub variants: HashMap<String, Vec<String>>,
#[serde(skip)] #[serde(skip)]
pub templates: HashMap<String, PathBuf>, pub template_paths: HashMap<String, PathBuf>,
#[serde(skip)] #[serde(skip)]
pub variants: HashMap<String, PathBuf>, pub variant_paths: HashMap<String, PathBuf>,
#[serde(skip)] #[serde(skip)]
pub assets: HashMap<String, PathBuf>, pub asset_paths: HashMap<String, PathBuf>,
} }
/// Loads the given file as an XML tree /// Loads the given file as an XML tree
@ -63,25 +66,25 @@ pub fn load_species(path: impl AsRef<Path>) -> Result<SpeciesDecl, ParseError> {
let path = path.as_ref().to_path_buf().join(base); let path = path.as_ref().to_path_buf().join(base);
let base = load_species(path)?; let base = load_species(path)?;
res.templates = base.templates; res.template_paths = base.template_paths;
res.variants = base.variants; res.variant_paths = base.variant_paths;
res.assets = base.assets; res.asset_paths = base.asset_paths;
} }
// Read the `templates` directory and populate the `templates` field; // Read the `templates` directory and populate the `template_paths` field;
// on error, ignore the directory. // on error, ignore the directory.
for (name, path) in read_dir_xml(path.as_ref().join("templates")) { for (name, path) in read_dir_xml(path.as_ref().join("templates")) {
res.templates.insert(name, path); res.template_paths.insert(name, path);
} }
// Read the `variants` directory // Read the `variants` directory
for (name, path) in read_dir_xml(path.as_ref().join("variants")) { for (name, path) in read_dir_xml(path.as_ref().join("variants")) {
res.variants.insert(name, path); res.variant_paths.insert(name, path);
} }
// Read the `assets` directory // Read the `assets` directory
for (name, path) in read_dir_xml(path.as_ref().join("assets")) { for (name, path) in read_dir_xml(path.as_ref().join("assets")) {
res.assets.insert(name, path); res.asset_paths.insert(name, path);
} }
Ok(res) Ok(res)

@ -1,15 +1,9 @@
use mustache::{
Context,
PartialLoader,
Template,
MapBuilder,
Data,
};
use super::*; use super::*;
use mustache::{Context, Data, MapBuilder, PartialLoader, Template};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use xmltree::{XMLNode, Element}; use xmltree::{Element, XMLNode};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RenderingContext { pub struct RenderingContext {
@ -34,17 +28,21 @@ impl RenderingContext {
Context::with_loader(self.clone()).compile(template.chars()) Context::with_loader(self.clone()).compile(template.chars())
} }
fn render_to_string(&self, string: &str) -> Result<String, mustache::Error> { fn render_to_string(
&self,
string: &str,
variant_name: &str,
) -> Result<String, mustache::Error> {
Context::with_loader(self.clone()) Context::with_loader(self.clone())
.compile(string.chars())? .compile(string.chars())?
.render_data_to_string(&self.get_data()) .render_data_to_string(&self.get_data(variant_name))
} }
pub fn get_data(&self) -> Data { pub fn get_data(&self, variant_name: &str) -> Data {
let mut builder = MapBuilder::new(); let mut builder = MapBuilder::new();
builder = builder.insert_map("variant", |mut builder| { builder = builder.insert_map("variant", |mut builder| {
for variant_name in self.species.variants.keys() { for variant_name in self.species.variant_paths.keys() {
let this = self.clone(); let this = self.clone();
let variant_name = variant_name.to_string(); let variant_name = variant_name.to_string();
builder = builder.insert_fn(variant_name.clone(), move |selector| { builder = builder.insert_fn(variant_name.clone(), move |selector| {
@ -52,7 +50,7 @@ impl RenderingContext {
if let Some(svg) = svg { if let Some(svg) = svg {
if let Some(element) = query_selector(svg, &selector) { if let Some(element) = query_selector(svg, &selector) {
if let Some(string) = xml_to_string(element) { if let Some(string) = xml_to_string(element) {
return string return string;
} }
} }
} }
@ -63,7 +61,7 @@ impl RenderingContext {
builder builder
}); });
for asset_name in self.species.assets.keys() { for asset_name in self.species.asset_paths.keys() {
let this = self.clone(); let this = self.clone();
let asset_name = asset_name.to_string(); let asset_name = asset_name.to_string();
@ -72,7 +70,7 @@ impl RenderingContext {
if let Some(svg) = svg { if let Some(svg) = svg {
if let Some(element) = query_selector(svg, &selector) { if let Some(element) = query_selector(svg, &selector) {
if let Some(string) = xml_to_string(element) { if let Some(string) = xml_to_string(element) {
return string return string;
} }
} }
} }
@ -82,21 +80,24 @@ impl RenderingContext {
} }
let this = self.clone(); let this = self.clone();
let variant_name_owned = variant_name.to_string();
builder = builder.insert_fn("set-fill", move |input| { builder = builder.insert_fn("set-fill", move |input| {
// Parse `color|xml` // Parse `color|xml`
if let [color, xml] = input.splitn(2, '|').collect::<Vec<_>>()[..] { if let [color, xml] = input.splitn(2, '|').collect::<Vec<_>>()[..] {
// Render `color` and `xml` // Render `color` and `xml`
if let (Ok(color), Ok(xml)) = (this.render_to_string(&color), this.render_to_string(&xml)) { if let (Ok(color), Ok(xml)) = (
this.render_to_string(&color, &variant_name_owned),
this.render_to_string(&xml, &variant_name_owned),
) {
// Convert `xml` to XML // Convert `xml` to XML
match Element::parse(xml.as_bytes()) { match Element::parse(xml.as_bytes()) {
Ok(mut xml) => { Ok(mut xml) => {
// Substitute the fill color // Substitute the fill color
if let Some(style) = xml.attributes.get("style") { if let Some(style) = xml.attributes.get("style") {
xml.attributes.insert("style".to_string(), format!( xml.attributes.insert(
"{};fill: {};", "style".to_string(),
style, format!("{};fill: {};", style, color),
color );
));
} }
if let Some(_fill) = xml.attributes.get("fill") { if let Some(_fill) = xml.attributes.get("fill") {
xml.attributes.insert("fill".to_string(), color); xml.attributes.insert("fill".to_string(), color);
@ -121,6 +122,17 @@ impl RenderingContext {
} }
}); });
// Variant tags
if let Some(tags) = self.species.variants.get(variant_name) {
builder = builder.insert_map("tags", move |mut builder| {
for tag in tags.iter() {
builder = builder.insert_bool(tag, true);
}
builder
});
}
builder.build() builder.build()
} }
@ -128,14 +140,17 @@ impl RenderingContext {
let rendered = self.rendered_variants.lock().unwrap().get(name).cloned(); let rendered = self.rendered_variants.lock().unwrap().get(name).cloned();
if let Some(rendered) = rendered { if let Some(rendered) = rendered {
Some(rendered) Some(rendered)
} else if let Some(path) = self.species.variants.get(name) { } else if let Some(path) = self.species.variant_paths.get(name) {
// TODO: log error // TODO: log error
let template = self.compile(path).ok()?; let template = self.compile(path).ok()?;
let data = self.get_data(); let data = self.get_data(name);
let rendered = template.render_data_to_string(&data).ok()?; let rendered = template.render_data_to_string(&data).ok()?;
let parsed = Element::parse(rendered.as_bytes()).ok()?; let parsed = Element::parse(rendered.as_bytes()).ok()?;
self.rendered_variants.lock().unwrap().insert(name.clone(), parsed.clone()); self.rendered_variants
.lock()
.unwrap()
.insert(name.clone(), parsed.clone());
Some(parsed) Some(parsed)
} else { } else {
@ -147,10 +162,13 @@ impl RenderingContext {
let loaded = self.loaded_assets.lock().unwrap().get(name).cloned(); let loaded = self.loaded_assets.lock().unwrap().get(name).cloned();
if let Some(loaded) = loaded { if let Some(loaded) = loaded {
Some(loaded) Some(loaded)
} else if let Some(path) = self.species.assets.get(name) { } else if let Some(path) = self.species.asset_paths.get(name) {
let string = std::fs::read_to_string(path).ok()?; let string = std::fs::read_to_string(path).ok()?;
let parsed = Element::parse(string.as_bytes()).ok()?; let parsed = Element::parse(string.as_bytes()).ok()?;
self.loaded_assets.lock().unwrap().insert(name.clone(), parsed.clone()); self.loaded_assets
.lock()
.unwrap()
.insert(name.clone(), parsed.clone());
Some(parsed) Some(parsed)
} else { } else {
@ -167,7 +185,7 @@ impl PartialLoader for RenderingContext {
fn load(&self, name: impl AsRef<Path>) -> Result<String, mustache::Error> { fn load(&self, name: impl AsRef<Path>) -> Result<String, mustache::Error> {
let name = name.as_ref().to_str().ok_or(mustache::Error::InvalidStr)?; let name = name.as_ref().to_str().ok_or(mustache::Error::InvalidStr)?;
if let Some(path) = self.species.templates.get(name) { if let Some(path) = self.species.template_paths.get(name) {
Ok(std::fs::read_to_string(path)?) Ok(std::fs::read_to_string(path)?)
} else { } else {
eprintln!("No template named {}", name); eprintln!("No template named {}", name);
@ -184,7 +202,12 @@ pub fn query_selector(svg: Element, pattern: &str) -> Option<Element> {
for child in svg.children { for child in svg.children {
if let XMLNode::Element(child) = child { if let XMLNode::Element(child) = child {
if let ("#", pattern_id) = pattern.split_at(1) { if let ("#", pattern_id) = pattern.split_at(1) {
if child.attributes.get("id").map(|id| id == pattern_id).unwrap_or(false) { if child
.attributes
.get("id")
.map(|id| id == pattern_id)
.unwrap_or(false)
{
return Some(child); return Some(child);
} else if child.children.len() > 0 { } else if child.children.len() > 0 {
if let Some(res) = query_selector(child, pattern) { if let Some(res) = query_selector(child, pattern) {

Loading…
Cancel
Save