use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use anyhow::Error;
use inflections::Inflect;
use crate::svg::{generate_svg_component, parse_svg_content};
pub const MOD_HEADER: &str = r"// Auto Generated! DO NOT EDIT!
use dioxus::prelude::*;
use crate::IconShape;
";
const LIB_HEADER: &str = r"// Auto Generated! DO NOT EDIT!
use dioxus::prelude::*;
pub use dioxus_icon_component::{Icon, IconProps, IconShape};
";
#[must_use]
pub fn map_filename(name: &str, remapping_names: &[&str]) -> String {
if remapping_names.contains(&name) {
return format!("icon-{name}");
}
name.to_string()
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ModuleInfo {
pub module_name: String,
pub node_name: String,
pub module_content: String,
}
pub fn generate_variants_icons(
variant_list: &[&str],
svg_dir: &str,
remapping_names: &[&str],
) -> Result<(), Error> {
for feature in variant_list {
generate_variant_icons(svg_dir, feature, remapping_names)?;
}
let mut module_file = File::create("src/lib.rs")?;
let line = r"// Auto Generated! DO NOT EDIT!
pub use dioxus_icon_component::{Icon, IconProps, IconShape};
";
module_file.write_all(line.as_bytes())?;
for feature in variant_list {
let module_name = to_module_name(feature);
let feature_line = r#"
#[cfg(feature = "{MODULE_NAME}")]
pub mod {MODULE_NAME};
"#
.replace("{MODULE_NAME}", &module_name);
module_file.write_all(feature_line.as_bytes())?;
}
Ok(())
}
pub fn generate_variant_icons(
base_dir: &str,
variant_dirname: &str,
remapping_names: &[&str],
) -> Result<(), Error> {
let mut module_names = Vec::new();
let mut dir = PathBuf::new();
dir.push(base_dir);
dir.push(variant_dirname);
let svg_extension = OsStr::new("svg");
for entry in fs::read_dir(&dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_file() {
continue;
}
if path.extension() != Some(svg_extension) {
eprintln!("Ignore non svg file {}", path.display());
continue;
}
let Some(stem) = path.file_stem() else {
eprintln!("Ignore file without stem: {}", path.display());
continue;
};
let Some(stem_str) = stem.to_str() else {
eprintln!("Ignore file with non-utf-8 name: {}", path.display());
continue;
};
let stem_str = map_filename(stem_str, remapping_names);
let node_name = to_node_name(&stem_str);
let module_name = to_module_name(&stem_str);
let svg_content = fs::read_to_string(&path)?;
if let Some(svg_obj) = parse_svg_content(&svg_content) {
let module_content = generate_svg_component(&node_name, None, &svg_obj);
module_names.push(ModuleInfo {
module_name,
node_name,
module_content,
});
} else {
eprintln!("Failed to parse svg file {}", path.display());
}
}
module_names.sort();
let variant_module_name = to_module_name(variant_dirname);
let mut module_file = File::create(format!("src/{variant_module_name}.rs"))?;
module_file.write_all(MOD_HEADER.as_bytes())?;
for ModuleInfo {
module_name: _module_name,
node_name: _node_name,
module_content,
} in module_names
{
module_file.write_all(module_content.as_bytes())?;
}
Ok(())
}
pub fn generate_icons(base_dir: &str, remapping_names: &[&str]) -> Result<(), Error> {
let mut module_names = Vec::new();
let dir = PathBuf::from(base_dir);
let svg_extension = OsStr::new("svg");
for entry in fs::read_dir(&dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_file() {
continue;
}
if path.extension() != Some(svg_extension) {
eprintln!("Ignore non svg file {}", path.display());
continue;
}
let Some(stem) = path.file_stem() else {
eprintln!("Ignore file without stem: {}", path.display());
continue;
};
let Some(stem_str) = stem.to_str() else {
eprintln!("Ignore file with non-utf-8 name: {}", path.display());
continue;
};
let stem_str = map_filename(stem_str, remapping_names);
let node_name = to_node_name(&stem_str);
let module_name = to_module_name(&stem_str);
let svg_content = fs::read_to_string(&path)?;
if let Some(svg_obj) = parse_svg_content(&svg_content) {
let module_content = generate_svg_component(&node_name, None, &svg_obj);
module_names.push(ModuleInfo {
module_name,
node_name,
module_content,
});
} else {
eprintln!("Failed to parse svg file {}", path.display());
}
}
module_names.sort();
let mut module_file = File::create("src/lib.rs")?;
module_file.write_all(LIB_HEADER.as_bytes())?;
for ModuleInfo {
module_name: _module_name,
node_name: _node_name,
module_content,
} in module_names
{
module_file.write_all(module_content.as_bytes())?;
}
Ok(())
}
#[must_use]
pub fn to_module_name(dir_name: &str) -> String {
dir_name
.replace('&', "")
.replace(" ", " ")
.replace("__", "_")
.to_snake_case()
}
#[must_use]
pub fn to_node_name(dir_name: &str) -> String {
dir_name
.replace('&', "")
.replace(" ", " ")
.replace("__", "_")
.to_pascal_case()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_module_name_games_sports() {
assert_eq!(to_module_name("Games & Sports"), "games_sports");
}
#[test]
fn test_to_node_name_games_sports() {
assert_eq!(to_node_name("Games & Sports"), "GamesSports");
assert_eq!(to_node_name("fork_&_knife"), "ForkKnife");
}
}