tcss_core/builtins/
strings.rs

1//! Built-in string utility functions for TCSS
2//!
3//! This module provides string manipulation functions:
4//! - uppercase(str) - Convert to uppercase
5//! - lowercase(str) - Convert to lowercase
6//! - concat(str1, str2, ...) - Concatenate strings
7//! - quote(str) - Wrap in quotes
8//! - unquote(str) - Remove quotes
9//! - str_length(str) - Get string length
10//! - str_slice(str, start, end) - Extract substring
11//! - str_replace(str, search, replace) - Replace text
12
13use crate::context::Value;
14
15/// Convert string to uppercase
16pub fn uppercase(args: &[Value]) -> Result<Value, String> {
17    if args.len() != 1 {
18        return Err("uppercase() expects 1 argument".to_string());
19    }
20    
21    match &args[0] {
22        Value::String(s) => Ok(Value::String(s.to_uppercase())),
23        Value::Color(c) => Ok(Value::Color(c.to_uppercase())),
24        _ => Err("uppercase() expects a string argument".to_string()),
25    }
26}
27
28/// Convert string to lowercase
29pub fn lowercase(args: &[Value]) -> Result<Value, String> {
30    if args.len() != 1 {
31        return Err("lowercase() expects 1 argument".to_string());
32    }
33    
34    match &args[0] {
35        Value::String(s) => Ok(Value::String(s.to_lowercase())),
36        Value::Color(c) => Ok(Value::Color(c.to_lowercase())),
37        _ => Err("lowercase() expects a string argument".to_string()),
38    }
39}
40
41/// Concatenate strings
42pub fn concat(args: &[Value]) -> Result<Value, String> {
43    if args.is_empty() {
44        return Err("concat() expects at least 1 argument".to_string());
45    }
46    
47    let mut result = String::new();
48    
49    for arg in args {
50        result.push_str(&arg.to_css_string());
51    }
52    
53    Ok(Value::String(result))
54}
55
56/// Wrap string in quotes
57pub fn quote(args: &[Value]) -> Result<Value, String> {
58    if args.len() != 1 {
59        return Err("quote() expects 1 argument".to_string());
60    }
61    
62    let s = args[0].to_css_string();
63    
64    // If already quoted, return as-is
65    if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
66        Ok(Value::String(s))
67    } else {
68        Ok(Value::String(format!("\"{}\"", s)))
69    }
70}
71
72/// Remove quotes from string
73pub fn unquote(args: &[Value]) -> Result<Value, String> {
74    if args.len() != 1 {
75        return Err("unquote() expects 1 argument".to_string());
76    }
77    
78    let s = args[0].to_css_string();
79    
80    // Remove surrounding quotes if present
81    let unquoted = if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
82        s[1..s.len()-1].to_string()
83    } else {
84        s
85    };
86    
87    Ok(Value::String(unquoted))
88}
89
90/// Get string length
91pub fn str_length(args: &[Value]) -> Result<Value, String> {
92    if args.len() != 1 {
93        return Err("str_length() expects 1 argument".to_string());
94    }
95    
96    let s = args[0].to_css_string();
97    Ok(Value::Number(s.len() as f64))
98}
99
100/// Extract substring
101pub fn str_slice(args: &[Value]) -> Result<Value, String> {
102    if args.len() < 2 || args.len() > 3 {
103        return Err("str_slice() expects 2 or 3 arguments: str, start, [end]".to_string());
104    }
105    
106    let s = args[0].to_css_string();
107    
108    let start = match &args[1] {
109        Value::Number(n) => *n as usize,
110        _ => return Err("str_slice() expects a number for start index".to_string()),
111    };
112    
113    let end = if args.len() == 3 {
114        match &args[2] {
115            Value::Number(n) => *n as usize,
116            _ => return Err("str_slice() expects a number for end index".to_string()),
117        }
118    } else {
119        s.len()
120    };
121    
122    if start > s.len() || end > s.len() || start > end {
123        return Err("str_slice() indices out of bounds".to_string());
124    }
125    
126    Ok(Value::String(s[start..end].to_string()))
127}
128
129/// Replace text in string
130pub fn str_replace(args: &[Value]) -> Result<Value, String> {
131    if args.len() != 3 {
132        return Err("str_replace() expects 3 arguments: str, search, replace".to_string());
133    }
134    
135    let s = args[0].to_css_string();
136    let search = args[1].to_css_string();
137    let replace = args[2].to_css_string();
138    
139    Ok(Value::String(s.replace(&search, &replace)))
140}
141