summaryrefslogtreecommitdiff
path: root/src/library/utility.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/utility.rs')
-rw-r--r--src/library/utility.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/library/utility.rs b/src/library/utility.rs
new file mode 100644
index 00000000..146fce9c
--- /dev/null
+++ b/src/library/utility.rs
@@ -0,0 +1,100 @@
+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::<Value>(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::<Value>(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::<Spanned<Value>>(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<Spanned<f64>>, 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::<Value>(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::<Value>(ctx, "value");
+ Value::Error
+ })
+}