1use crate::stack::{Stack, pop, push};
26use crate::value::Value;
27use std::fs;
28use std::path::Path;
29
30#[unsafe(no_mangle)]
41pub unsafe extern "C" fn patch_seq_file_slurp(stack: Stack) -> Stack {
42 assert!(!stack.is_null(), "file-slurp: stack is empty");
43
44 let (rest, value) = unsafe { pop(stack) };
45
46 match value {
47 Value::String(path) => {
48 let contents = fs::read_to_string(path.as_str()).unwrap_or_else(|e| {
49 panic!("file-slurp: failed to read '{}': {}", path.as_str(), e)
50 });
51
52 unsafe { push(rest, Value::String(contents.into())) }
53 }
54 _ => panic!("file-slurp: expected String path on stack, got {:?}", value),
55 }
56}
57
58#[unsafe(no_mangle)]
68pub unsafe extern "C" fn patch_seq_file_exists(stack: Stack) -> Stack {
69 assert!(!stack.is_null(), "file-exists?: stack is empty");
70
71 let (rest, value) = unsafe { pop(stack) };
72
73 match value {
74 Value::String(path) => {
75 let exists = if Path::new(path.as_str()).exists() {
76 1i64
77 } else {
78 0i64
79 };
80
81 unsafe { push(rest, Value::Int(exists)) }
82 }
83 _ => panic!(
84 "file-exists?: expected String path on stack, got {:?}",
85 value
86 ),
87 }
88}
89
90pub use patch_seq_file_exists as file_exists;
92pub use patch_seq_file_slurp as file_slurp;
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use std::io::Write;
98 use tempfile::NamedTempFile;
99
100 #[test]
101 fn test_file_slurp() {
102 let mut temp_file = NamedTempFile::new().unwrap();
104 writeln!(temp_file, "Hello, file!").unwrap();
105 let path = temp_file.path().to_str().unwrap().to_string();
106
107 unsafe {
108 let stack = std::ptr::null_mut();
109 let stack = push(stack, Value::String(path.into()));
110 let stack = patch_seq_file_slurp(stack);
111
112 let (stack, value) = pop(stack);
113 match value {
114 Value::String(s) => assert_eq!(s.as_str().trim(), "Hello, file!"),
115 _ => panic!("Expected String"),
116 }
117 assert!(stack.is_null());
118 }
119 }
120
121 #[test]
122 fn test_file_exists_true() {
123 let temp_file = NamedTempFile::new().unwrap();
124 let path = temp_file.path().to_str().unwrap().to_string();
125
126 unsafe {
127 let stack = std::ptr::null_mut();
128 let stack = push(stack, Value::String(path.into()));
129 let stack = patch_seq_file_exists(stack);
130
131 let (stack, value) = pop(stack);
132 assert_eq!(value, Value::Int(1));
133 assert!(stack.is_null());
134 }
135 }
136
137 #[test]
138 fn test_file_exists_false() {
139 unsafe {
140 let stack = std::ptr::null_mut();
141 let stack = push(stack, Value::String("/nonexistent/path/to/file.txt".into()));
142 let stack = patch_seq_file_exists(stack);
143
144 let (stack, value) = pop(stack);
145 assert_eq!(value, Value::Int(0));
146 assert!(stack.is_null());
147 }
148 }
149
150 #[test]
151 fn test_file_slurp_utf8() {
152 let mut temp_file = NamedTempFile::new().unwrap();
153 write!(temp_file, "Hello, δΈη! π").unwrap();
154 let path = temp_file.path().to_str().unwrap().to_string();
155
156 unsafe {
157 let stack = std::ptr::null_mut();
158 let stack = push(stack, Value::String(path.into()));
159 let stack = patch_seq_file_slurp(stack);
160
161 let (stack, value) = pop(stack);
162 match value {
163 Value::String(s) => assert_eq!(s.as_str(), "Hello, δΈη! π"),
164 _ => panic!("Expected String"),
165 }
166 assert!(stack.is_null());
167 }
168 }
169
170 #[test]
171 fn test_file_slurp_empty() {
172 let temp_file = NamedTempFile::new().unwrap();
173 let path = temp_file.path().to_str().unwrap().to_string();
174
175 unsafe {
176 let stack = std::ptr::null_mut();
177 let stack = push(stack, Value::String(path.into()));
178 let stack = patch_seq_file_slurp(stack);
179
180 let (stack, value) = pop(stack);
181 match value {
182 Value::String(s) => assert_eq!(s.as_str(), ""),
183 _ => panic!("Expected String"),
184 }
185 assert!(stack.is_null());
186 }
187 }
188}