1use crate::error::StdlibError;
27use crate::module::StdlibModule;
28use crate::value::Value;
29
30pub struct StringModule;
32
33impl StringModule {
34 pub fn new() -> Self {
35 Self
36 }
37}
38
39impl Default for StringModule {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl StdlibModule for StringModule {
46 fn name(&self) -> &'static str {
47 "string"
48 }
49
50 fn has_function(&self, function: &str) -> bool {
51 matches!(
52 function,
53 "length"
54 | "concat"
55 | "contains"
56 | "slice"
57 | "trim"
58 | "split"
59 | "to_upper"
60 | "to_lower"
61 | "starts_with"
62 | "ends_with"
63 | "replace"
64 | "replace_all"
65 | "pad_start"
66 | "pad_end"
67 | "repeat"
68 | "join"
69 | "format"
70 | "from"
71 | "is_empty"
72 | "index_of"
73 )
74 }
75
76 fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
77 match function {
78 "length" => self.length(args),
79 "concat" => self.concat(args),
80 "contains" => self.contains(args),
81 "slice" => self.slice(args),
82 "trim" => self.trim(args),
83 "split" => self.split(args),
84 "to_upper" => self.to_upper(args),
85 "to_lower" => self.to_lower(args),
86 "starts_with" => self.starts_with(args),
87 "ends_with" => self.ends_with(args),
88 "replace" => self.replace(args),
89 "replace_all" => self.replace_all(args),
90 "pad_start" => self.pad_start(args),
91 "pad_end" => self.pad_end(args),
92 "repeat" => self.repeat(args),
93 "join" => self.join(args),
94 "format" => self.format(args),
95 "from" => self.value_to_string(args),
96 "is_empty" => self.is_empty(args),
97 "index_of" => self.index_of(args),
98 _ => Err(StdlibError::unknown_function("string", function)),
99 }
100 }
101}
102
103fn expect_one_string(fn_name: &str, args: &[Value]) -> Result<String, StdlibError> {
107 if args.len() != 1 {
108 return Err(StdlibError::wrong_args(fn_name, 1, args.len()));
109 }
110 match &args[0] {
111 Value::String(s) => Ok(s.clone()),
112 other => Err(StdlibError::type_mismatch(
113 fn_name,
114 1,
115 "string",
116 other.type_name(),
117 )),
118 }
119}
120
121fn expect_two_strings(fn_name: &str, args: &[Value]) -> Result<(String, String), StdlibError> {
123 if args.len() != 2 {
124 return Err(StdlibError::wrong_args(fn_name, 2, args.len()));
125 }
126 let a = match &args[0] {
127 Value::String(s) => s.clone(),
128 other => {
129 return Err(StdlibError::type_mismatch(
130 fn_name,
131 1,
132 "string",
133 other.type_name(),
134 ));
135 }
136 };
137 let b = match &args[1] {
138 Value::String(s) => s.clone(),
139 other => {
140 return Err(StdlibError::type_mismatch(
141 fn_name,
142 2,
143 "string",
144 other.type_name(),
145 ));
146 }
147 };
148 Ok((a, b))
149}
150
151fn expect_three_strings(
153 fn_name: &str,
154 args: &[Value],
155) -> Result<(String, String, String), StdlibError> {
156 if args.len() != 3 {
157 return Err(StdlibError::wrong_args(fn_name, 3, args.len()));
158 }
159 let a = match &args[0] {
160 Value::String(s) => s.clone(),
161 other => {
162 return Err(StdlibError::type_mismatch(
163 fn_name,
164 1,
165 "string",
166 other.type_name(),
167 ));
168 }
169 };
170 let b = match &args[1] {
171 Value::String(s) => s.clone(),
172 other => {
173 return Err(StdlibError::type_mismatch(
174 fn_name,
175 2,
176 "string",
177 other.type_name(),
178 ));
179 }
180 };
181 let c = match &args[2] {
182 Value::String(s) => s.clone(),
183 other => {
184 return Err(StdlibError::type_mismatch(
185 fn_name,
186 3,
187 "string",
188 other.type_name(),
189 ));
190 }
191 };
192 Ok((a, b, c))
193}
194
195impl StringModule {
198 fn length(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
202 let s = expect_one_string("string.length", &args)?;
203 Ok(Value::Number(s.chars().count() as f64))
204 }
205
206 fn concat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
208 let (a, b) = expect_two_strings("string.concat", &args)?;
209 Ok(Value::String(format!("{a}{b}")))
210 }
211
212 fn contains(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
214 let (haystack, needle) = expect_two_strings("string.contains", &args)?;
215 Ok(Value::Bool(haystack.contains(&needle)))
216 }
217
218 fn slice(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
224 if args.len() != 3 {
225 return Err(StdlibError::wrong_args("string.slice", 3, args.len()));
226 }
227 let s = match &args[0] {
228 Value::String(s) => s.clone(),
229 other => {
230 return Err(StdlibError::type_mismatch(
231 "string.slice",
232 1,
233 "string",
234 other.type_name(),
235 ));
236 }
237 };
238 let start = match &args[1] {
239 Value::Number(n) => *n,
240 other => {
241 return Err(StdlibError::type_mismatch(
242 "string.slice",
243 2,
244 "number",
245 other.type_name(),
246 ));
247 }
248 };
249 let end = match &args[2] {
250 Value::Number(n) => *n,
251 other => {
252 return Err(StdlibError::type_mismatch(
253 "string.slice",
254 3,
255 "number",
256 other.type_name(),
257 ));
258 }
259 };
260
261 let len = s.chars().count() as isize;
262 let start = (start as isize).clamp(0, len) as usize;
263 let end = (end as isize).clamp(0, len) as usize;
264
265 if start >= end {
266 return Ok(Value::String(String::new()));
267 }
268
269 let result: String = s.chars().skip(start).take(end - start).collect();
270 Ok(Value::String(result))
271 }
272
273 fn trim(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
275 let s = expect_one_string("string.trim", &args)?;
276 Ok(Value::String(s.trim().to_string()))
277 }
278
279 fn split(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
281 let (s, delimiter) = expect_two_strings("string.split", &args)?;
282 let parts: Vec<Value> = if delimiter.is_empty() {
283 s.chars().map(|c| Value::String(c.to_string())).collect()
285 } else {
286 s.split(&delimiter)
287 .map(|part| Value::String(part.to_string()))
288 .collect()
289 };
290 Ok(Value::List(parts))
291 }
292
293 fn to_upper(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
295 let s = expect_one_string("string.to_upper", &args)?;
296 Ok(Value::String(s.to_uppercase()))
297 }
298
299 fn to_lower(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
301 let s = expect_one_string("string.to_lower", &args)?;
302 Ok(Value::String(s.to_lowercase()))
303 }
304
305 fn starts_with(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
307 let (s, prefix) = expect_two_strings("string.starts_with", &args)?;
308 Ok(Value::Bool(s.starts_with(&prefix)))
309 }
310
311 fn ends_with(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
313 let (s, suffix) = expect_two_strings("string.ends_with", &args)?;
314 Ok(Value::Bool(s.ends_with(&suffix)))
315 }
316
317 fn replace(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
321 let (s, old, new) = expect_three_strings("string.replace", &args)?;
322 if old.is_empty() {
323 return Ok(Value::String(s));
325 }
326 let result = if let Some(pos) = s.find(&old) {
327 format!("{}{new}{}", &s[..pos], &s[pos + old.len()..])
328 } else {
329 s
330 };
331 Ok(Value::String(result))
332 }
333
334 fn replace_all(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
336 let (s, old, new) = expect_three_strings("string.replace_all", &args)?;
337 if old.is_empty() {
338 return Ok(Value::String(s));
339 }
340 Ok(Value::String(s.replace(&old, &new)))
341 }
342
343 fn pad_start(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
348 if args.len() != 3 {
349 return Err(StdlibError::wrong_args("string.pad_start", 3, args.len()));
350 }
351 let s = match &args[0] {
352 Value::String(s) => s.clone(),
353 other => {
354 return Err(StdlibError::type_mismatch(
355 "string.pad_start",
356 1,
357 "string",
358 other.type_name(),
359 ));
360 }
361 };
362 let target_len = match &args[1] {
363 Value::Number(n) => *n,
364 other => {
365 return Err(StdlibError::type_mismatch(
366 "string.pad_start",
367 2,
368 "number",
369 other.type_name(),
370 ));
371 }
372 };
373 let pad = match &args[2] {
374 Value::String(s) => s.clone(),
375 other => {
376 return Err(StdlibError::type_mismatch(
377 "string.pad_start",
378 3,
379 "string",
380 other.type_name(),
381 ));
382 }
383 };
384
385 let current_len = s.chars().count();
386 let target_len = target_len as usize;
387
388 if current_len >= target_len || pad.is_empty() {
389 return Ok(Value::String(s));
390 }
391
392 let needed = target_len - current_len;
393 let padding: String = pad.chars().cycle().take(needed).collect();
394 Ok(Value::String(format!("{padding}{s}")))
395 }
396
397 fn pad_end(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
401 if args.len() != 3 {
402 return Err(StdlibError::wrong_args("string.pad_end", 3, args.len()));
403 }
404 let s = match &args[0] {
405 Value::String(s) => s.clone(),
406 other => {
407 return Err(StdlibError::type_mismatch(
408 "string.pad_end",
409 1,
410 "string",
411 other.type_name(),
412 ));
413 }
414 };
415 let target_len = match &args[1] {
416 Value::Number(n) => *n,
417 other => {
418 return Err(StdlibError::type_mismatch(
419 "string.pad_end",
420 2,
421 "number",
422 other.type_name(),
423 ));
424 }
425 };
426 let pad = match &args[2] {
427 Value::String(s) => s.clone(),
428 other => {
429 return Err(StdlibError::type_mismatch(
430 "string.pad_end",
431 3,
432 "string",
433 other.type_name(),
434 ));
435 }
436 };
437
438 let current_len = s.chars().count();
439 let target_len = target_len as usize;
440
441 if current_len >= target_len || pad.is_empty() {
442 return Ok(Value::String(s));
443 }
444
445 let needed = target_len - current_len;
446 let padding: String = pad.chars().cycle().take(needed).collect();
447 Ok(Value::String(format!("{s}{padding}")))
448 }
449
450 fn repeat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
452 if args.len() != 2 {
453 return Err(StdlibError::wrong_args("string.repeat", 2, args.len()));
454 }
455 let s = match &args[0] {
456 Value::String(s) => s.clone(),
457 other => {
458 return Err(StdlibError::type_mismatch(
459 "string.repeat",
460 1,
461 "string",
462 other.type_name(),
463 ));
464 }
465 };
466 let count = match &args[1] {
467 Value::Number(n) => *n,
468 other => {
469 return Err(StdlibError::type_mismatch(
470 "string.repeat",
471 2,
472 "number",
473 other.type_name(),
474 ));
475 }
476 };
477
478 if count < 0.0 || count.fract() != 0.0 {
479 return Err(StdlibError::RuntimeError(
480 "string.repeat: count must be a non-negative integer".to_string(),
481 ));
482 }
483
484 Ok(Value::String(s.repeat(count as usize)))
485 }
486
487 fn join(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
489 if args.len() != 2 {
490 return Err(StdlibError::wrong_args("string.join", 2, args.len()));
491 }
492 let items = match &args[0] {
493 Value::List(l) => l.clone(),
494 other => {
495 return Err(StdlibError::type_mismatch(
496 "string.join",
497 1,
498 "list",
499 other.type_name(),
500 ));
501 }
502 };
503 let separator = match &args[1] {
504 Value::String(s) => s.clone(),
505 other => {
506 return Err(StdlibError::type_mismatch(
507 "string.join",
508 2,
509 "string",
510 other.type_name(),
511 ));
512 }
513 };
514
515 let mut parts = Vec::with_capacity(items.len());
516 for (i, item) in items.iter().enumerate() {
517 match item {
518 Value::String(s) => parts.push(s.clone()),
519 other => {
520 return Err(StdlibError::TypeMismatch {
521 function: "string.join".to_string(),
522 position: i + 1,
523 expected: "string".to_string(),
524 got: other.type_name().to_string(),
525 });
526 }
527 }
528 }
529
530 Ok(Value::String(parts.join(&separator)))
531 }
532
533 fn format(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
538 if args.len() != 2 {
539 return Err(StdlibError::wrong_args("string.format", 2, args.len()));
540 }
541 let template = match &args[0] {
542 Value::String(s) => s.clone(),
543 other => {
544 return Err(StdlibError::type_mismatch(
545 "string.format",
546 1,
547 "string",
548 other.type_name(),
549 ));
550 }
551 };
552 let fields = match &args[1] {
553 Value::Record { fields, .. } => fields.clone(),
554 other => {
555 return Err(StdlibError::type_mismatch(
556 "string.format",
557 2,
558 "record",
559 other.type_name(),
560 ));
561 }
562 };
563
564 let mut result = template;
565 for (key, val) in &fields {
566 let placeholder = format!("{{{key}}}");
567 let replacement = format!("{val}");
568 result = result.replace(&placeholder, &replacement);
569 }
570
571 Ok(Value::String(result))
572 }
573
574 fn value_to_string(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
578 if args.len() != 1 {
579 return Err(StdlibError::wrong_args("string.from", 1, args.len()));
580 }
581 Ok(Value::String(format!("{}", args[0])))
582 }
583
584 fn is_empty(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
586 let s = expect_one_string("string.is_empty", &args)?;
587 Ok(Value::Bool(s.is_empty()))
588 }
589
590 fn index_of(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
595 let (s, sub) = expect_two_strings("string.index_of", &args)?;
596 if sub.is_empty() {
597 return Ok(Value::Number(0.0));
598 }
599 match s.find(&sub) {
601 Some(byte_pos) => {
602 let char_index = s[..byte_pos].chars().count();
603 Ok(Value::Number(char_index as f64))
604 }
605 None => Ok(Value::Number(-1.0)),
606 }
607 }
608}