1use crate::seqstring::global_string;
14use crate::stack::{Stack, pop, push};
15use crate::value::Value;
16
17#[unsafe(no_mangle)]
26pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
27 use crate::value::VariantData;
28
29 assert!(!stack.is_null(), "string_split: stack is empty");
30
31 let (stack, delim_val) = unsafe { pop(stack) };
32 assert!(!stack.is_null(), "string_split: need two strings");
33 let (stack, str_val) = unsafe { pop(stack) };
34
35 match (str_val, delim_val) {
36 (Value::String(s), Value::String(d)) => {
37 let fields: Vec<Value> = s
39 .as_str()
40 .split(d.as_str())
41 .map(|part| Value::String(global_string(part.to_owned())))
42 .collect();
43
44 let variant = Value::Variant(Box::new(VariantData::new(0, fields)));
46
47 unsafe { push(stack, variant) }
48 }
49 _ => panic!("string_split: expected two strings on stack"),
50 }
51}
52
53#[unsafe(no_mangle)]
60pub unsafe extern "C" fn patch_seq_string_empty(stack: Stack) -> Stack {
61 assert!(!stack.is_null(), "string_empty: stack is empty");
62
63 let (stack, value) = unsafe { pop(stack) };
64
65 match value {
66 Value::String(s) => {
67 let is_empty = s.as_str().is_empty();
68 let result = if is_empty { 1 } else { 0 };
69 unsafe { push(stack, Value::Int(result)) }
70 }
71 _ => panic!("string_empty: expected String on stack"),
72 }
73}
74
75#[unsafe(no_mangle)]
82pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
83 assert!(!stack.is_null(), "string_contains: stack is empty");
84
85 let (stack, substring_val) = unsafe { pop(stack) };
86 assert!(!stack.is_null(), "string_contains: need two strings");
87 let (stack, str_val) = unsafe { pop(stack) };
88
89 match (str_val, substring_val) {
90 (Value::String(s), Value::String(sub)) => {
91 let contains = s.as_str().contains(sub.as_str());
92 let result = if contains { 1 } else { 0 };
93 unsafe { push(stack, Value::Int(result)) }
94 }
95 _ => panic!("string_contains: expected two strings on stack"),
96 }
97}
98
99#[unsafe(no_mangle)]
106pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
107 assert!(!stack.is_null(), "string_starts_with: stack is empty");
108
109 let (stack, prefix_val) = unsafe { pop(stack) };
110 assert!(!stack.is_null(), "string_starts_with: need two strings");
111 let (stack, str_val) = unsafe { pop(stack) };
112
113 match (str_val, prefix_val) {
114 (Value::String(s), Value::String(prefix)) => {
115 let starts = s.as_str().starts_with(prefix.as_str());
116 let result = if starts { 1 } else { 0 };
117 unsafe { push(stack, Value::Int(result)) }
118 }
119 _ => panic!("string_starts_with: expected two strings on stack"),
120 }
121}
122
123#[unsafe(no_mangle)]
130pub unsafe extern "C" fn patch_seq_string_concat(stack: Stack) -> Stack {
131 assert!(!stack.is_null(), "string_concat: stack is empty");
132
133 let (stack, str2_val) = unsafe { pop(stack) };
134 assert!(!stack.is_null(), "string_concat: need two strings");
135 let (stack, str1_val) = unsafe { pop(stack) };
136
137 match (str1_val, str2_val) {
138 (Value::String(s1), Value::String(s2)) => {
139 let result = format!("{}{}", s1.as_str(), s2.as_str());
140 unsafe { push(stack, Value::String(global_string(result))) }
141 }
142 _ => panic!("string_concat: expected two strings on stack"),
143 }
144}
145
146#[unsafe(no_mangle)]
156pub unsafe extern "C" fn patch_seq_string_length(stack: Stack) -> Stack {
157 assert!(!stack.is_null(), "string_length: stack is empty");
158
159 let (stack, str_val) = unsafe { pop(stack) };
160
161 match str_val {
162 Value::String(s) => {
163 let len = s.as_str().chars().count() as i64;
164 unsafe { push(stack, Value::Int(len)) }
165 }
166 _ => panic!("string_length: expected String on stack"),
167 }
168}
169
170#[unsafe(no_mangle)]
179pub unsafe extern "C" fn patch_seq_string_byte_length(stack: Stack) -> Stack {
180 assert!(!stack.is_null(), "string_byte_length: stack is empty");
181
182 let (stack, str_val) = unsafe { pop(stack) };
183
184 match str_val {
185 Value::String(s) => {
186 let len = s.as_str().len() as i64;
187 unsafe { push(stack, Value::Int(len)) }
188 }
189 _ => panic!("string_byte_length: expected String on stack"),
190 }
191}
192
193#[unsafe(no_mangle)]
203pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
204 assert!(!stack.is_null(), "string_char_at: stack is empty");
205
206 let (stack, index_val) = unsafe { pop(stack) };
207 assert!(!stack.is_null(), "string_char_at: need string and index");
208 let (stack, str_val) = unsafe { pop(stack) };
209
210 match (str_val, index_val) {
211 (Value::String(s), Value::Int(index)) => {
212 let result = if index < 0 {
213 -1
214 } else {
215 s.as_str()
216 .chars()
217 .nth(index as usize)
218 .map(|c| c as i64)
219 .unwrap_or(-1)
220 };
221 unsafe { push(stack, Value::Int(result)) }
222 }
223 _ => panic!("string_char_at: expected String and Int on stack"),
224 }
225}
226
227#[unsafe(no_mangle)]
243pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
244 assert!(!stack.is_null(), "string_substring: stack is empty");
245
246 let (stack, len_val) = unsafe { pop(stack) };
247 assert!(
248 !stack.is_null(),
249 "string_substring: need string, start, len"
250 );
251 let (stack, start_val) = unsafe { pop(stack) };
252 assert!(
253 !stack.is_null(),
254 "string_substring: need string, start, len"
255 );
256 let (stack, str_val) = unsafe { pop(stack) };
257
258 match (str_val, start_val, len_val) {
259 (Value::String(s), Value::Int(start), Value::Int(len)) => {
260 let result = if start < 0 || len < 0 {
261 String::new()
262 } else {
263 s.as_str()
264 .chars()
265 .skip(start as usize)
266 .take(len as usize)
267 .collect()
268 };
269 unsafe { push(stack, Value::String(global_string(result))) }
270 }
271 _ => panic!("string_substring: expected String, Int, Int on stack"),
272 }
273}
274
275#[unsafe(no_mangle)]
285pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
286 assert!(!stack.is_null(), "char_to_string: stack is empty");
287
288 let (stack, code_point_val) = unsafe { pop(stack) };
289
290 match code_point_val {
291 Value::Int(code_point) => {
292 let result = if !(0..=0x10FFFF).contains(&code_point) {
293 String::new()
295 } else {
296 match char::from_u32(code_point as u32) {
297 Some(c) => c.to_string(),
298 None => String::new(), }
300 };
301 unsafe { push(stack, Value::String(global_string(result))) }
302 }
303 _ => panic!("char_to_string: expected Int on stack"),
304 }
305}
306
307#[unsafe(no_mangle)]
317pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
318 assert!(!stack.is_null(), "string_find: stack is empty");
319
320 let (stack, needle_val) = unsafe { pop(stack) };
321 assert!(!stack.is_null(), "string_find: need string and needle");
322 let (stack, str_val) = unsafe { pop(stack) };
323
324 match (str_val, needle_val) {
325 (Value::String(haystack), Value::String(needle)) => {
326 let haystack_str = haystack.as_str();
327 let needle_str = needle.as_str();
328
329 let result = match haystack_str.find(needle_str) {
331 Some(byte_pos) => {
332 haystack_str[..byte_pos].chars().count() as i64
334 }
335 None => -1,
336 };
337 unsafe { push(stack, Value::Int(result)) }
338 }
339 _ => panic!("string_find: expected two Strings on stack"),
340 }
341}
342
343#[unsafe(no_mangle)]
350pub unsafe extern "C" fn patch_seq_string_trim(stack: Stack) -> Stack {
351 assert!(!stack.is_null(), "string_trim: stack is empty");
352
353 let (stack, str_val) = unsafe { pop(stack) };
354
355 match str_val {
356 Value::String(s) => {
357 let trimmed = s.as_str().trim();
358 unsafe { push(stack, Value::String(global_string(trimmed.to_owned()))) }
359 }
360 _ => panic!("string_trim: expected String on stack"),
361 }
362}
363
364#[unsafe(no_mangle)]
371pub unsafe extern "C" fn patch_seq_string_to_upper(stack: Stack) -> Stack {
372 assert!(!stack.is_null(), "string_to_upper: stack is empty");
373
374 let (stack, str_val) = unsafe { pop(stack) };
375
376 match str_val {
377 Value::String(s) => {
378 let upper = s.as_str().to_uppercase();
379 unsafe { push(stack, Value::String(global_string(upper))) }
380 }
381 _ => panic!("string_to_upper: expected String on stack"),
382 }
383}
384
385#[unsafe(no_mangle)]
392pub unsafe extern "C" fn patch_seq_string_to_lower(stack: Stack) -> Stack {
393 assert!(!stack.is_null(), "string_to_lower: stack is empty");
394
395 let (stack, str_val) = unsafe { pop(stack) };
396
397 match str_val {
398 Value::String(s) => {
399 let lower = s.as_str().to_lowercase();
400 unsafe { push(stack, Value::String(global_string(lower))) }
401 }
402 _ => panic!("string_to_lower: expected String on stack"),
403 }
404}
405
406#[unsafe(no_mangle)]
413pub unsafe extern "C" fn patch_seq_string_equal(stack: Stack) -> Stack {
414 assert!(!stack.is_null(), "string_equal: stack is empty");
415
416 let (stack, str2_val) = unsafe { pop(stack) };
417 assert!(!stack.is_null(), "string_equal: need two strings");
418 let (stack, str1_val) = unsafe { pop(stack) };
419
420 match (str1_val, str2_val) {
421 (Value::String(s1), Value::String(s2)) => {
422 let equal = s1.as_str() == s2.as_str();
423 let result = if equal { 1 } else { 0 };
424 unsafe { push(stack, Value::Int(result)) }
425 }
426 _ => panic!("string_equal: expected two strings on stack"),
427 }
428}
429
430pub use patch_seq_char_to_string as char_to_string;
432pub use patch_seq_string_byte_length as string_byte_length;
433pub use patch_seq_string_char_at as string_char_at;
434pub use patch_seq_string_concat as string_concat;
435pub use patch_seq_string_contains as string_contains;
436pub use patch_seq_string_empty as string_empty;
437pub use patch_seq_string_equal as string_equal;
438pub use patch_seq_string_find as string_find;
439pub use patch_seq_string_length as string_length;
440pub use patch_seq_string_split as string_split;
441pub use patch_seq_string_starts_with as string_starts_with;
442pub use patch_seq_string_substring as string_substring;
443pub use patch_seq_string_to_lower as string_to_lower;
444pub use patch_seq_string_to_upper as string_to_upper;
445pub use patch_seq_string_trim as string_trim;
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
452 fn test_string_split_simple() {
453 unsafe {
454 let stack = std::ptr::null_mut();
455 let stack = push(stack, Value::String(global_string("a b c".to_owned())));
456 let stack = push(stack, Value::String(global_string(" ".to_owned())));
457
458 let stack = string_split(stack);
459
460 let (stack, result) = pop(stack);
462 match result {
463 Value::Variant(v) => {
464 assert_eq!(v.tag, 0);
465 assert_eq!(v.fields.len(), 3);
466 assert_eq!(v.fields[0], Value::String(global_string("a".to_owned())));
467 assert_eq!(v.fields[1], Value::String(global_string("b".to_owned())));
468 assert_eq!(v.fields[2], Value::String(global_string("c".to_owned())));
469 }
470 _ => panic!("Expected Variant, got {:?}", result),
471 }
472
473 assert!(stack.is_null());
474 }
475 }
476
477 #[test]
478 fn test_string_split_empty() {
479 unsafe {
480 let stack = std::ptr::null_mut();
481 let stack = push(stack, Value::String(global_string("".to_owned())));
482 let stack = push(stack, Value::String(global_string(" ".to_owned())));
483
484 let stack = string_split(stack);
485
486 let (stack, result) = pop(stack);
488 match result {
489 Value::Variant(v) => {
490 assert_eq!(v.tag, 0);
491 assert_eq!(v.fields.len(), 1);
492 assert_eq!(v.fields[0], Value::String(global_string("".to_owned())));
493 }
494 _ => panic!("Expected Variant, got {:?}", result),
495 }
496
497 assert!(stack.is_null());
498 }
499 }
500
501 #[test]
502 fn test_string_empty_true() {
503 unsafe {
504 let stack = std::ptr::null_mut();
505 let stack = push(stack, Value::String(global_string("".to_owned())));
506
507 let stack = string_empty(stack);
508
509 let (stack, result) = pop(stack);
510 assert_eq!(result, Value::Int(1));
511 assert!(stack.is_null());
512 }
513 }
514
515 #[test]
516 fn test_string_empty_false() {
517 unsafe {
518 let stack = std::ptr::null_mut();
519 let stack = push(stack, Value::String(global_string("hello".to_owned())));
520
521 let stack = string_empty(stack);
522
523 let (stack, result) = pop(stack);
524 assert_eq!(result, Value::Int(0));
525 assert!(stack.is_null());
526 }
527 }
528
529 #[test]
530 fn test_string_contains_true() {
531 unsafe {
532 let stack = std::ptr::null_mut();
533 let stack = push(
534 stack,
535 Value::String(global_string("hello world".to_owned())),
536 );
537 let stack = push(stack, Value::String(global_string("world".to_owned())));
538
539 let stack = string_contains(stack);
540
541 let (stack, result) = pop(stack);
542 assert_eq!(result, Value::Int(1));
543 assert!(stack.is_null());
544 }
545 }
546
547 #[test]
548 fn test_string_contains_false() {
549 unsafe {
550 let stack = std::ptr::null_mut();
551 let stack = push(
552 stack,
553 Value::String(global_string("hello world".to_owned())),
554 );
555 let stack = push(stack, Value::String(global_string("foo".to_owned())));
556
557 let stack = string_contains(stack);
558
559 let (stack, result) = pop(stack);
560 assert_eq!(result, Value::Int(0));
561 assert!(stack.is_null());
562 }
563 }
564
565 #[test]
566 fn test_string_starts_with_true() {
567 unsafe {
568 let stack = std::ptr::null_mut();
569 let stack = push(
570 stack,
571 Value::String(global_string("hello world".to_owned())),
572 );
573 let stack = push(stack, Value::String(global_string("hello".to_owned())));
574
575 let stack = string_starts_with(stack);
576
577 let (stack, result) = pop(stack);
578 assert_eq!(result, Value::Int(1));
579 assert!(stack.is_null());
580 }
581 }
582
583 #[test]
584 fn test_string_starts_with_false() {
585 unsafe {
586 let stack = std::ptr::null_mut();
587 let stack = push(
588 stack,
589 Value::String(global_string("hello world".to_owned())),
590 );
591 let stack = push(stack, Value::String(global_string("world".to_owned())));
592
593 let stack = string_starts_with(stack);
594
595 let (stack, result) = pop(stack);
596 assert_eq!(result, Value::Int(0));
597 assert!(stack.is_null());
598 }
599 }
600
601 #[test]
602 fn test_http_request_line_parsing() {
603 unsafe {
605 let stack = std::ptr::null_mut();
606 let stack = push(
607 stack,
608 Value::String(global_string("GET /api/users HTTP/1.1".to_owned())),
609 );
610 let stack = push(stack, Value::String(global_string(" ".to_owned())));
611
612 let stack = string_split(stack);
613
614 let (stack, result) = pop(stack);
616 match result {
617 Value::Variant(v) => {
618 assert_eq!(v.tag, 0);
619 assert_eq!(v.fields.len(), 3);
620 assert_eq!(v.fields[0], Value::String(global_string("GET".to_owned())));
621 assert_eq!(
622 v.fields[1],
623 Value::String(global_string("/api/users".to_owned()))
624 );
625 assert_eq!(
626 v.fields[2],
627 Value::String(global_string("HTTP/1.1".to_owned()))
628 );
629 }
630 _ => panic!("Expected Variant, got {:?}", result),
631 }
632
633 assert!(stack.is_null());
634 }
635 }
636
637 #[test]
638 fn test_path_routing() {
639 unsafe {
641 let stack = std::ptr::null_mut();
642 let stack = push(stack, Value::String(global_string("/api/users".to_owned())));
643 let stack = push(stack, Value::String(global_string("/api/".to_owned())));
644
645 let stack = string_starts_with(stack);
646
647 let (stack, result) = pop(stack);
648 assert_eq!(result, Value::Int(1));
649 assert!(stack.is_null());
650 }
651 }
652
653 #[test]
654 fn test_string_concat() {
655 unsafe {
656 let stack = std::ptr::null_mut();
657 let stack = push(stack, Value::String(global_string("Hello, ".to_owned())));
658 let stack = push(stack, Value::String(global_string("World!".to_owned())));
659
660 let stack = string_concat(stack);
661
662 let (stack, result) = pop(stack);
663 assert_eq!(
664 result,
665 Value::String(global_string("Hello, World!".to_owned()))
666 );
667 assert!(stack.is_null());
668 }
669 }
670
671 #[test]
672 fn test_string_length() {
673 unsafe {
674 let stack = std::ptr::null_mut();
675 let stack = push(stack, Value::String(global_string("Hello".to_owned())));
676
677 let stack = string_length(stack);
678
679 let (stack, result) = pop(stack);
680 assert_eq!(result, Value::Int(5));
681 assert!(stack.is_null());
682 }
683 }
684
685 #[test]
686 fn test_string_length_empty() {
687 unsafe {
688 let stack = std::ptr::null_mut();
689 let stack = push(stack, Value::String(global_string("".to_owned())));
690
691 let stack = string_length(stack);
692
693 let (stack, result) = pop(stack);
694 assert_eq!(result, Value::Int(0));
695 assert!(stack.is_null());
696 }
697 }
698
699 #[test]
700 fn test_string_trim() {
701 unsafe {
702 let stack = std::ptr::null_mut();
703 let stack = push(
704 stack,
705 Value::String(global_string(" Hello, World! ".to_owned())),
706 );
707
708 let stack = string_trim(stack);
709
710 let (stack, result) = pop(stack);
711 assert_eq!(
712 result,
713 Value::String(global_string("Hello, World!".to_owned()))
714 );
715 assert!(stack.is_null());
716 }
717 }
718
719 #[test]
720 fn test_string_to_upper() {
721 unsafe {
722 let stack = std::ptr::null_mut();
723 let stack = push(
724 stack,
725 Value::String(global_string("Hello, World!".to_owned())),
726 );
727
728 let stack = string_to_upper(stack);
729
730 let (stack, result) = pop(stack);
731 assert_eq!(
732 result,
733 Value::String(global_string("HELLO, WORLD!".to_owned()))
734 );
735 assert!(stack.is_null());
736 }
737 }
738
739 #[test]
740 fn test_string_to_lower() {
741 unsafe {
742 let stack = std::ptr::null_mut();
743 let stack = push(
744 stack,
745 Value::String(global_string("Hello, World!".to_owned())),
746 );
747
748 let stack = string_to_lower(stack);
749
750 let (stack, result) = pop(stack);
751 assert_eq!(
752 result,
753 Value::String(global_string("hello, world!".to_owned()))
754 );
755 assert!(stack.is_null());
756 }
757 }
758
759 #[test]
760 fn test_http_header_content_length() {
761 unsafe {
763 let stack = std::ptr::null_mut();
764 let stack = push(
765 stack,
766 Value::String(global_string("Content-Length: ".to_owned())),
767 );
768 let stack = push(stack, Value::String(global_string("42".to_owned())));
769
770 let stack = string_concat(stack);
771
772 let (stack, result) = pop(stack);
773 assert_eq!(
774 result,
775 Value::String(global_string("Content-Length: 42".to_owned()))
776 );
777 assert!(stack.is_null());
778 }
779 }
780
781 #[test]
782 fn test_string_equal_true() {
783 unsafe {
784 let stack = std::ptr::null_mut();
785 let stack = push(stack, Value::String(global_string("hello".to_owned())));
786 let stack = push(stack, Value::String(global_string("hello".to_owned())));
787
788 let stack = string_equal(stack);
789
790 let (stack, result) = pop(stack);
791 assert_eq!(result, Value::Int(1));
792 assert!(stack.is_null());
793 }
794 }
795
796 #[test]
797 fn test_string_equal_false() {
798 unsafe {
799 let stack = std::ptr::null_mut();
800 let stack = push(stack, Value::String(global_string("hello".to_owned())));
801 let stack = push(stack, Value::String(global_string("world".to_owned())));
802
803 let stack = string_equal(stack);
804
805 let (stack, result) = pop(stack);
806 assert_eq!(result, Value::Int(0));
807 assert!(stack.is_null());
808 }
809 }
810
811 #[test]
812 fn test_string_equal_empty_strings() {
813 unsafe {
814 let stack = std::ptr::null_mut();
815 let stack = push(stack, Value::String(global_string("".to_owned())));
816 let stack = push(stack, Value::String(global_string("".to_owned())));
817
818 let stack = string_equal(stack);
819
820 let (stack, result) = pop(stack);
821 assert_eq!(result, Value::Int(1));
822 assert!(stack.is_null());
823 }
824 }
825
826 #[test]
829 fn test_string_length_utf8() {
830 unsafe {
832 let stack = std::ptr::null_mut();
833 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
834
835 let stack = string_length(stack);
836
837 let (stack, result) = pop(stack);
838 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
840 }
841 }
842
843 #[test]
844 fn test_string_length_emoji() {
845 unsafe {
847 let stack = std::ptr::null_mut();
848 let stack = push(stack, Value::String(global_string("hi🎉".to_owned())));
849
850 let stack = string_length(stack);
851
852 let (stack, result) = pop(stack);
853 assert_eq!(result, Value::Int(3)); assert!(stack.is_null());
855 }
856 }
857
858 #[test]
859 fn test_string_byte_length_ascii() {
860 unsafe {
861 let stack = std::ptr::null_mut();
862 let stack = push(stack, Value::String(global_string("hello".to_owned())));
863
864 let stack = string_byte_length(stack);
865
866 let (stack, result) = pop(stack);
867 assert_eq!(result, Value::Int(5)); assert!(stack.is_null());
869 }
870 }
871
872 #[test]
873 fn test_string_byte_length_utf8() {
874 unsafe {
876 let stack = std::ptr::null_mut();
877 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
878
879 let stack = string_byte_length(stack);
880
881 let (stack, result) = pop(stack);
882 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
884 }
885 }
886
887 #[test]
888 fn test_string_char_at_ascii() {
889 unsafe {
890 let stack = std::ptr::null_mut();
891 let stack = push(stack, Value::String(global_string("hello".to_owned())));
892 let stack = push(stack, Value::Int(0));
893
894 let stack = string_char_at(stack);
895
896 let (stack, result) = pop(stack);
897 assert_eq!(result, Value::Int(104)); assert!(stack.is_null());
899 }
900 }
901
902 #[test]
903 fn test_string_char_at_utf8() {
904 unsafe {
906 let stack = std::ptr::null_mut();
907 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
908 let stack = push(stack, Value::Int(1));
909
910 let stack = string_char_at(stack);
911
912 let (stack, result) = pop(stack);
913 assert_eq!(result, Value::Int(233)); assert!(stack.is_null());
915 }
916 }
917
918 #[test]
919 fn test_string_char_at_out_of_bounds() {
920 unsafe {
921 let stack = std::ptr::null_mut();
922 let stack = push(stack, Value::String(global_string("hello".to_owned())));
923 let stack = push(stack, Value::Int(10)); let stack = string_char_at(stack);
926
927 let (stack, result) = pop(stack);
928 assert_eq!(result, Value::Int(-1));
929 assert!(stack.is_null());
930 }
931 }
932
933 #[test]
934 fn test_string_char_at_negative() {
935 unsafe {
936 let stack = std::ptr::null_mut();
937 let stack = push(stack, Value::String(global_string("hello".to_owned())));
938 let stack = push(stack, Value::Int(-1));
939
940 let stack = string_char_at(stack);
941
942 let (stack, result) = pop(stack);
943 assert_eq!(result, Value::Int(-1));
944 assert!(stack.is_null());
945 }
946 }
947
948 #[test]
949 fn test_string_substring_simple() {
950 unsafe {
951 let stack = std::ptr::null_mut();
952 let stack = push(stack, Value::String(global_string("hello".to_owned())));
953 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
957
958 let (stack, result) = pop(stack);
959 assert_eq!(result, Value::String(global_string("ell".to_owned())));
960 assert!(stack.is_null());
961 }
962 }
963
964 #[test]
965 fn test_string_substring_utf8() {
966 unsafe {
968 let stack = std::ptr::null_mut();
969 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
970 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
974
975 let (stack, result) = pop(stack);
976 assert_eq!(result, Value::String(global_string("éll".to_owned())));
977 assert!(stack.is_null());
978 }
979 }
980
981 #[test]
982 fn test_string_substring_clamp() {
983 unsafe {
985 let stack = std::ptr::null_mut();
986 let stack = push(stack, Value::String(global_string("hello".to_owned())));
987 let stack = push(stack, Value::Int(2)); let stack = push(stack, Value::Int(100)); let stack = string_substring(stack);
991
992 let (stack, result) = pop(stack);
993 assert_eq!(result, Value::String(global_string("llo".to_owned())));
994 assert!(stack.is_null());
995 }
996 }
997
998 #[test]
999 fn test_string_substring_beyond_end() {
1000 unsafe {
1002 let stack = std::ptr::null_mut();
1003 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1004 let stack = push(stack, Value::Int(10)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1008
1009 let (stack, result) = pop(stack);
1010 assert_eq!(result, Value::String(global_string("".to_owned())));
1011 assert!(stack.is_null());
1012 }
1013 }
1014
1015 #[test]
1016 fn test_char_to_string_ascii() {
1017 unsafe {
1018 let stack = std::ptr::null_mut();
1019 let stack = push(stack, Value::Int(65)); let stack = char_to_string(stack);
1022
1023 let (stack, result) = pop(stack);
1024 assert_eq!(result, Value::String(global_string("A".to_owned())));
1025 assert!(stack.is_null());
1026 }
1027 }
1028
1029 #[test]
1030 fn test_char_to_string_utf8() {
1031 unsafe {
1032 let stack = std::ptr::null_mut();
1033 let stack = push(stack, Value::Int(233)); let stack = char_to_string(stack);
1036
1037 let (stack, result) = pop(stack);
1038 assert_eq!(result, Value::String(global_string("é".to_owned())));
1039 assert!(stack.is_null());
1040 }
1041 }
1042
1043 #[test]
1044 fn test_char_to_string_newline() {
1045 unsafe {
1046 let stack = std::ptr::null_mut();
1047 let stack = push(stack, Value::Int(10)); let stack = char_to_string(stack);
1050
1051 let (stack, result) = pop(stack);
1052 assert_eq!(result, Value::String(global_string("\n".to_owned())));
1053 assert!(stack.is_null());
1054 }
1055 }
1056
1057 #[test]
1058 fn test_char_to_string_invalid() {
1059 unsafe {
1060 let stack = std::ptr::null_mut();
1061 let stack = push(stack, Value::Int(-1)); let stack = char_to_string(stack);
1064
1065 let (stack, result) = pop(stack);
1066 assert_eq!(result, Value::String(global_string("".to_owned())));
1067 assert!(stack.is_null());
1068 }
1069 }
1070
1071 #[test]
1072 fn test_string_find_found() {
1073 unsafe {
1074 let stack = std::ptr::null_mut();
1075 let stack = push(
1076 stack,
1077 Value::String(global_string("hello world".to_owned())),
1078 );
1079 let stack = push(stack, Value::String(global_string("world".to_owned())));
1080
1081 let stack = string_find(stack);
1082
1083 let (stack, result) = pop(stack);
1084 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1086 }
1087 }
1088
1089 #[test]
1090 fn test_string_find_not_found() {
1091 unsafe {
1092 let stack = std::ptr::null_mut();
1093 let stack = push(
1094 stack,
1095 Value::String(global_string("hello world".to_owned())),
1096 );
1097 let stack = push(stack, Value::String(global_string("xyz".to_owned())));
1098
1099 let stack = string_find(stack);
1100
1101 let (stack, result) = pop(stack);
1102 assert_eq!(result, Value::Int(-1));
1103 assert!(stack.is_null());
1104 }
1105 }
1106
1107 #[test]
1108 fn test_string_find_first_match() {
1109 unsafe {
1111 let stack = std::ptr::null_mut();
1112 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1113 let stack = push(stack, Value::String(global_string("l".to_owned())));
1114
1115 let stack = string_find(stack);
1116
1117 let (stack, result) = pop(stack);
1118 assert_eq!(result, Value::Int(2)); assert!(stack.is_null());
1120 }
1121 }
1122
1123 #[test]
1124 fn test_string_find_utf8() {
1125 unsafe {
1127 let stack = std::ptr::null_mut();
1128 let stack = push(
1129 stack,
1130 Value::String(global_string("héllo wörld".to_owned())),
1131 );
1132 let stack = push(stack, Value::String(global_string("wörld".to_owned())));
1133
1134 let stack = string_find(stack);
1135
1136 let (stack, result) = pop(stack);
1137 assert_eq!(result, Value::Int(6)); assert!(stack.is_null());
1139 }
1140 }
1141}