1use crate::seqstring::{global_bytes, global_string};
4use crate::stack::{Stack, pop, push};
5use crate::value::Value;
6use std::sync::Arc;
7
8#[unsafe(no_mangle)]
11pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
12 use crate::value::VariantData;
13
14 assert!(!stack.is_null(), "string_split: stack is empty");
15
16 let (stack, delim_val) = unsafe { pop(stack) };
17 assert!(!stack.is_null(), "string_split: need two strings");
18 let (stack, str_val) = unsafe { pop(stack) };
19
20 match (str_val, delim_val) {
21 (Value::String(s), Value::String(d)) => {
22 let bytes = s.as_bytes();
27 let needle = d.as_bytes();
28 let parts: Vec<Vec<u8>> = if needle.is_empty() {
29 let mut parts = Vec::with_capacity(bytes.len() + 2);
32 parts.push(Vec::new());
33 for b in bytes {
34 parts.push(vec![*b]);
35 }
36 parts.push(Vec::new());
37 parts
38 } else {
39 let mut parts: Vec<Vec<u8>> = Vec::new();
40 let mut last = 0usize;
41 let mut i = 0usize;
42 while i + needle.len() <= bytes.len() {
43 if &bytes[i..i + needle.len()] == needle {
44 parts.push(bytes[last..i].to_vec());
45 i += needle.len();
46 last = i;
47 } else {
48 i += 1;
49 }
50 }
51 parts.push(bytes[last..].to_vec());
52 parts
53 };
54
55 let fields: Vec<Value> = parts
56 .into_iter()
57 .map(|part| Value::String(global_bytes(part)))
58 .collect();
59
60 let variant = Value::Variant(Arc::new(VariantData::new(
62 global_string("List".to_string()),
63 fields,
64 )));
65
66 unsafe { push(stack, variant) }
67 }
68 _ => panic!("string_split: expected two strings on stack"),
69 }
70}
71
72#[unsafe(no_mangle)]
79pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
80 assert!(!stack.is_null(), "string_contains: stack is empty");
81
82 let (stack, substring_val) = unsafe { pop(stack) };
83 assert!(!stack.is_null(), "string_contains: need two strings");
84 let (stack, str_val) = unsafe { pop(stack) };
85
86 match (str_val, substring_val) {
87 (Value::String(s), Value::String(sub)) => {
88 let contains = byte_contains(s.as_bytes(), sub.as_bytes());
91 unsafe { push(stack, Value::Bool(contains)) }
92 }
93 _ => panic!("string_contains: expected two strings on stack"),
94 }
95}
96
97fn byte_contains(haystack: &[u8], needle: &[u8]) -> bool {
100 if needle.is_empty() {
101 return true;
102 }
103 haystack.windows(needle.len()).any(|w| w == needle)
104}
105
106#[unsafe(no_mangle)]
113pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
114 assert!(!stack.is_null(), "string_starts_with: stack is empty");
115
116 let (stack, prefix_val) = unsafe { pop(stack) };
117 assert!(!stack.is_null(), "string_starts_with: need two strings");
118 let (stack, str_val) = unsafe { pop(stack) };
119
120 match (str_val, prefix_val) {
121 (Value::String(s), Value::String(prefix)) => {
122 let starts = s.as_bytes().starts_with(prefix.as_bytes());
124 unsafe { push(stack, Value::Bool(starts)) }
125 }
126 _ => panic!("string_starts_with: expected two strings on stack"),
127 }
128}
129
130#[unsafe(no_mangle)]
137pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
138 assert!(!stack.is_null(), "string_char_at: stack is empty");
139
140 let (stack, index_val) = unsafe { pop(stack) };
141 assert!(!stack.is_null(), "string_char_at: need string and index");
142 let (stack, str_val) = unsafe { pop(stack) };
143
144 match (str_val, index_val) {
145 (Value::String(s), Value::Int(index)) => {
146 let result = if index < 0 {
147 -1
148 } else {
149 s.as_str_or_empty()
150 .chars()
151 .nth(index as usize)
152 .map(|c| c as i64)
153 .unwrap_or(-1)
154 };
155 unsafe { push(stack, Value::Int(result)) }
156 }
157 _ => panic!("string_char_at: expected String and Int on stack"),
158 }
159}
160
161#[unsafe(no_mangle)]
177pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
178 assert!(!stack.is_null(), "string_substring: stack is empty");
179
180 let (stack, len_val) = unsafe { pop(stack) };
181 assert!(
182 !stack.is_null(),
183 "string_substring: need string, start, len"
184 );
185 let (stack, start_val) = unsafe { pop(stack) };
186 assert!(
187 !stack.is_null(),
188 "string_substring: need string, start, len"
189 );
190 let (stack, str_val) = unsafe { pop(stack) };
191
192 match (str_val, start_val, len_val) {
193 (Value::String(s), Value::Int(start), Value::Int(len)) => {
194 let result = if start < 0 || len < 0 {
195 String::new()
196 } else {
197 s.as_str_or_empty()
198 .chars()
199 .skip(start as usize)
200 .take(len as usize)
201 .collect()
202 };
203 unsafe { push(stack, Value::String(global_string(result))) }
204 }
205 _ => panic!("string_substring: expected String, Int, Int on stack"),
206 }
207}
208
209#[unsafe(no_mangle)]
219pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
220 assert!(!stack.is_null(), "char_to_string: stack is empty");
221
222 let (stack, code_point_val) = unsafe { pop(stack) };
223
224 match code_point_val {
225 Value::Int(code_point) => {
226 let result = if !(0..=0x10FFFF).contains(&code_point) {
227 String::new()
229 } else {
230 match char::from_u32(code_point as u32) {
231 Some(c) => c.to_string(),
232 None => String::new(), }
234 };
235 unsafe { push(stack, Value::String(global_string(result))) }
236 }
237 _ => panic!("char_to_string: expected Int on stack"),
238 }
239}
240
241#[unsafe(no_mangle)]
251pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
252 assert!(!stack.is_null(), "string_find: stack is empty");
253
254 let (stack, needle_val) = unsafe { pop(stack) };
255 assert!(!stack.is_null(), "string_find: need string and needle");
256 let (stack, str_val) = unsafe { pop(stack) };
257
258 match (str_val, needle_val) {
259 (Value::String(haystack), Value::String(needle)) => {
260 let haystack_str = haystack.as_str_or_empty();
261 let needle_str = needle.as_str_or_empty();
262
263 let result = match haystack_str.find(needle_str) {
265 Some(byte_pos) => {
266 haystack_str[..byte_pos].chars().count() as i64
268 }
269 None => -1,
270 };
271 unsafe { push(stack, Value::Int(result)) }
272 }
273 _ => panic!("string_find: expected two Strings on stack"),
274 }
275}
276
277#[unsafe(no_mangle)]
284pub unsafe extern "C" fn patch_seq_string_join(stack: Stack) -> Stack {
285 unsafe {
286 let (stack, sep_val) = pop(stack);
288 let sep = match &sep_val {
289 Value::String(s) => s.as_str_or_empty().to_owned(),
290 _ => panic!("string.join: expected String separator, got {:?}", sep_val),
291 };
292
293 let (stack, list_val) = pop(stack);
295 let variant_data = match &list_val {
296 Value::Variant(v) => v,
297 _ => panic!("string.join: expected Variant (list), got {:?}", list_val),
298 };
299
300 let parts: Vec<String> = variant_data
302 .fields
303 .iter()
304 .map(|v| match v {
305 Value::String(s) => s.as_str_or_empty().to_owned(),
306 Value::Int(n) => n.to_string(),
307 Value::Float(f) => f.to_string(),
308 Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
309 Value::Symbol(s) => format!(":{}", s.as_str_or_empty()),
310 _ => format!("{:?}", v),
311 })
312 .collect();
313
314 let result = parts.join(&sep);
315 push(stack, Value::String(global_string(result)))
316 }
317}