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<Vec<(String, String)>, Box<dyn Error>> {
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 result = 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<dyn Error>> {
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<dyn Error>> {
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(())
}