summaryrefslogtreecommitdiff
path: root/crates/typst-eval/src/methods.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-27 19:04:55 +0100
committerGitHub <noreply@github.com>2024-10-27 18:04:55 +0000
commitbe7cfc85d08c545abfac08098b7b33b4bd71f37e (patch)
treef4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-eval/src/methods.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-eval/src/methods.rs')
-rw-r--r--crates/typst-eval/src/methods.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/crates/typst-eval/src/methods.rs b/crates/typst-eval/src/methods.rs
new file mode 100644
index 00000000..7cb36a00
--- /dev/null
+++ b/crates/typst-eval/src/methods.rs
@@ -0,0 +1,91 @@
+//! Handles special built-in methods on values.
+
+use typst_library::diag::{At, SourceResult};
+use typst_library::foundations::{Args, Str, Type, Value};
+use typst_syntax::Span;
+
+/// Whether a specific method is mutating.
+pub(crate) fn is_mutating_method(method: &str) -> bool {
+ matches!(method, "push" | "pop" | "insert" | "remove")
+}
+
+/// Whether a specific method is an accessor.
+pub(crate) fn is_accessor_method(method: &str) -> bool {
+ matches!(method, "first" | "last" | "at")
+}
+
+/// Call a mutating method on a value.
+pub(crate) fn call_method_mut(
+ value: &mut Value,
+ method: &str,
+ mut args: Args,
+ span: Span,
+) -> SourceResult<Value> {
+ let ty = value.ty();
+ let missing = || Err(missing_method(ty, method)).at(span);
+ let mut output = Value::None;
+
+ match value {
+ Value::Array(array) => match method {
+ "push" => array.push(args.expect("value")?),
+ "pop" => output = array.pop().at(span)?,
+ "insert" => {
+ array.insert(args.expect("index")?, args.expect("value")?).at(span)?
+ }
+ "remove" => {
+ output = array
+ .remove(args.expect("index")?, args.named("default")?)
+ .at(span)?
+ }
+ _ => return missing(),
+ },
+
+ Value::Dict(dict) => match method {
+ "insert" => dict.insert(args.expect::<Str>("key")?, args.expect("value")?),
+ "remove" => {
+ output =
+ dict.remove(args.expect("key")?, args.named("default")?).at(span)?
+ }
+ _ => return missing(),
+ },
+
+ _ => return missing(),
+ }
+
+ args.finish()?;
+ Ok(output)
+}
+
+/// Call an accessor method on a value.
+pub(crate) fn call_method_access<'a>(
+ value: &'a mut Value,
+ method: &str,
+ mut args: Args,
+ span: Span,
+) -> SourceResult<&'a mut Value> {
+ let ty = value.ty();
+ let missing = || Err(missing_method(ty, method)).at(span);
+
+ let slot = match value {
+ Value::Array(array) => match method {
+ "first" => array.first_mut().at(span)?,
+ "last" => array.last_mut().at(span)?,
+ "at" => array.at_mut(args.expect("index")?).at(span)?,
+ _ => return missing(),
+ },
+ Value::Dict(dict) => match method {
+ "at" => dict.at_mut(&args.expect::<Str>("key")?).at(span)?,
+ _ => return missing(),
+ },
+ _ => return missing(),
+ };
+
+ args.finish()?;
+ Ok(slot)
+}
+
+/// The missing method error message.
+#[cold]
+fn missing_method(ty: Type, method: &str) -> String {
+ format!("type {ty} has no method `{method}`")
+}