1use crate::stack::{Stack, pop, push};
27use crate::value::{Value, VariantData};
28use std::fs::{self, File, OpenOptions};
29use std::io::{BufRead, BufReader, Write};
30use std::path::Path;
31use std::sync::Arc;
32
33#[unsafe(no_mangle)]
46pub unsafe extern "C" fn patch_seq_file_slurp(stack: Stack) -> Stack {
47 assert!(!stack.is_null(), "file-slurp: stack is empty");
48
49 let (rest, value) = unsafe { pop(stack) };
50
51 match value {
52 Value::String(path) => match fs::read_to_string(path.as_str()) {
53 Ok(contents) => {
54 let stack = unsafe { push(rest, Value::String(contents.into())) };
55 unsafe { push(stack, Value::Bool(true)) }
56 }
57 Err(_) => {
58 let stack = unsafe { push(rest, Value::String("".into())) };
59 unsafe { push(stack, Value::Bool(false)) }
60 }
61 },
62 _ => panic!("file-slurp: expected String path on stack, got {:?}", value),
63 }
64}
65
66#[unsafe(no_mangle)]
76pub unsafe extern "C" fn patch_seq_file_exists(stack: Stack) -> Stack {
77 assert!(!stack.is_null(), "file-exists?: stack is empty");
78
79 let (rest, value) = unsafe { pop(stack) };
80
81 match value {
82 Value::String(path) => {
83 let exists = Path::new(path.as_str()).exists();
84 unsafe { push(rest, Value::Bool(exists)) }
85 }
86 _ => panic!(
87 "file-exists?: expected String path on stack, got {:?}",
88 value
89 ),
90 }
91}
92
93#[unsafe(no_mangle)]
129pub unsafe extern "C" fn patch_seq_file_for_each_line_plus(stack: Stack) -> Stack {
130 assert!(!stack.is_null(), "file-for-each-line+: stack is empty");
131
132 let (stack, quot_value) = unsafe { pop(stack) };
134
135 let (stack, path_value) = unsafe { pop(stack) };
137 let path = match path_value {
138 Value::String(s) => s,
139 _ => panic!(
140 "file-for-each-line+: expected String path, got {:?}",
141 path_value
142 ),
143 };
144
145 let file = match File::open(path.as_str()) {
147 Ok(f) => f,
148 Err(e) => {
149 let stack = unsafe { push(stack, Value::String(e.to_string().into())) };
151 return unsafe { push(stack, Value::Int(0)) };
152 }
153 };
154
155 let (wrapper, env_data, env_len): (usize, *const Value, usize) = match quot_value {
157 Value::Quotation { wrapper, .. } => {
158 if wrapper == 0 {
159 panic!("file-for-each-line+: quotation wrapper function pointer is null");
160 }
161 (wrapper, std::ptr::null(), 0)
162 }
163 Value::Closure { fn_ptr, ref env } => {
164 if fn_ptr == 0 {
165 panic!("file-for-each-line+: closure function pointer is null");
166 }
167 (fn_ptr, env.as_ptr(), env.len())
168 }
169 _ => panic!(
170 "file-for-each-line+: expected Quotation or Closure, got {:?}",
171 quot_value
172 ),
173 };
174
175 let reader = BufReader::new(file);
177 let mut current_stack = stack;
178
179 for line_result in reader.lines() {
180 match line_result {
181 Ok(mut line_str) => {
182 line_str.push('\n');
185
186 current_stack = unsafe { push(current_stack, Value::String(line_str.into())) };
188
189 if env_data.is_null() {
191 let fn_ref: unsafe extern "C" fn(Stack) -> Stack =
193 unsafe { std::mem::transmute(wrapper) };
194 current_stack = unsafe { fn_ref(current_stack) };
195 } else {
196 let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
198 unsafe { std::mem::transmute(wrapper) };
199 current_stack = unsafe { fn_ref(current_stack, env_data, env_len) };
200 }
201
202 may::coroutine::yield_now();
204 }
205 Err(e) => {
206 let stack = unsafe { push(current_stack, Value::String(e.to_string().into())) };
208 return unsafe { push(stack, Value::Bool(false)) };
209 }
210 }
211 }
212
213 let stack = unsafe { push(current_stack, Value::String("".into())) };
215 unsafe { push(stack, Value::Bool(true)) }
216}
217
218#[unsafe(no_mangle)]
230pub unsafe extern "C" fn patch_seq_file_spit(stack: Stack) -> Stack {
231 assert!(!stack.is_null(), "file.spit: stack is empty");
232
233 let (stack, path_value) = unsafe { pop(stack) };
235 let path = match path_value {
236 Value::String(s) => s,
237 _ => panic!("file.spit: expected String path, got {:?}", path_value),
238 };
239
240 let (stack, content_value) = unsafe { pop(stack) };
242 let content = match content_value {
243 Value::String(s) => s,
244 _ => panic!(
245 "file.spit: expected String content, got {:?}",
246 content_value
247 ),
248 };
249
250 match fs::write(path.as_str(), content.as_str()) {
251 Ok(()) => unsafe { push(stack, Value::Bool(true)) },
252 Err(_) => unsafe { push(stack, Value::Bool(false)) },
253 }
254}
255
256#[unsafe(no_mangle)]
268pub unsafe extern "C" fn patch_seq_file_append(stack: Stack) -> Stack {
269 assert!(!stack.is_null(), "file.append: stack is empty");
270
271 let (stack, path_value) = unsafe { pop(stack) };
273 let path = match path_value {
274 Value::String(s) => s,
275 _ => panic!("file.append: expected String path, got {:?}", path_value),
276 };
277
278 let (stack, content_value) = unsafe { pop(stack) };
280 let content = match content_value {
281 Value::String(s) => s,
282 _ => panic!(
283 "file.append: expected String content, got {:?}",
284 content_value
285 ),
286 };
287
288 let result = OpenOptions::new()
289 .create(true)
290 .append(true)
291 .open(path.as_str())
292 .and_then(|mut file| file.write_all(content.as_str().as_bytes()));
293
294 match result {
295 Ok(()) => unsafe { push(stack, Value::Bool(true)) },
296 Err(_) => unsafe { push(stack, Value::Bool(false)) },
297 }
298}
299
300#[unsafe(no_mangle)]
311pub unsafe extern "C" fn patch_seq_file_delete(stack: Stack) -> Stack {
312 assert!(!stack.is_null(), "file.delete: stack is empty");
313
314 let (stack, path_value) = unsafe { pop(stack) };
315 let path = match path_value {
316 Value::String(s) => s,
317 _ => panic!("file.delete: expected String path, got {:?}", path_value),
318 };
319
320 match fs::remove_file(path.as_str()) {
321 Ok(()) => unsafe { push(stack, Value::Bool(true)) },
322 Err(_) => unsafe { push(stack, Value::Bool(false)) },
323 }
324}
325
326#[unsafe(no_mangle)]
337pub unsafe extern "C" fn patch_seq_file_size(stack: Stack) -> Stack {
338 assert!(!stack.is_null(), "file.size: stack is empty");
339
340 let (stack, path_value) = unsafe { pop(stack) };
341 let path = match path_value {
342 Value::String(s) => s,
343 _ => panic!("file.size: expected String path, got {:?}", path_value),
344 };
345
346 match fs::metadata(path.as_str()) {
347 Ok(metadata) => {
348 let size = metadata.len() as i64;
349 let stack = unsafe { push(stack, Value::Int(size)) };
350 unsafe { push(stack, Value::Bool(true)) }
351 }
352 Err(_) => {
353 let stack = unsafe { push(stack, Value::Int(0)) };
354 unsafe { push(stack, Value::Bool(false)) }
355 }
356 }
357}
358
359#[unsafe(no_mangle)]
373pub unsafe extern "C" fn patch_seq_dir_exists(stack: Stack) -> Stack {
374 assert!(!stack.is_null(), "dir.exists?: stack is empty");
375
376 let (stack, path_value) = unsafe { pop(stack) };
377 let path = match path_value {
378 Value::String(s) => s,
379 _ => panic!("dir.exists?: expected String path, got {:?}", path_value),
380 };
381
382 let exists = Path::new(path.as_str()).is_dir();
383 unsafe { push(stack, Value::Bool(exists)) }
384}
385
386#[unsafe(no_mangle)]
397pub unsafe extern "C" fn patch_seq_dir_make(stack: Stack) -> Stack {
398 assert!(!stack.is_null(), "dir.make: stack is empty");
399
400 let (stack, path_value) = unsafe { pop(stack) };
401 let path = match path_value {
402 Value::String(s) => s,
403 _ => panic!("dir.make: expected String path, got {:?}", path_value),
404 };
405
406 match fs::create_dir_all(path.as_str()) {
407 Ok(()) => unsafe { push(stack, Value::Bool(true)) },
408 Err(_) => unsafe { push(stack, Value::Bool(false)) },
409 }
410}
411
412#[unsafe(no_mangle)]
423pub unsafe extern "C" fn patch_seq_dir_delete(stack: Stack) -> Stack {
424 assert!(!stack.is_null(), "dir.delete: stack is empty");
425
426 let (stack, path_value) = unsafe { pop(stack) };
427 let path = match path_value {
428 Value::String(s) => s,
429 _ => panic!("dir.delete: expected String path, got {:?}", path_value),
430 };
431
432 match fs::remove_dir(path.as_str()) {
433 Ok(()) => unsafe { push(stack, Value::Bool(true)) },
434 Err(_) => unsafe { push(stack, Value::Bool(false)) },
435 }
436}
437
438#[unsafe(no_mangle)]
449pub unsafe extern "C" fn patch_seq_dir_list(stack: Stack) -> Stack {
450 assert!(!stack.is_null(), "dir.list: stack is empty");
451
452 let (stack, path_value) = unsafe { pop(stack) };
453 let path = match path_value {
454 Value::String(s) => s,
455 _ => panic!("dir.list: expected String path, got {:?}", path_value),
456 };
457
458 match fs::read_dir(path.as_str()) {
459 Ok(entries) => {
460 let mut names: Vec<Value> = Vec::new();
461 for entry in entries.flatten() {
462 if let Some(name) = entry.file_name().to_str() {
463 names.push(Value::String(name.to_string().into()));
464 }
465 }
466 let list = Value::Variant(Arc::new(VariantData::new(
467 crate::seqstring::global_string("List".to_string()),
468 names,
469 )));
470 let stack = unsafe { push(stack, list) };
471 unsafe { push(stack, Value::Bool(true)) }
472 }
473 Err(_) => {
474 let empty_list = Value::Variant(Arc::new(VariantData::new(
475 crate::seqstring::global_string("List".to_string()),
476 vec![],
477 )));
478 let stack = unsafe { push(stack, empty_list) };
479 unsafe { push(stack, Value::Bool(false)) }
480 }
481 }
482}
483
484pub use patch_seq_dir_delete as dir_delete;
486pub use patch_seq_dir_exists as dir_exists;
487pub use patch_seq_dir_list as dir_list;
488pub use patch_seq_dir_make as dir_make;
489pub use patch_seq_file_append as file_append;
490pub use patch_seq_file_delete as file_delete;
491pub use patch_seq_file_exists as file_exists;
492pub use patch_seq_file_for_each_line_plus as file_for_each_line_plus;
493pub use patch_seq_file_size as file_size;
494pub use patch_seq_file_slurp as file_slurp;
495pub use patch_seq_file_spit as file_spit;
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use std::io::Write;
501 use tempfile::NamedTempFile;
502
503 #[test]
504 fn test_file_slurp() {
505 let mut temp_file = NamedTempFile::new().unwrap();
507 writeln!(temp_file, "Hello, file!").unwrap();
508 let path = temp_file.path().to_str().unwrap().to_string();
509
510 unsafe {
511 let stack = crate::stack::alloc_test_stack();
512 let stack = push(stack, Value::String(path.into()));
513 let stack = patch_seq_file_slurp(stack);
514
515 let (stack, success) = pop(stack);
517 assert_eq!(success, Value::Bool(true));
518 let (_stack, value) = pop(stack);
519 match value {
520 Value::String(s) => assert_eq!(s.as_str().trim(), "Hello, file!"),
521 _ => panic!("Expected String"),
522 }
523 }
524 }
525
526 #[test]
527 fn test_file_exists_true() {
528 let temp_file = NamedTempFile::new().unwrap();
529 let path = temp_file.path().to_str().unwrap().to_string();
530
531 unsafe {
532 let stack = crate::stack::alloc_test_stack();
533 let stack = push(stack, Value::String(path.into()));
534 let stack = patch_seq_file_exists(stack);
535
536 let (_stack, value) = pop(stack);
537 assert_eq!(value, Value::Bool(true));
538 }
539 }
540
541 #[test]
542 fn test_file_exists_false() {
543 unsafe {
544 let stack = crate::stack::alloc_test_stack();
545 let stack = push(stack, Value::String("/nonexistent/path/to/file.txt".into()));
546 let stack = patch_seq_file_exists(stack);
547
548 let (_stack, value) = pop(stack);
549 assert_eq!(value, Value::Bool(false));
550 }
551 }
552
553 #[test]
554 fn test_file_slurp_utf8() {
555 let mut temp_file = NamedTempFile::new().unwrap();
556 write!(temp_file, "Hello, δΈη! π").unwrap();
557 let path = temp_file.path().to_str().unwrap().to_string();
558
559 unsafe {
560 let stack = crate::stack::alloc_test_stack();
561 let stack = push(stack, Value::String(path.into()));
562 let stack = patch_seq_file_slurp(stack);
563
564 let (stack, success) = pop(stack);
566 assert_eq!(success, Value::Bool(true));
567 let (_stack, value) = pop(stack);
568 match value {
569 Value::String(s) => assert_eq!(s.as_str(), "Hello, δΈη! π"),
570 _ => panic!("Expected String"),
571 }
572 }
573 }
574
575 #[test]
576 fn test_file_slurp_empty() {
577 let temp_file = NamedTempFile::new().unwrap();
578 let path = temp_file.path().to_str().unwrap().to_string();
579
580 unsafe {
581 let stack = crate::stack::alloc_test_stack();
582 let stack = push(stack, Value::String(path.into()));
583 let stack = patch_seq_file_slurp(stack);
584
585 let (stack, success) = pop(stack);
587 assert_eq!(success, Value::Bool(true)); let (_stack, value) = pop(stack);
589 match value {
590 Value::String(s) => assert_eq!(s.as_str(), ""),
591 _ => panic!("Expected String"),
592 }
593 }
594 }
595
596 #[test]
597 fn test_file_slurp_not_found() {
598 unsafe {
599 let stack = crate::stack::alloc_test_stack();
600 let stack = push(stack, Value::String("/nonexistent/path/to/file.txt".into()));
601 let stack = patch_seq_file_slurp(stack);
602
603 let (stack, success) = pop(stack);
604 let (_stack, contents) = pop(stack);
605 assert_eq!(success, Value::Bool(false));
606 match contents {
607 Value::String(s) => assert_eq!(s.as_str(), ""),
608 _ => panic!("Expected String"),
609 }
610 }
611 }
612
613 #[test]
618 fn test_file_spit_creates_new_file() {
619 let temp_dir = tempfile::tempdir().unwrap();
620 let path = temp_dir.path().join("test.txt");
621 let path_str = path.to_str().unwrap().to_string();
622
623 unsafe {
624 let stack = crate::stack::alloc_test_stack();
625 let stack = push(stack, Value::String("hello world".into()));
626 let stack = push(stack, Value::String(path_str.clone().into()));
627 let stack = patch_seq_file_spit(stack);
628
629 let (_stack, success) = pop(stack);
630 assert_eq!(success, Value::Bool(true));
631 }
632
633 let contents = std::fs::read_to_string(&path).unwrap();
635 assert_eq!(contents, "hello world");
636 }
637
638 #[test]
639 fn test_file_spit_overwrites_existing() {
640 let mut temp_file = NamedTempFile::new().unwrap();
641 writeln!(temp_file, "old content").unwrap();
642 let path = temp_file.path().to_str().unwrap().to_string();
643
644 unsafe {
645 let stack = crate::stack::alloc_test_stack();
646 let stack = push(stack, Value::String("new content".into()));
647 let stack = push(stack, Value::String(path.clone().into()));
648 let stack = patch_seq_file_spit(stack);
649
650 let (_stack, success) = pop(stack);
651 assert_eq!(success, Value::Bool(true));
652 }
653
654 let contents = std::fs::read_to_string(&path).unwrap();
655 assert_eq!(contents, "new content");
656 }
657
658 #[test]
659 fn test_file_spit_invalid_path() {
660 unsafe {
661 let stack = crate::stack::alloc_test_stack();
662 let stack = push(stack, Value::String("content".into()));
663 let stack = push(stack, Value::String("/nonexistent/dir/file.txt".into()));
664 let stack = patch_seq_file_spit(stack);
665
666 let (_stack, success) = pop(stack);
667 assert_eq!(success, Value::Bool(false));
668 }
669 }
670
671 #[test]
676 fn test_file_append_to_existing() {
677 let mut temp_file = NamedTempFile::new().unwrap();
678 write!(temp_file, "hello").unwrap();
679 let path = temp_file.path().to_str().unwrap().to_string();
680
681 unsafe {
682 let stack = crate::stack::alloc_test_stack();
683 let stack = push(stack, Value::String(" world".into()));
684 let stack = push(stack, Value::String(path.clone().into()));
685 let stack = patch_seq_file_append(stack);
686
687 let (_stack, success) = pop(stack);
688 assert_eq!(success, Value::Bool(true));
689 }
690
691 let contents = std::fs::read_to_string(&path).unwrap();
692 assert_eq!(contents, "hello world");
693 }
694
695 #[test]
696 fn test_file_append_creates_new() {
697 let temp_dir = tempfile::tempdir().unwrap();
698 let path = temp_dir.path().join("new.txt");
699 let path_str = path.to_str().unwrap().to_string();
700
701 unsafe {
702 let stack = crate::stack::alloc_test_stack();
703 let stack = push(stack, Value::String("content".into()));
704 let stack = push(stack, Value::String(path_str.clone().into()));
705 let stack = patch_seq_file_append(stack);
706
707 let (_stack, success) = pop(stack);
708 assert_eq!(success, Value::Bool(true));
709 }
710
711 let contents = std::fs::read_to_string(&path).unwrap();
712 assert_eq!(contents, "content");
713 }
714
715 #[test]
720 fn test_file_delete_existing() {
721 let temp_file = NamedTempFile::new().unwrap();
722 let path = temp_file.path().to_str().unwrap().to_string();
723 let path_copy = path.clone();
725 drop(temp_file);
726 std::fs::write(&path_copy, "content").unwrap();
727
728 unsafe {
729 let stack = crate::stack::alloc_test_stack();
730 let stack = push(stack, Value::String(path_copy.clone().into()));
731 let stack = patch_seq_file_delete(stack);
732
733 let (_stack, success) = pop(stack);
734 assert_eq!(success, Value::Bool(true));
735 }
736
737 assert!(!std::path::Path::new(&path_copy).exists());
738 }
739
740 #[test]
741 fn test_file_delete_nonexistent() {
742 unsafe {
743 let stack = crate::stack::alloc_test_stack();
744 let stack = push(stack, Value::String("/nonexistent/file.txt".into()));
745 let stack = patch_seq_file_delete(stack);
746
747 let (_stack, success) = pop(stack);
748 assert_eq!(success, Value::Bool(false));
749 }
750 }
751
752 #[test]
757 fn test_file_size_existing() {
758 let mut temp_file = NamedTempFile::new().unwrap();
759 write!(temp_file, "hello world").unwrap(); let path = temp_file.path().to_str().unwrap().to_string();
761
762 unsafe {
763 let stack = crate::stack::alloc_test_stack();
764 let stack = push(stack, Value::String(path.into()));
765 let stack = patch_seq_file_size(stack);
766
767 let (stack, success) = pop(stack);
768 assert_eq!(success, Value::Bool(true));
769 let (_stack, size) = pop(stack);
770 assert_eq!(size, Value::Int(11));
771 }
772 }
773
774 #[test]
775 fn test_file_size_nonexistent() {
776 unsafe {
777 let stack = crate::stack::alloc_test_stack();
778 let stack = push(stack, Value::String("/nonexistent/file.txt".into()));
779 let stack = patch_seq_file_size(stack);
780
781 let (stack, success) = pop(stack);
782 assert_eq!(success, Value::Bool(false));
783 let (_stack, size) = pop(stack);
784 assert_eq!(size, Value::Int(0));
785 }
786 }
787
788 #[test]
793 fn test_dir_exists_true() {
794 let temp_dir = tempfile::tempdir().unwrap();
795 let path = temp_dir.path().to_str().unwrap().to_string();
796
797 unsafe {
798 let stack = crate::stack::alloc_test_stack();
799 let stack = push(stack, Value::String(path.into()));
800 let stack = patch_seq_dir_exists(stack);
801
802 let (_stack, exists) = pop(stack);
803 assert_eq!(exists, Value::Bool(true));
804 }
805 }
806
807 #[test]
808 fn test_dir_exists_false() {
809 unsafe {
810 let stack = crate::stack::alloc_test_stack();
811 let stack = push(stack, Value::String("/nonexistent/directory".into()));
812 let stack = patch_seq_dir_exists(stack);
813
814 let (_stack, exists) = pop(stack);
815 assert_eq!(exists, Value::Bool(false));
816 }
817 }
818
819 #[test]
820 fn test_dir_exists_file_is_not_dir() {
821 let temp_file = NamedTempFile::new().unwrap();
822 let path = temp_file.path().to_str().unwrap().to_string();
823
824 unsafe {
825 let stack = crate::stack::alloc_test_stack();
826 let stack = push(stack, Value::String(path.into()));
827 let stack = patch_seq_dir_exists(stack);
828
829 let (_stack, exists) = pop(stack);
830 assert_eq!(exists, Value::Bool(false)); }
832 }
833
834 #[test]
839 fn test_dir_make_success() {
840 let temp_dir = tempfile::tempdir().unwrap();
841 let new_dir = temp_dir.path().join("newdir");
842 let path = new_dir.to_str().unwrap().to_string();
843
844 unsafe {
845 let stack = crate::stack::alloc_test_stack();
846 let stack = push(stack, Value::String(path.clone().into()));
847 let stack = patch_seq_dir_make(stack);
848
849 let (_stack, success) = pop(stack);
850 assert_eq!(success, Value::Bool(true));
851 }
852
853 assert!(new_dir.is_dir());
854 }
855
856 #[test]
857 fn test_dir_make_nested() {
858 let temp_dir = tempfile::tempdir().unwrap();
859 let nested = temp_dir.path().join("a").join("b").join("c");
860 let path = nested.to_str().unwrap().to_string();
861
862 unsafe {
863 let stack = crate::stack::alloc_test_stack();
864 let stack = push(stack, Value::String(path.clone().into()));
865 let stack = patch_seq_dir_make(stack);
866
867 let (_stack, success) = pop(stack);
868 assert_eq!(success, Value::Bool(true));
869 }
870
871 assert!(nested.is_dir());
872 }
873
874 #[test]
879 fn test_dir_delete_empty() {
880 let temp_dir = tempfile::tempdir().unwrap();
881 let to_delete = temp_dir.path().join("to_delete");
882 std::fs::create_dir(&to_delete).unwrap();
883 let path = to_delete.to_str().unwrap().to_string();
884
885 unsafe {
886 let stack = crate::stack::alloc_test_stack();
887 let stack = push(stack, Value::String(path.clone().into()));
888 let stack = patch_seq_dir_delete(stack);
889
890 let (_stack, success) = pop(stack);
891 assert_eq!(success, Value::Bool(true));
892 }
893
894 assert!(!to_delete.exists());
895 }
896
897 #[test]
898 fn test_dir_delete_nonempty_fails() {
899 let temp_dir = tempfile::tempdir().unwrap();
900 let to_delete = temp_dir.path().join("nonempty");
901 std::fs::create_dir(&to_delete).unwrap();
902 std::fs::write(to_delete.join("file.txt"), "content").unwrap();
903 let path = to_delete.to_str().unwrap().to_string();
904
905 unsafe {
906 let stack = crate::stack::alloc_test_stack();
907 let stack = push(stack, Value::String(path.clone().into()));
908 let stack = patch_seq_dir_delete(stack);
909
910 let (_stack, success) = pop(stack);
911 assert_eq!(success, Value::Bool(false)); }
913
914 assert!(to_delete.exists());
915 }
916
917 #[test]
918 fn test_dir_delete_nonexistent() {
919 unsafe {
920 let stack = crate::stack::alloc_test_stack();
921 let stack = push(stack, Value::String("/nonexistent/dir".into()));
922 let stack = patch_seq_dir_delete(stack);
923
924 let (_stack, success) = pop(stack);
925 assert_eq!(success, Value::Bool(false));
926 }
927 }
928
929 #[test]
934 fn test_dir_list_success() {
935 let temp_dir = tempfile::tempdir().unwrap();
936 std::fs::write(temp_dir.path().join("a.txt"), "a").unwrap();
937 std::fs::write(temp_dir.path().join("b.txt"), "b").unwrap();
938 let path = temp_dir.path().to_str().unwrap().to_string();
939
940 unsafe {
941 let stack = crate::stack::alloc_test_stack();
942 let stack = push(stack, Value::String(path.into()));
943 let stack = patch_seq_dir_list(stack);
944
945 let (stack, success) = pop(stack);
946 assert_eq!(success, Value::Bool(true));
947
948 let (_stack, list) = pop(stack);
949 match list {
950 Value::Variant(v) => {
951 assert_eq!(v.tag.as_str(), "List");
952 assert_eq!(v.fields.len(), 2);
953 }
954 _ => panic!("Expected Variant(List)"),
955 }
956 }
957 }
958
959 #[test]
960 fn test_dir_list_empty() {
961 let temp_dir = tempfile::tempdir().unwrap();
962 let path = temp_dir.path().to_str().unwrap().to_string();
963
964 unsafe {
965 let stack = crate::stack::alloc_test_stack();
966 let stack = push(stack, Value::String(path.into()));
967 let stack = patch_seq_dir_list(stack);
968
969 let (stack, success) = pop(stack);
970 assert_eq!(success, Value::Bool(true));
971
972 let (_stack, list) = pop(stack);
973 match list {
974 Value::Variant(v) => {
975 assert_eq!(v.tag.as_str(), "List");
976 assert_eq!(v.fields.len(), 0);
977 }
978 _ => panic!("Expected Variant(List)"),
979 }
980 }
981 }
982
983 #[test]
984 fn test_dir_list_nonexistent() {
985 unsafe {
986 let stack = crate::stack::alloc_test_stack();
987 let stack = push(stack, Value::String("/nonexistent/dir".into()));
988 let stack = patch_seq_dir_list(stack);
989
990 let (stack, success) = pop(stack);
991 assert_eq!(success, Value::Bool(false));
992
993 let (_stack, list) = pop(stack);
994 match list {
995 Value::Variant(v) => {
996 assert_eq!(v.tag.as_str(), "List");
997 assert_eq!(v.fields.len(), 0); }
999 _ => panic!("Expected Variant(List)"),
1000 }
1001 }
1002 }
1003}