summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/foundations/fields.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-library/src/foundations/fields.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-library/src/foundations/fields.rs')
-rw-r--r--crates/typst-library/src/foundations/fields.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/crates/typst-library/src/foundations/fields.rs b/crates/typst-library/src/foundations/fields.rs
new file mode 100644
index 00000000..422f30b8
--- /dev/null
+++ b/crates/typst-library/src/foundations/fields.rs
@@ -0,0 +1,91 @@
+//! Fields on values.
+
+use ecow::{eco_format, EcoString};
+
+use crate::diag::StrResult;
+use crate::foundations::{IntoValue, Type, Value, Version};
+use crate::layout::{Alignment, Length, Rel};
+use crate::visualize::Stroke;
+
+/// Try to access a field on a value.
+///
+/// This function is exclusively for types which have predefined fields, such as
+/// stroke and length.
+pub(crate) fn field(value: &Value, field: &str) -> StrResult<Value> {
+ let ty = value.ty();
+ let nope = || Err(no_fields(ty));
+ let missing = || Err(missing_field(ty, field));
+
+ // Special cases, such as module and dict, are handled by Value itself
+ let result = match value {
+ Value::Version(version) => match version.component(field) {
+ Ok(i) => i.into_value(),
+ Err(_) => return missing(),
+ },
+ Value::Length(length) => match field {
+ "em" => length.em.get().into_value(),
+ "abs" => length.abs.into_value(),
+ _ => return missing(),
+ },
+ Value::Relative(rel) => match field {
+ "ratio" => rel.rel.into_value(),
+ "length" => rel.abs.into_value(),
+ _ => return missing(),
+ },
+ Value::Dyn(dynamic) => {
+ if let Some(stroke) = dynamic.downcast::<Stroke>() {
+ match field {
+ "paint" => stroke.paint.clone().into_value(),
+ "thickness" => stroke.thickness.into_value(),
+ "cap" => stroke.cap.into_value(),
+ "join" => stroke.join.into_value(),
+ "dash" => stroke.dash.clone().into_value(),
+ "miter-limit" => {
+ stroke.miter_limit.map(|limit| limit.get()).into_value()
+ }
+ _ => return missing(),
+ }
+ } else if let Some(align) = dynamic.downcast::<Alignment>() {
+ match field {
+ "x" => align.x().into_value(),
+ "y" => align.y().into_value(),
+ _ => return missing(),
+ }
+ } else {
+ return nope();
+ }
+ }
+ _ => return nope(),
+ };
+
+ Ok(result)
+}
+
+/// The error message for a type not supporting field access.
+#[cold]
+fn no_fields(ty: Type) -> EcoString {
+ eco_format!("cannot access fields on type {ty}")
+}
+
+/// The missing field error message.
+#[cold]
+fn missing_field(ty: Type, field: &str) -> EcoString {
+ eco_format!("{ty} does not contain field \"{field}\"")
+}
+
+/// List the available fields for a type.
+pub fn fields_on(ty: Type) -> &'static [&'static str] {
+ if ty == Type::of::<Version>() {
+ &Version::COMPONENTS
+ } else if ty == Type::of::<Length>() {
+ &["em", "abs"]
+ } else if ty == Type::of::<Rel>() {
+ &["ratio", "length"]
+ } else if ty == Type::of::<Stroke>() {
+ &["paint", "thickness", "cap", "join", "dash", "miter-limit"]
+ } else if ty == Type::of::<Alignment>() {
+ &["x", "y"]
+ } else {
+ &[]
+ }
+}