mirror of
https://github.com/typst/typst
synced 2025-06-29 00:32:53 +08:00
134 lines
4.0 KiB
Rust
134 lines
4.0 KiB
Rust
//! Methods on values.
|
|
|
|
use super::{Args, Regex, StrExt, Value};
|
|
use crate::diag::{At, TypResult};
|
|
use crate::syntax::Span;
|
|
use crate::util::EcoString;
|
|
use crate::Context;
|
|
|
|
/// Call a method on a value.
|
|
pub fn call(
|
|
ctx: &mut Context,
|
|
value: Value,
|
|
method: &str,
|
|
mut args: Args,
|
|
span: Span,
|
|
) -> TypResult<Value> {
|
|
let name = value.type_name();
|
|
let missing = || Err(missing_method(name, method)).at(span);
|
|
|
|
let output = match value {
|
|
Value::Str(string) => match method {
|
|
"len" => Value::Int(string.len() as i64),
|
|
"trim" => Value::Str(string.trim().into()),
|
|
"split" => Value::Array(string.split(args.eat()?)),
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Array(array) => match method {
|
|
"len" => Value::Int(array.len()),
|
|
"slice" => {
|
|
let start = args.expect("start")?;
|
|
let mut end = args.eat()?;
|
|
if end.is_none() {
|
|
end = args.named("count")?.map(|c: i64| start + c);
|
|
}
|
|
Value::Array(array.slice(start, end).at(span)?)
|
|
}
|
|
"map" => Value::Array(array.map(ctx, args.expect("function")?)?),
|
|
"filter" => Value::Array(array.filter(ctx, args.expect("function")?)?),
|
|
"flatten" => Value::Array(array.flatten()),
|
|
"find" => array.find(args.expect("value")?).map_or(Value::None, Value::Int),
|
|
"join" => {
|
|
let sep = args.eat()?;
|
|
let last = args.named("last")?;
|
|
array.join(sep, last).at(span)?
|
|
}
|
|
"sorted" => Value::Array(array.sorted().at(span)?),
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Dict(dict) => match method {
|
|
"len" => Value::Int(dict.len()),
|
|
"keys" => Value::Array(dict.keys()),
|
|
"values" => Value::Array(dict.values()),
|
|
"pairs" => Value::Array(dict.map(ctx, args.expect("function")?)?),
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Func(func) => match method {
|
|
"with" => Value::Func(func.clone().with(args.take())),
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Args(args) => match method {
|
|
"positional" => Value::Array(args.to_positional()),
|
|
"named" => Value::Dict(args.to_named()),
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Dyn(dynamic) => {
|
|
if let Some(regex) = dynamic.downcast::<Regex>() {
|
|
match method {
|
|
"matches" => {
|
|
Value::Bool(regex.matches(&args.expect::<EcoString>("text")?))
|
|
}
|
|
_ => missing()?,
|
|
}
|
|
} else {
|
|
missing()?
|
|
}
|
|
}
|
|
|
|
_ => missing()?,
|
|
};
|
|
|
|
args.finish()?;
|
|
Ok(output)
|
|
}
|
|
|
|
/// Call a mutating method on a value.
|
|
pub fn call_mut(
|
|
_: &mut Context,
|
|
value: &mut Value,
|
|
method: &str,
|
|
mut args: Args,
|
|
span: Span,
|
|
) -> TypResult<()> {
|
|
let name = value.type_name();
|
|
let missing = || Err(missing_method(name, method)).at(span);
|
|
|
|
match value {
|
|
Value::Array(array) => match method {
|
|
"push" => array.push(args.expect("value")?),
|
|
"pop" => array.pop().at(span)?,
|
|
"insert" => {
|
|
array.insert(args.expect("index")?, args.expect("value")?).at(span)?
|
|
}
|
|
"remove" => array.remove(args.expect("index")?).at(span)?,
|
|
_ => missing()?,
|
|
},
|
|
|
|
Value::Dict(dict) => match method {
|
|
"remove" => dict.remove(&args.expect("key")?).at(span)?,
|
|
_ => missing()?,
|
|
},
|
|
|
|
_ => missing()?,
|
|
}
|
|
|
|
args.finish()?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Whether a specific method is mutating.
|
|
pub fn is_mutating(method: &str) -> bool {
|
|
matches!(method, "push" | "pop" | "insert" | "remove")
|
|
}
|
|
|
|
/// The missing method error message.
|
|
#[cold]
|
|
fn missing_method(type_name: &str, method: &str) -> String {
|
|
format!("type {type_name} has no method `{method}`")
|
|
}
|