use crate::Options;
use crate::resolve::{AttrMap, ResolvedCall, ResolvedValue, VarTable};
pub fn format_value(value: &ResolvedValue, vars: &VarTable, opts: &Options) -> String {
match value {
ResolvedValue::Number(n) => num(*n),
ResolvedValue::Percent(n) => format!("{}%", num(*n)),
ResolvedValue::String(s) => format!("\"{}\"", s),
ResolvedValue::Hex(h) => format!("#{}", h),
ResolvedValue::Ident(s) => s.clone(),
ResolvedValue::RawCss(s) => s.clone(),
ResolvedValue::Tuple(items) => {
let parts: Vec<String> = items.iter().map(|v| format_value(v, vars, opts)).collect();
parts.join(" ")
}
ResolvedValue::List(items) => {
let parts: Vec<String> = items.iter().map(|v| format_value(v, vars, opts)).collect();
parts.join(", ")
}
ResolvedValue::Call(c) => {
if opts.bake_vars && c.name == "light-dark" && !c.args.is_empty() {
format_value(&c.args[0], vars, opts)
} else {
format_call(c, vars, opts)
}
}
ResolvedValue::LiveVar { name, raw } => {
if opts.bake_vars {
if *raw {
format!("var(--{})", name)
} else if let Some(value) = vars.get(name) {
format_value(value, vars, opts)
} else {
format!("var(--lini-{})", name)
}
} else if *raw {
format!("var(--{})", name)
} else {
format!("var(--lini-{})", name)
}
}
}
}
fn format_call(c: &ResolvedCall, vars: &VarTable, opts: &Options) -> String {
let parts: Vec<String> = c.args.iter().map(|a| format_value(a, vars, opts)).collect();
format!("{}({})", c.name, parts.join(", "))
}
pub fn css_value(prop: &str, value: &ResolvedValue, vars: &VarTable, opts: &Options) -> String {
match prop {
"font-size" => format!("{}px", format_value(value, vars, opts)),
"text-shadow" => px_lengths(value, vars, opts),
_ => format_value(value, vars, opts),
}
}
fn px_lengths(value: &ResolvedValue, vars: &VarTable, opts: &Options) -> String {
match value {
ResolvedValue::Number(n) => format!("{}px", num(*n)),
ResolvedValue::Tuple(items) => items
.iter()
.map(|i| px_lengths(i, vars, opts))
.collect::<Vec<_>>()
.join(" "),
ResolvedValue::List(items) => items
.iter()
.map(|i| px_lengths(i, vars, opts))
.collect::<Vec<_>>()
.join(", "),
other => format_value(other, vars, opts),
}
}
pub fn attr_or_var(
attrs: &AttrMap,
name: &str,
var_name: &str,
vars: &VarTable,
opts: &Options,
) -> String {
match attrs.get(name) {
Some(v) => format_value(v, vars, opts),
None => format_value(
&ResolvedValue::LiveVar {
name: var_name.to_string(),
raw: false,
},
vars,
opts,
),
}
}
pub fn attr_points(attrs: &AttrMap, name: &str) -> Option<Vec<(f64, f64)>> {
match attrs.get(name)? {
ResolvedValue::List(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
if let ResolvedValue::Tuple(t) = item
&& t.len() == 2
{
let x = t[0].as_number()?;
let y = t[1].as_number()?;
out.push((x, y));
continue;
}
return None;
}
Some(out)
}
_ => None,
}
}
pub fn dasharray_value(attrs: &AttrMap, width: f64) -> String {
match attrs.get("stroke-style") {
Some(ResolvedValue::Ident(s)) => dash_pattern(s, width),
_ => String::new(),
}
}
pub fn dash_pattern(style: &str, width: f64) -> String {
match style {
"dashed" => format!("{},{}", num(width * 4.0), num(width * 4.0)),
"dotted" => format!("{},{}", num(width), num(width * 2.0)),
_ => String::new(),
}
}
pub fn num(n: f64) -> String {
if n.is_finite() && n == n.trunc() && n.abs() < 1e15 {
return (n as i64).to_string();
}
let s = format!("{:.4}", n);
let trimmed = s.trim_end_matches('0').trim_end_matches('.');
if trimmed.is_empty() || trimmed == "-" {
"0".to_string()
} else {
trimmed.to_string()
}
}
pub fn escape_xml(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for ch in s.chars() {
match ch {
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'&' => out.push_str("&"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(ch),
}
}
out
}
pub fn class_list(
primitive_kind: &str,
type_chain: &[String],
applied_styles: &[String],
) -> Vec<String> {
let mut classes = vec!["lini-node".to_string()];
for name in type_chain {
classes.push(format!("lini-{}", name));
}
classes.push(format!("lini-{}", primitive_kind));
for name in applied_styles {
classes.push(format!("lini-style-{}", name));
}
classes
}