Skip to main content

seq_runtime/string_ops/
case.rs

1//! Case conversion and whitespace trimming.
2
3use crate::seqstring::{global_bytes, global_string};
4use crate::stack::{Stack, pop, push};
5use crate::value::Value;
6
7/// # Safety
8/// Stack must have the expected values on top for this operation.
9#[unsafe(no_mangle)]
10pub unsafe extern "C" fn patch_seq_string_trim(stack: Stack) -> Stack {
11    assert!(!stack.is_null(), "string_trim: stack is empty");
12
13    let (stack, str_val) = unsafe { pop(stack) };
14
15    match str_val {
16        Value::String(s) => {
17            let trimmed = s.as_str_or_empty().trim();
18            unsafe { push(stack, Value::String(global_string(trimmed.to_owned()))) }
19        }
20        _ => panic!("string_trim: expected String on stack"),
21    }
22}
23
24/// Convert a string to uppercase
25///
26/// Stack effect: ( str -- upper )
27///
28/// # Safety
29/// Stack must have a String value on top
30#[unsafe(no_mangle)]
31pub unsafe extern "C" fn patch_seq_string_to_upper(stack: Stack) -> Stack {
32    assert!(!stack.is_null(), "string_to_upper: stack is empty");
33
34    let (stack, str_val) = unsafe { pop(stack) };
35
36    match str_val {
37        Value::String(s) => {
38            let upper = s.as_str_or_empty().to_uppercase();
39            unsafe { push(stack, Value::String(global_string(upper))) }
40        }
41        _ => panic!("string_to_upper: expected String on stack"),
42    }
43}
44
45/// Convert a string to lowercase
46///
47/// Stack effect: ( str -- lower )
48///
49/// # Safety
50/// Stack must have a String value on top
51#[unsafe(no_mangle)]
52pub unsafe extern "C" fn patch_seq_string_to_lower(stack: Stack) -> Stack {
53    assert!(!stack.is_null(), "string_to_lower: stack is empty");
54
55    let (stack, str_val) = unsafe { pop(stack) };
56
57    match str_val {
58        Value::String(s) => {
59            let lower = s.as_str_or_empty().to_lowercase();
60            unsafe { push(stack, Value::String(global_string(lower))) }
61        }
62        _ => panic!("string_to_lower: expected String on stack"),
63    }
64}
65
66/// Check if two strings are equal
67///
68/// Stack effect: ( str1 str2 -- bool )
69///
70/// # Safety
71/// Stack must have two String values on top
72#[unsafe(no_mangle)]
73pub unsafe extern "C" fn patch_seq_string_chomp(stack: Stack) -> Stack {
74    assert!(!stack.is_null(), "string_chomp: stack is empty");
75
76    let (stack, str_val) = unsafe { pop(stack) };
77
78    match str_val {
79        Value::String(s) => {
80            // Byte-clean: peel a trailing `\n` (and its preceding
81            // `\r`, for CRLF line endings). All comparisons against
82            // ASCII bytes — works on text and on binary content with
83            // a trailing newline.
84            let mut bytes = s.as_bytes().to_vec();
85            if bytes.last() == Some(&b'\n') {
86                bytes.pop();
87                if bytes.last() == Some(&b'\r') {
88                    bytes.pop();
89                }
90            }
91            unsafe { push(stack, Value::String(global_bytes(bytes))) }
92        }
93        _ => panic!("string_chomp: expected String on stack"),
94    }
95}