From d08c04de5b275f7d0c700a074ff79674be04641a Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 1 Aug 2024 19:55:40 +0100 Subject: [PATCH] Add __get template-accessible method Signed-off-by: Olivier --- .../src/functions/defaults.rs | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/hornbeam_interpreter/src/functions/defaults.rs b/hornbeam_interpreter/src/functions/defaults.rs index 2c1525c..1f4ddd2 100644 --- a/hornbeam_interpreter/src/functions/defaults.rs +++ b/hornbeam_interpreter/src/functions/defaults.rs @@ -7,8 +7,9 @@ use crate::interface::Value; use super::TemplateAccessibleMethod; -const DEFAULT_TEMPLATE_ACCESSIBLE_METHODS: &'static [( - &'static str, +#[allow(clippy::type_complexity)] +const DEFAULT_TEMPLATE_ACCESSIBLE_METHODS: &[( + &str, fn(Value, Vec) -> Result, )] = &[ ("leftpad", leftpad), @@ -16,6 +17,7 @@ const DEFAULT_TEMPLATE_ACCESSIBLE_METHODS: &'static [( ("len", len), ("split", split), ("unwrap_or", unwrap_or), + ("__get", __get), ]; /// Return a map of the default suggested template-accessible methods. @@ -45,12 +47,12 @@ pub fn leftpad(obj: Value, args: Vec) -> Result { )); } let Value::Int(pad_length) = &args[0] else { - return Err(format!("leftpad's first arg should be an integer")); + return Err("leftpad's first arg should be an integer".to_owned()); }; let Value::Str(padding_character) = &args[1] else { - return Err(format!( - "leftpad's second arg should be a string (usually a single character)" - )); + return Err( + "leftpad's second arg should be a string (usually a single character)".to_owned(), + ); }; if string_to_pad.len() as i64 >= *pad_length { @@ -63,7 +65,7 @@ pub fn leftpad(obj: Value, args: Vec) -> Result { let mut result = String::new(); for _ in 0..repetitions { - result.push_str(&padding_character); + result.push_str(padding_character); } result.push_str(&string_to_pad); @@ -77,7 +79,7 @@ pub fn urlencode(obj: Value, args: Vec) -> Result { let Value::Str(string_to_encode) = obj else { return Err(format!("{obj:?} is not a string: can't urlencode!")); }; - if args.len() != 0 { + if !args.is_empty() { return Err(format!("urlencode takes 0 args, not {}", args.len())); } @@ -91,7 +93,7 @@ pub fn urlencode(obj: Value, args: Vec) -> Result { /// - `.len() -> Int` /// - `.len() -> Int` pub fn len(obj: Value, args: Vec) -> Result { - if args.len() != 0 { + if !args.is_empty() { return Err(format!("len takes 0 args, not {}", args.len())); } @@ -140,7 +142,7 @@ pub fn split(obj: Value, args: Vec) -> Result { Ok(Value::List(result)) } -/// Unwraps an Option or returns the given default.. +/// Unwraps an Option or returns the given default. /// /// `>.unwrap_or() -> ` pub fn unwrap_or(obj: Value, mut args: Vec) -> Result { @@ -172,3 +174,44 @@ pub fn unwrap_or(obj: Value, mut args: Vec) -> Result { other => Err(format!("{other:?} is not an Option")), } } + +/// Gets a field on an object by name. +/// +/// `.__get() -> ` +pub fn __get(obj: Value, args: Vec) -> Result { + if args.len() != 1 { + return Err(format!("__get takes 1 arg, not {}", args.len())); + } + let Value::Str(ident) = &args[0] else { + // TODO support ints for tuple structs in the future? + return Err("first arg is not a string: can't __get!".to_owned()); + }; + + match obj { + Value::Reflective(reflective) => { + match reflective.reflect_ref() { + ReflectRef::Struct(ss) => { + if let Some(field) = ss.field(ident) { + Ok(Value::from_reflect(field.clone_value())) + } else { + Err(format!("__get Field Lookup for '{ident}': {reflective:?} is a reflective struct that does not have that field!")) + } + } + ReflectRef::TupleStruct(ts) => { + let field_id: Result = ident.parse(); + if let Ok(field_id) = field_id { + if let Some(field) = ts.field(field_id) { + Ok(Value::from_reflect(field.clone_value())) + } else { + Err(format!("__get Field Lookup for '{ident}': {reflective:?} is a reflective tuple struct that does not have that field!")) + } + } else { + Err(format!("__get Field Lookup for '{ident}': {reflective:?} is a reflective tuple struct that does not have names for field!")) + } + } + _ => Err(format!("__get Field Lookup for '{ident}': {reflective:?} is of a reflective type that has no fields!")) + } + } + other => Err(format!("{other:?} is not Reflective")), + } +}