use crate::{
error::{EvalResult, InterpreterError},
value::Value,
};
pub fn constant(name: &str) -> Option<Value> {
let s = match name {
"ascii_lowercase" => "abcdefghijklmnopqrstuvwxyz",
"ascii_uppercase" => "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"ascii_letters" => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
"digits" => "0123456789",
"hexdigits" => "0123456789abcdefABCDEF",
"octdigits" => "01234567",
"punctuation" => "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
"whitespace" => " \t\n\r\x0b\x0c",
"printable" => {
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c"
}
_ => return None,
};
Some(Value::String(s.into()))
}
pub struct StringModule;
#[async_trait::async_trait]
impl crate::eval::modules::Module for StringModule {
fn name(&self) -> &'static str {
"string"
}
fn constant(&self, name: &str) -> Option<Value> {
constant(name)
}
fn has_function(&self, name: &str) -> bool {
matches!(name, "Template" | "capwords")
}
async fn call(
&self,
_state: &mut crate::state::InterpreterState,
func: &str,
args: &[Value],
_kwargs: &indexmap::IndexMap<String, Value>,
_tools: &crate::tools::Tools,
) -> EvalResult {
match func {
"Template" => match args.first() {
Some(Value::String(s)) => Ok(Value::Template(s.clone())),
Some(other) => Err(InterpreterError::TypeError(format!(
"Template() argument must be str, not {}",
other.type_name()
))
.into()),
None => Err(InterpreterError::TypeError(
"Template() missing required argument: 'template'".into(),
)
.into()),
},
"capwords" => {
let Some(Value::String(s)) = args.first() else {
return Err(InterpreterError::TypeError(
"capwords() requires a string argument".into(),
)
.into());
};
let sep = match args.get(1) {
None | Some(Value::None) => None,
Some(Value::String(sp)) => Some(sp.as_str()),
Some(other) => {
return Err(InterpreterError::TypeError(format!(
"capwords() sep must be str or None, not {}",
other.type_name()
))
.into());
}
};
let words: Vec<String> = match sep {
None => s.split_whitespace().map(py_capitalize).collect(),
Some("") => {
return Err(InterpreterError::ValueError("empty separator".into()).into());
}
Some(sp) => s.split(sp).map(py_capitalize).collect(),
};
Ok(Value::String(words.join(sep.unwrap_or(" ")).into()))
}
_ => Err(InterpreterError::AttributeError(format!(
"module 'string' has no callable '{func}'"
))
.into()),
}
}
}
fn py_capitalize(word: &str) -> String {
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().chain(chars.flat_map(char::to_lowercase)).collect(),
}
}