use proc_macro2::TokenStream; use quote::quote; use std::env; use std::fs; use std::io::{Read, Write}; use std::path::Path; enum OcticonKind { Polygon, Path, } fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let project_root = util::find_project_root(Path::new("./")).unwrap(); let mut octicons_impl = String::new(); let data_dir = project_root.as_path().join("build/svg/"); println!("data_dir = {:?}", data_dir); assert!(data_dir.is_dir()); for entry in fs::read_dir(data_dir).unwrap() { let entry = entry.unwrap(); let file_path = entry.path().to_path_buf(); let octicon_name = match file_path.as_path().file_stem() { Some(os_str) => os_str.to_str().unwrap().to_string(), None => panic!(), }; //let octicon_name = file_stem.to_str().unwrap().to_string(); if file_path.is_file() { let mut buffer = String::new(); { let mut file = fs::File::open(file_path).unwrap(); file.read_to_string(&mut buffer).unwrap(); } // Data we are looking for let mut width: Option = None; let mut height: Option = None; let mut kind: Option = None; let mut data: Option = None; let mut view_box: Option = None; // We expect format or eprintln!("data {:?}, {:?}", octicon_name, buffer); // The parser doesn't check an input encoding, assuming that it's UTF-8. // TODO evaluate it by yourself or you will get `Error::Utf8Error`. let mut p = xmlparser::Tokenizer::from(buffer.as_str()); for token in &mut p { match token.unwrap() { xmlparser::Token::ElementStart { local, .. } if local.as_str() == "svg" => {} xmlparser::Token::ElementStart { local, .. } if local.as_str() == "path" => { kind = Some(OcticonKind::Path); } xmlparser::Token::ElementStart { local, .. } if local.as_str() == "polygon" => { kind = Some(OcticonKind::Polygon); } xmlparser::Token::ElementStart { local, .. } => { // I only expect to encounter , , and elements in the // design files panic!( "Unexpected svg element <{:?}> found in {:?}", local.as_str(), entry.path() ); } xmlparser::Token::Attribute { local, value, .. } => { // svg::Token::Attribute( // QName { // local: Name::Svg(aid), // .. // }, // value, // ) => { match (local.as_str(), &kind) { ("d", &Some(OcticonKind::Path)) | ("oints", &Some(OcticonKind::Polygon)) => { data = Some(value.as_str().to_owned()) } ("width", _) => { width = Some(value.as_str().parse::().unwrap()); } ("height", _) => { height = Some(value.as_str().parse::().unwrap()); } ("viewBox", _) => { view_box = Some(value.as_str().to_owned()); } _ => { // ignore the attribute } } } _ => {} } } let width = width.expect("width attribute"); let height = height.expect("height attribute"); let data = data.expect("points or d"); let view_box = view_box.expect("viewBox attribute"); let ident_name: String = octicon_name.replace('-', "_").to_uppercase(); let octicon_macro_impl: TokenStream = match kind.expect("path or polygon") { OcticonKind::Path => { let path_static_ident = quote::format_ident!("{}_PATH", ident_name); let static_octicon_ident = quote::format_ident!("{}", ident_name); quote! { generate_path_octicon!( #static_octicon_ident, #octicon_name, #view_box, #width, #height, #path_static_ident => #data ); } } OcticonKind::Polygon => { let points_static_ident = quote::format_ident!("{}_PATH", ident_name); let static_octicon_ident = quote::format_ident!("{}", ident_name); quote! { generate_polygon_octicon!( #static_octicon_ident, #octicon_name, #view_box, #width, #height, #points_static_ident => #data, ); } } }; octicons_impl.push_str(&octicon_macro_impl.to_string()); } } // Generate the macro impl file let mut file = fs::File::create(Path::new(&out_dir).join("octicons_macro_instantiations.rs")).unwrap(); file.write_all(octicons_impl.to_string().as_bytes()) .unwrap(); } mod util { use std::fs::{canonicalize, metadata}; use std::io::{self, Error, ErrorKind}; use std::path::{Path, PathBuf}; /// Look for the cargo.toml file that we are building to determine the root directory of the /// project (so that we can locate the source files) pub fn find_project_root(search: &Path) -> io::Result { if !search.is_dir() { return Err(Error::new( ErrorKind::InvalidInput, format!("`{:?}` is not a directory", search), )); } let mut path_buf = canonicalize(search)?; let error: io::Error = Error::new( ErrorKind::NotFound, format!( "could not find `Cargo.toml` in `{:?}` or any parent directory", path_buf ), ); loop { let test_file = path_buf.as_path().join("Cargo.toml"); match metadata(test_file) { Ok(_) => return Ok(path_buf), Err(_) => (), } match path_buf.as_path().parent() { Some(_) => (), None => return Err(error), } path_buf.pop(); } } }