use crate::{FromValue, FunctionResult, Value};
pub(crate) struct Opts {
start: Option<bool>,
end: Option<bool>,
matches: Option<Value>,
}
impl FromValue for Opts {
fn from_value(val: Value) -> Result<Self, Box<dyn std::error::Error>> {
if val == Value::Null {
Ok(Self {
start: None,
end: None,
matches: None,
})
} else if let Value::Object(mut val) = val {
Ok(Self {
start: if let Some(val) = val.remove("start") {
Some(bool::from_value(val)?)
} else {
None
},
end: if let Some(val) = val.remove("end") {
Some(bool::from_value(val)?)
} else {
None
},
matches: val.remove("matches"),
})
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Object".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
pub(crate) fn trim(val: String, opts: Option<Opts>) -> FunctionResult<String> {
let opts = opts.unwrap_or(Opts {
start: None,
end: None,
matches: None,
});
let start = opts.start.unwrap_or(true);
let end = opts.end.unwrap_or(true);
if let Some(matches) = opts.matches {
if let Value::String(matches) = matches {
let mut val = if start {
val.trim_start_matches(&matches)
} else {
&val
};
if end {
val = val.trim_end_matches(&matches);
}
Ok(val.to_string())
} else if let Value::Array(matches) = matches {
let matches = matches
.iter()
.filter_map(|m| {
if let Value::String(m) = m {
m.chars().next()
} else {
None
}
})
.collect::<Vec<char>>();
let mut val = if start {
val.trim_start_matches(|ch| matches.contains(&ch))
} else {
&val
};
if end {
val = val.trim_end_matches(|ch| matches.contains(&ch));
}
Ok(val.to_string())
} else {
Err(format!(
"mismatched types for the object field `matches` passed as argument of the `trim` function: expected String or Array, found {}",
matches.type_name()
).into())
}
} else {
let mut val = if start { val.trim_start() } else { &val };
if end {
val = val.trim_end();
}
Ok(val.to_string())
}
}
#[cfg(test)]
mod tests {
use crate::IntoValue;
use super::*;
#[test]
fn trim_test() {
assert_eq!(
trim(" hello ".to_string(), None).unwrap(),
"hello".to_string()
);
assert_eq!(
trim(
" hello ".to_string(),
Some(Opts {
start: Some(false),
end: None,
matches: None,
}),
)
.unwrap(),
" hello".to_string()
);
assert_eq!(
trim(
"11hello22".to_string(),
Some(Opts {
start: None,
end: None,
matches: Some(
vec![Value::String("1".to_string()), "2".into_value()].into_value()
),
}),
)
.unwrap(),
"hello".to_string()
);
}
}