grass/src/builtin/list.rs

213 lines
6.9 KiB
Rust
Raw Normal View History

use std::collections::HashMap;
2020-02-02 21:09:29 -05:00
2020-03-22 22:28:54 -04:00
use num_traits::{One, Signed, ToPrimitive, Zero};
2020-03-20 12:09:08 -04:00
2020-02-14 10:10:51 -05:00
use super::Builtin;
use crate::common::{Brackets, ListSeparator, QuoteKind};
use crate::unit::Unit;
2020-02-14 10:10:51 -05:00
use crate::value::{Number, Value};
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
f.insert(
"length".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
2020-03-20 10:03:54 -04:00
max_args!(args, 1);
let len = match arg!(args, 0, "list") {
Value::List(v, ..) => Number::from(v.len()),
2020-03-30 16:01:44 -04:00
Value::Map(m) => Number::from(m.len()),
2020-03-22 22:28:54 -04:00
_ => Number::one(),
};
Ok(Value::Dimension(len, Unit::None))
}),
);
2020-03-20 12:09:08 -04:00
f.insert(
"nth".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
2020-03-20 12:09:08 -04:00
max_args!(args, 2);
let list = match arg!(args, 0, "list") {
Value::List(v, ..) => v,
2020-03-20 12:36:31 -04:00
v => vec![v],
2020-03-20 12:09:08 -04:00
};
let n = match arg!(args, 1, "n") {
Value::Dimension(num, _) => num,
v => return Err(format!("$n: {} is not a number.", v).into()),
};
2020-03-22 22:28:54 -04:00
if n.is_zero() {
2020-03-20 12:09:08 -04:00
return Err("$n: List index may not be 0.".into());
}
if n.abs() > Number::from(list.len()) {
return Err(format!(
"$n: Invalid index {} for a list with {} elements.",
n,
list.len()
)
.into());
}
if n.is_decimal() {
return Err(format!("$n: {} is not an int.", n).into());
}
2020-03-22 22:28:54 -04:00
if n.is_positive() {
2020-03-20 12:09:08 -04:00
Ok(list[n.to_integer().to_usize().unwrap() - 1].clone())
} else {
Ok(list[list.len() - n.abs().to_integer().to_usize().unwrap()].clone())
}
}),
);
f.insert(
"list-separator".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
max_args!(args, 1);
Ok(Value::Ident(
match arg!(args, 0, "list") {
Value::List(_, sep, ..) => sep.name(),
_ => ListSeparator::Space.name(),
}
.to_owned(),
QuoteKind::None,
))
}),
);
2020-03-20 19:27:26 -04:00
f.insert(
"set-nth".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
2020-03-20 19:27:26 -04:00
max_args!(args, 3);
let (mut list, sep) = match arg!(args, 0, "list") {
Value::List(v, sep, ..) => (v, sep),
2020-03-20 19:27:26 -04:00
v => (vec![v], ListSeparator::Space),
};
let n = match arg!(args, 1, "n") {
Value::Dimension(num, _) => num,
v => return Err(format!("$n: {} is not a number.", v).into()),
};
2020-03-22 22:28:54 -04:00
if n.is_zero() {
2020-03-20 19:27:26 -04:00
return Err("$n: List index may not be 0.".into());
}
let len = list.len();
if n.abs() > Number::from(len) {
return Err(
format!("$n: Invalid index {} for a list with {} elements.", n, len).into(),
);
}
if n.is_decimal() {
return Err(format!("$n: {} is not an int.", n).into());
}
let val = arg!(args, 2, "value");
2020-03-22 22:28:54 -04:00
if n.is_positive() {
2020-03-20 19:27:26 -04:00
list[n.to_integer().to_usize().unwrap() - 1] = val;
} else {
list[len - n.abs().to_integer().to_usize().unwrap()] = val;
}
Ok(Value::List(list, sep, Brackets::None))
2020-03-20 19:50:23 -04:00
}),
);
f.insert(
"append".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
2020-03-20 19:50:23 -04:00
max_args!(args, 3);
let (mut list, sep) = match arg!(args, 0, "list") {
Value::List(v, sep, ..) => (v, sep),
2020-03-20 19:50:23 -04:00
v => (vec![v], ListSeparator::Space),
};
let val = arg!(args, 1, "val");
let sep = match arg!(
args,
2,
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
) {
Value::Ident(s, ..) => match s.as_str() {
"auto" => sep,
"comma" => ListSeparator::Comma,
"space" => ListSeparator::Space,
_ => {
return Err("$separator: Must be \"space\", \"comma\", or \"auto\".".into())
}
},
_ => return Err("$separator: Must be \"space\", \"comma\", or \"auto\".".into()),
};
list.push(val);
Ok(Value::List(list, sep, Brackets::None))
2020-03-20 19:27:26 -04:00
}),
);
2020-03-22 14:04:16 -04:00
f.insert(
"join".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
2020-03-22 14:04:16 -04:00
max_args!(args, 3);
let (mut list1, sep1, brackets) = match arg!(args, 0, "list") {
Value::List(v, sep, brackets) => (v, sep, brackets),
v => (vec![v], ListSeparator::Space, Brackets::None),
2020-03-22 14:04:16 -04:00
};
let list2 = match arg!(args, 1, "list") {
Value::List(v, ..) => v,
v => vec![v],
};
let sep = match arg!(
args,
2,
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
) {
Value::Ident(s, ..) => match s.as_str() {
"auto" => {
if list1.len() < 2 && list2.len() < 2 {
ListSeparator::Space
} else {
sep1
}
2020-03-22 15:08:13 -04:00
}
2020-03-22 14:04:16 -04:00
"comma" => ListSeparator::Comma,
"space" => ListSeparator::Space,
_ => {
return Err("$separator: Must be \"space\", \"comma\", or \"auto\".".into())
}
},
_ => return Err("$separator: Must be \"space\", \"comma\", or \"auto\".".into()),
};
list1.extend(list2);
Ok(Value::List(list1, sep, brackets))
2020-03-22 14:04:16 -04:00
}),
);
f.insert(
"is-bracketed".to_owned(),
2020-04-02 13:45:14 -04:00
Box::new(|mut args, _| {
max_args!(args, 1);
Ok(Value::bool(match arg!(args, 0, "list") {
Value::List(.., brackets) => match brackets {
Brackets::Bracketed => true,
Brackets::None => false,
},
_ => false,
}))
}),
);
2020-04-02 14:22:38 -04:00
f.insert(
"index".to_owned(),
Box::new(|mut args, _| {
max_args!(args, 2);
let list = match arg!(args, 0, "list") {
Value::List(v, ..) => v,
v => vec![v],
};
let value = arg!(args, 1, "value");
let index = match list.into_iter().position(|v| v == value) {
Some(v) => Number::from(v + 1),
None => return Ok(Value::Null),
};
Ok(Value::Dimension(index, Unit::None))
}),
);
2020-02-14 10:10:51 -05:00
}