use std:: { cmp::Ordering, error::Error, env, fs::File, io::{Write, Read}, }; use physical_quantity::{ predefined::unit::DEFAULT_UNIT_DEF, Dimension, DynDim }; use syn:: { self, Item, ItemConst, Expr, ExprTuple, Type, Lit }; use proc_macro2::TokenTree; use float_pretty_print::PrettyPrintFloat; fn read_default_def(s: &str) -> Result, Box> { let mut f_in = File::open(s)?; let mut content = String::new(); f_in.read_to_string(&mut content)?; let ast = syn::parse_file(&content)?; // let mut f_out = File::create("../target/def.txt")?; let mut result = Vec::new(); // writeln!(&mut f_out, "{ast:?}")?; // Ok(Vec::new()) for item in ast.items.iter() { match item { Item::Const(ItemConst { ty, expr, .. }) => match (ty.as_ref(), expr.as_ref()) { (&Type::Array(_), &Expr::Array(ref expr)) => { for e in expr.elems.iter() { match e { Expr::Tuple(expr) => match extract_comment(expr) { Some(pair) => result.push(pair), _ => (), }, _ => (), } } }, _ => (), }, _ => (), } } result.sort_by(|left, right| left.0.cmp(&right.0)); Ok(result) } fn extract_comment(expr: &ExprTuple) -> Option<(String, String)> { let key = match expr.elems.first()? { Expr::Lit(expr) => match &expr.lit { &Lit::Str(ref name) => name.value(), _ => return None, }, _ => return None, }; let remarks = expr.attrs.iter().fold( String::new(), |mut remarks, attr| match attr.path.segments.first() { Some(path) => if path.ident.to_string() == "doc" { for tt in attr.tokens.clone() { match tt { TokenTree::Literal(lit) => { let line = lit.to_string(); let line = if line.starts_with('"') { &line[1..] } else { &line }; let line = if line.ends_with('"') { &line[..line.len() - 1] } else { &line }; remarks += line; }, _ => continue, } } remarks } else { remarks }, _ => remarks, }); (key, remarks).into() } const BAR: &str = "\u{2014}"; fn float2string(float: f64) -> String { if float == 0.0 { BAR.to_string() } else { format!("{}", PrettyPrintFloat(float)) } } fn dump_default_def(s: &str, remarks: Vec<(String, String)>) -> Result<(), Box> { let mut f_out = File::create(s)?; let mut order = Vec::new(); for (key, (cnv, dim)) in DEFAULT_UNIT_DEF.iter() { order.push((*key, cnv.0.to_f64(), cnv.1.to_f64(), dim.dim_code())); } order.sort_by(|left, right| match left.3.cmp(&right.3) { Ordering::Equal => match left.1.partial_cmp(&right.1) { Some(Ordering::Equal) => match left.2.partial_cmp(&right.2) { Some(Ordering::Equal) => left.0.cmp(&right.0), Some(o) => o, None => Ordering::Equal, }, Some(o) => o, None => Ordering::Equal, }, o => o, }); writeln!(&mut f_out, "| name | a | b | dim | remarks |\n\ |------|---|---|-----|---------|" )?; for (key, a, b, code) in order { let a = float2string(a); let b = float2string(b); let dim = DynDim::new(code); let remark = match remarks.binary_search_by(|(name, _)| name.as_str().cmp(key)) { Ok(idx) => &remarks[idx].1, Err(_) => BAR, }; writeln!(&mut f_out, "| {key} | {a} | {b} | {dim} | {remark} |")?; } Ok(()) } fn main() -> Result<(), Box> { let mut args = env::args(); println!("starts: {}", args.next().unwrap()); let remarks = read_default_def(&args.next().unwrap())?; dump_default_def(&args.next().unwrap(), remarks)?; Ok(()) }