sage_runtime/stdlib/
string.rs1#[must_use]
6pub fn str_index_of(haystack: &str, needle: &str) -> Option<i64> {
7 haystack.find(needle).map(|byte_pos| {
8 haystack[..byte_pos].chars().count() as i64
10 })
11}
12
13#[must_use]
16pub fn str_slice(s: &str, start: i64, end: i64) -> String {
17 let start = start.max(0) as usize;
18 let end = end.max(0) as usize;
19 s.chars()
20 .skip(start)
21 .take(end.saturating_sub(start))
22 .collect()
23}
24
25#[must_use]
27pub fn str_pad_start(s: &str, target_len: i64, pad: &str) -> String {
28 let target_len = target_len.max(0) as usize;
29 let current_len = s.chars().count();
30 if current_len >= target_len || pad.is_empty() {
31 return s.to_string();
32 }
33 let needed = target_len - current_len;
34 let pad_chars: Vec<char> = pad.chars().collect();
35 let mut result = String::new();
36 for i in 0..needed {
37 result.push(pad_chars[i % pad_chars.len()]);
38 }
39 result.push_str(s);
40 result
41}
42
43#[must_use]
45pub fn str_pad_end(s: &str, target_len: i64, pad: &str) -> String {
46 let target_len = target_len.max(0) as usize;
47 let current_len = s.chars().count();
48 if current_len >= target_len || pad.is_empty() {
49 return s.to_string();
50 }
51 let needed = target_len - current_len;
52 let pad_chars: Vec<char> = pad.chars().collect();
53 let mut result = s.to_string();
54 for i in 0..needed {
55 result.push(pad_chars[i % pad_chars.len()]);
56 }
57 result
58}
59
60#[must_use]
63pub fn chr(code: i64) -> String {
64 char::from_u32(code as u32)
65 .unwrap_or('\u{FFFD}')
66 .to_string()
67}
68
69#[must_use]
72pub fn list_slice<T: Clone>(list: Vec<T>, start: i64, end: i64) -> Vec<T> {
73 let len = list.len();
74 let start = start.max(0) as usize;
75 let end = end.max(0) as usize;
76 let start = start.min(len);
77 let end = end.min(len);
78 if start >= end {
79 return Vec::new();
80 }
81 list[start..end].to_vec()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_list_slice() {
90 assert_eq!(list_slice(vec![1, 2, 3, 4, 5], 1, 4), vec![2, 3, 4]);
91 assert_eq!(list_slice(vec![1, 2, 3], 0, 10), vec![1, 2, 3]);
92 assert_eq!(list_slice(vec![1, 2, 3], -5, 2), vec![1, 2]);
93 assert_eq!(list_slice(vec![1, 2, 3], 5, 10), Vec::<i64>::new());
94 }
95
96 #[test]
97 fn test_str_index_of() {
98 assert_eq!(str_index_of("hello world", "world"), Some(6));
99 assert_eq!(str_index_of("hello world", "foo"), None);
100 assert_eq!(str_index_of("hello", ""), Some(0));
101 assert_eq!(str_index_of("héllo wörld", "wörld"), Some(6));
103 }
104
105 #[test]
106 fn test_str_slice() {
107 assert_eq!(str_slice("hello", 1, 4), "ell");
108 assert_eq!(str_slice("hello", 0, 5), "hello");
109 assert_eq!(str_slice("hello", 3, 100), "lo");
110 assert_eq!(str_slice("hello", -5, 3), "hel");
111 assert_eq!(str_slice("héllo", 0, 3), "hél");
113 }
114
115 #[test]
116 fn test_str_pad_start() {
117 assert_eq!(str_pad_start("5", 3, "0"), "005");
118 assert_eq!(str_pad_start("hello", 3, "x"), "hello");
119 assert_eq!(str_pad_start("a", 5, "xy"), "xyxya");
120 }
121
122 #[test]
123 fn test_str_pad_end() {
124 assert_eq!(str_pad_end("5", 3, "0"), "500");
125 assert_eq!(str_pad_end("hello", 3, "x"), "hello");
126 assert_eq!(str_pad_end("a", 5, "xy"), "axyxy");
127 }
128}