Add __get template-accessible method

Signed-off-by: Olivier <olivier@librepush.net>
This commit is contained in:
Olivier 'reivilibre' 2024-08-01 19:55:40 +01:00
parent 708f5d214d
commit d08c04de5b

View File

@ -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<Value>) -> Result<Value, String>,
)] = &[
("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<Value>) -> Result<Value, String> {
));
}
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<Value>) -> Result<Value, String> {
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<Value>) -> Result<Value, String> {
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<Value>) -> Result<Value, String> {
/// - `<Str>.len() -> Int`
/// - `<List>.len() -> Int`
pub fn len(obj: Value, args: Vec<Value>) -> Result<Value, String> {
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<Value>) -> Result<Value, String> {
Ok(Value::List(result))
}
/// Unwraps an Option or returns the given default..
/// Unwraps an Option or returns the given default.
///
/// `<Option<T>>.unwrap_or(<T>) -> <T>`
pub fn unwrap_or(obj: Value, mut args: Vec<Value>) -> Result<Value, String> {
@ -172,3 +174,44 @@ pub fn unwrap_or(obj: Value, mut args: Vec<Value>) -> Result<Value, String> {
other => Err(format!("{other:?} is not an Option")),
}
}
/// Gets a field on an object by name.
///
/// `<Reflective>.__get(<Str>) -> <T>`
pub fn __get(obj: Value, args: Vec<Value>) -> Result<Value, String> {
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<usize, _> = 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")),
}
}