tatara_lisp_script/stdlib/
string.rs1use std::sync::Arc;
11
12use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
13
14use crate::script_ctx::ScriptCtx;
15use crate::stdlib::env::str_arg;
16
17pub fn install(interp: &mut Interpreter<ScriptCtx>) {
18 interp.register_fn(
19 "string-replace",
20 Arity::Exact(3),
21 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
22 let s = str_arg(&args[0], "string-replace", sp)?;
23 let from = str_arg(&args[1], "string-replace", sp)?;
24 let to = str_arg(&args[2], "string-replace", sp)?;
25 Ok(Value::Str(Arc::from(s.replace(&*from, &to))))
26 },
27 );
28
29 interp.register_fn(
30 "string-split",
31 Arity::Exact(2),
32 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
33 let s = str_arg(&args[0], "string-split", sp)?;
34 let sep = str_arg(&args[1], "string-split", sp)?;
35 Ok(Value::list(
36 s.split(&*sep)
37 .map(|p| Value::Str(Arc::from(p)))
38 .collect::<Vec<_>>(),
39 ))
40 },
41 );
42
43 interp.register_fn(
44 "string-contains?",
45 Arity::Exact(2),
46 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
47 let s = str_arg(&args[0], "string-contains?", sp)?;
48 let needle = str_arg(&args[1], "string-contains?", sp)?;
49 Ok(Value::Bool(s.contains(&*needle)))
50 },
51 );
52
53 interp.register_fn(
54 "string-lowercase",
55 Arity::Exact(1),
56 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
57 let s = str_arg(&args[0], "string-lowercase", sp)?;
58 Ok(Value::Str(Arc::from(s.to_lowercase())))
59 },
60 );
61
62 interp.register_fn(
63 "string-trim",
64 Arity::Exact(1),
65 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
66 let s = str_arg(&args[0], "string-trim", sp)?;
67 Ok(Value::Str(Arc::from(s.trim())))
68 },
69 );
70
71 interp.register_fn(
72 "string-format",
73 Arity::AtLeast(1),
74 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
75 let tmpl = str_arg(&args[0], "string-format", sp)?;
76 let mut out = String::with_capacity(tmpl.len());
77 let mut remaining = &*tmpl;
78 let mut idx = 1usize;
79 while let Some(pos) = remaining.find("{}") {
80 out.push_str(&remaining[..pos]);
81 let Some(arg) = args.get(idx) else {
82 return Err(EvalError::native_fn(
83 "string-format",
84 format!("template has more {{}} than args ({idx})"),
85 sp,
86 ));
87 };
88 out.push_str(&display_value(arg));
89 remaining = &remaining[pos + 2..];
90 idx += 1;
91 }
92 out.push_str(remaining);
93 Ok(Value::Str(Arc::from(out)))
94 },
95 );
96}
97
98fn display_value(v: &Value) -> String {
99 match v {
100 Value::Nil => "nil".to_string(),
101 Value::Bool(b) => b.to_string(),
102 Value::Int(n) => n.to_string(),
103 Value::Float(n) => n.to_string(),
104 Value::Str(s) | Value::Symbol(s) | Value::Keyword(s) => s.as_ref().to_string(),
105 other => format!("{other:?}"),
106 }
107}