use syn::spanned::Spanned;
#[derive(Debug, Clone, Copy)]
pub enum LiteralValue {
Float(f64),
Int(i64),
}
impl LiteralValue {
pub fn as_f64(self) -> f64 {
match self {
LiteralValue::Float(f) => f,
LiteralValue::Int(i) => i as f64,
}
}
}
pub fn eval_literal_expr(expr: &syn::Expr) -> syn::Result<LiteralValue> {
match expr {
syn::Expr::Lit(lit) => eval_lit(&lit.lit),
syn::Expr::Unary(unary) if matches!(unary.op, syn::UnOp::Neg(_)) => {
let inner = eval_literal_expr(&unary.expr)?;
Ok(match inner {
LiteralValue::Float(f) => LiteralValue::Float(-f),
LiteralValue::Int(i) => LiteralValue::Int(-i),
})
}
syn::Expr::Paren(paren) => eval_literal_expr(&paren.expr),
_ => Err(syn::Error::new(
expr.span(),
"expected numeric literal (e.g., `60.0`, `-24`)",
)),
}
}
fn eval_lit(lit: &syn::Lit) -> syn::Result<LiteralValue> {
match lit {
syn::Lit::Float(f) => {
let value: f64 = f.base10_parse().map_err(|e| {
syn::Error::new(f.span(), format!("invalid float literal: {}", e))
})?;
Ok(LiteralValue::Float(value))
}
syn::Lit::Int(i) => {
let value: i64 = i.base10_parse().map_err(|e| {
syn::Error::new(i.span(), format!("invalid integer literal: {}", e))
})?;
Ok(LiteralValue::Int(value))
}
_ => Err(syn::Error::new(
lit.span(),
"expected float or integer literal",
)),
}
}
pub fn eval_float_range(
start: &syn::Expr,
end: &syn::Expr,
) -> syn::Result<(f64, f64)> {
let start_val = eval_literal_expr(start)?;
let end_val = eval_literal_expr(end)?;
Ok((start_val.as_f64(), end_val.as_f64()))
}