use std::cmp::Ordering; use crate::color::{Color, RgbaColor}; use crate::pretty::pretty; use super::*; /// `type`: The name of a value's type. pub fn type_(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { match args.expect::(ctx, "value") { Some(value) => value.type_name().into(), None => Value::Error, } } /// `repr`: The string representation of a value. pub fn repr(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { match args.expect::(ctx, "value") { Some(value) => pretty(&value).into(), None => Value::Error, } } /// `len`: The length of a string, an array or a dictionary. pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { match args.expect::>(ctx, "collection") { Some(Spanned { v: Value::Str(v), .. }) => Value::Int(v.len() as i64), Some(Spanned { v: Value::Array(v), .. }) => Value::Int(v.len() as i64), Some(Spanned { v: Value::Dict(v), .. }) => Value::Int(v.len() as i64), Some(other) if other.v != Value::Error => { ctx.diag(error!(other.span, "expected string, array or dictionary")); Value::Error } _ => Value::Error, } } /// `rgb`: Create an RGB(A) color. pub fn rgb(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let r = args.expect(ctx, "red component"); let g = args.expect(ctx, "green component"); let b = args.expect(ctx, "blue component"); let a = args.eat(ctx); let mut clamp = |component: Option>, default| { component.map_or(default, |c| { if c.v < 0.0 || c.v > 1.0 { ctx.diag(warning!(c.span, "should be between 0.0 and 1.0")); } (c.v.max(0.0).min(1.0) * 255.0).round() as u8 }) }; Value::Color(Color::Rgba(RgbaColor::new( clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255), ))) } /// `min`: The minimum of a sequence of values. pub fn min(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { minmax(ctx, args, Ordering::Less) } /// `max`: The maximum of a sequence of values. pub fn max(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { minmax(ctx, args, Ordering::Greater) } /// Find the minimum or maximum of a sequence of values. fn minmax(ctx: &mut EvalContext, args: &mut FuncArgs, goal: Ordering) -> Value { let mut extremum = None; while let Some(value) = args.eat::(ctx) { if let Some(prev) = &extremum { match value.cmp(&prev) { Some(ordering) if ordering == goal => extremum = Some(value), Some(_) => {} None => { ctx.diag(error!( args.span, "cannot compare {} with {}", prev.type_name(), value.type_name(), )); return Value::Error; } } } else { extremum = Some(value); } } extremum.unwrap_or_else(|| { args.expect::(ctx, "value"); Value::Error }) }