1use std::{
2 collections::BTreeMap,
3 fs::{File, OpenOptions},
4 io::{BufRead, BufReader, BufWriter, Write},
5 path::PathBuf,
6};
7
8use adana_script_core::primitive::{Compiler, NativeFunctionCallResult, Primitive};
9use anyhow::Context;
10
11fn get_file_from_params(
12 params: &[Primitive],
13 param_len: usize,
14 open_option: &mut OpenOptions,
15 fail_if_not_exist: bool,
16 open_file: bool,
17) -> anyhow::Result<(PathBuf, Option<File>)> {
18 if params.len() != param_len {
19 return Err(anyhow::anyhow!(
20 "too many / not enough argument(s). expected argument count: {param_len}"
21 ));
22 }
23 let v = ¶ms[0];
24 match v {
25 Primitive::String(file_path) => {
26 let path_buf = PathBuf::from(file_path);
27
28 if !path_buf.exists() {
29 if fail_if_not_exist {
30 Err(anyhow::anyhow!("file {file_path} not found"))
31 } else {
32 Ok((path_buf, None))
33 }
34 } else if !open_file {
35 Ok((path_buf, None))
36 } else {
37 let file = open_option.open(file_path)?;
38 Ok((path_buf, Some(file)))
39 }
40 }
41 _ => Err(anyhow::anyhow!("wrong read lines call".to_string())),
42 }
43}
44
45fn _write(params: &[Primitive], open_options: &mut OpenOptions) -> NativeFunctionCallResult {
46 let (_, file) = get_file_from_params(params, 2, open_options, false, true)?;
47 let mut writer = BufWriter::new(file.context("file could not be opened")?);
48 let input = ¶ms[1];
49 match input {
50 Primitive::String(s) => writer.write_all(s.as_bytes())?,
51
52 _ => writer.write_all(input.to_string().as_bytes())?,
53 }
54 Ok(Primitive::Unit)
55}
56
57#[no_mangle]
58pub fn read_file(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
59 let mut open_options = OpenOptions::new();
60 let open_options = open_options.read(true).write(false);
61 let (pb, file) = get_file_from_params(¶ms, 1, open_options, true, true)?;
62 let file = file.context("file not found")?;
63 if !pb.is_file() {
64 return Err(anyhow::anyhow!("Not a file"));
65 }
66 let reader = BufReader::new(file);
67 Ok(Primitive::Array(
68 reader
69 .lines()
70 .map(|s| s.map(Primitive::String))
71 .collect::<Result<Vec<_>, _>>()?,
72 ))
73}
74
75#[no_mangle]
76pub fn make_dir(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
77 let mut open_options = OpenOptions::new();
78 let open_options = open_options.read(true).write(false);
79 let (pb, _) = get_file_from_params(¶ms, 1, open_options, false, false)?;
80 std::fs::create_dir(pb)?;
81 Ok(Primitive::Unit)
82}
83
84#[no_mangle]
85pub fn make_dir_all(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
86 let mut open_options = OpenOptions::new();
87 let open_options = open_options.read(true).write(false);
88 let (pb, _) = get_file_from_params(¶ms, 1, open_options, false, false)?;
89 std::fs::create_dir_all(pb)?;
90 Ok(Primitive::Unit)
91}
92
93#[no_mangle]
94pub fn read_dir(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
95 let mut open_options = OpenOptions::new();
96 let open_options = open_options.read(true).write(false);
97 let (pb, _) = get_file_from_params(¶ms, 1, open_options, true, false)?;
98 if !pb.is_dir() {
99 return Err(anyhow::anyhow!("Not a directory"));
100 }
101 let dir = std::fs::read_dir(pb)?;
102
103 let mut arr = vec![];
104
105 for d in dir {
106 let d = d?;
107 arr.push(Primitive::String(
108 d.path()
109 .to_str()
110 .context("path could not be created")?
111 .to_string(),
112 ));
113 }
114 Ok(Primitive::Array(arr))
115}
116
117#[no_mangle]
118pub fn delete_file(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
119 let mut open_options = OpenOptions::new();
120 let open_options = open_options.read(true).write(false);
121 let (pb, _) = get_file_from_params(¶ms, 1, open_options, true, false)?;
122 std::fs::remove_file(pb)?;
123
124 Ok(Primitive::Unit)
125}
126
127#[no_mangle]
128pub fn path_exists(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
129 let mut open_options = OpenOptions::new();
130 let open_options = open_options.read(true).write(false);
131 let (pb, _) = get_file_from_params(¶ms, 1, open_options, false, false)?;
132
133 Ok(Primitive::Bool(pb.exists()))
134}
135
136#[no_mangle]
137pub fn delete_empty_dir(
138 params: Vec<Primitive>,
139 _compiler: Box<Compiler>,
140) -> NativeFunctionCallResult {
141 let mut open_options = OpenOptions::new();
142 let open_options = open_options.read(true).write(false);
143 let (pb, _) = get_file_from_params(¶ms, 1, open_options, true, false)?;
144
145 std::fs::remove_dir(pb)?;
146
147 Ok(Primitive::Unit)
148}
149
150#[no_mangle]
151pub fn delete_dir_all(
152 params: Vec<Primitive>,
153 _compiler: Box<Compiler>,
154) -> NativeFunctionCallResult {
155 let mut open_options = OpenOptions::new();
156 let open_options = open_options.read(true).write(false);
157 let (pb, _) = get_file_from_params(¶ms, 1, open_options, true, false)?;
158
159 std::fs::remove_dir_all(pb)?;
160
161 Ok(Primitive::Unit)
162}
163
164#[no_mangle]
165pub fn write_file(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
166 let mut open_options = OpenOptions::new();
167 let open_options = open_options.write(true);
168 _write(¶ms, open_options)
169}
170
171#[no_mangle]
172pub fn append_file(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
173 let mut open_options = OpenOptions::new();
174 let open_options = open_options.append(true);
175 _write(¶ms, open_options)
176}
177
178#[no_mangle]
179pub fn rename_file_or_directory(
180 params: Vec<Primitive>,
181 _compiler: Box<Compiler>,
182) -> NativeFunctionCallResult {
183 let mut open_options = OpenOptions::new();
184
185 let open_options = open_options.read(true);
186
187 let (src, _) = get_file_from_params(¶ms, 2, open_options, true, false)?;
188 let (dest, _) = get_file_from_params(¶ms, 2, open_options, false, false)?;
189 std::fs::rename(src, dest)?;
190 Ok(Primitive::Unit)
191}
192
193#[no_mangle]
194pub fn fd_stats(params: Vec<Primitive>, _compiler: Box<Compiler>) -> NativeFunctionCallResult {
195 use std::time::UNIX_EPOCH;
196 let mut open_options = OpenOptions::new();
197 let open_options = open_options.read(true).write(false);
198
199 let (pb, file) = get_file_from_params(¶ms, 1, open_options, false, true)?;
200
201 let mut struc = BTreeMap::from([
202 ("exists".into(), Primitive::Bool(pb.exists())),
203 ("is_relative".into(), Primitive::Bool(pb.is_relative())),
204 ("is_absolute".into(), Primitive::Bool(pb.is_absolute())),
205 (
206 "extension".into(),
207 pb.extension()
208 .and_then(|p| p.to_str())
209 .map(|p| Primitive::String(p.to_string()))
210 .unwrap_or_else(|| Primitive::Null),
211 ),
212 (
213 "file_name".into(),
214 pb.file_name()
215 .and_then(|p| p.to_str())
216 .map(|p| Primitive::String(p.to_string()))
217 .unwrap_or_else(|| Primitive::Null),
218 ),
219 ]);
220 if let Some(file) = file {
221 let metadata = file.metadata()?;
222 struc.extend(
223 [
224 ("len".into(), Primitive::Int(metadata.len() as i128)),
225 (
226 "created".into(),
227 Primitive::Int(
228 metadata.created()?.duration_since(UNIX_EPOCH)?.as_millis() as i128
229 ),
230 ),
231 (
232 "modified".into(),
233 Primitive::Int(
234 metadata.modified()?.duration_since(UNIX_EPOCH)?.as_millis() as i128
235 ),
236 ),
237 ("is_file".into(), Primitive::Bool(metadata.is_file())),
238 ("is_dir".into(), Primitive::Bool(metadata.is_dir())),
239 ("is_symlink".into(), Primitive::Bool(metadata.is_symlink())),
240 ],
241 );
242 }
243
244 Ok(Primitive::Struct(struc))
245}
246
247#[no_mangle]
249pub fn api_description(
250 _params: Vec<Primitive>,
251 _compiler: Box<Compiler>,
252) -> NativeFunctionCallResult {
253 Ok(Primitive::Struct(BTreeMap::from([
254 (
255 "read_file".into(),
256 Primitive::String("read_file(file_path) -> [string], Read lines in file".into()),
257 ),
258 (
259 "fd_stats".into(),
260 Primitive::String("fd_stats(file_path) -> struct, Stats about the file".into()),
261 ),
262 (
263 "write_file".into(),
264 Primitive::String("write_file(file_path, content), Write content to file".into()),
265 ),
266 (
267 "delete_file".into(),
268 Primitive::String("delete_file(file_path), Delete a file".into())
269 ),
270 (
271 "delete_empty_dir".into(),
272 Primitive::String("delete_empty_dir(path), Delete an empty directory".into())
273 ),
274 (
275 "delete_dir_all".into(),
276 Primitive::String("delete_dir_all(path), Delete a directory and all its content.".into())
277 ),
278 (
279 "make_dir".into(),
280 Primitive::String("make_dir(path), Create a directory.".into())
281 ),
282 (
283 "make_dir_all".into(),
284 Primitive::String("make_dir_all(path), Create a directory recursively.".into())
285 ),
286 (
287 "append_file".into(),
288 Primitive::String("append_file(file_path, content), Append content to file".into()),
289 ),
290 (
291 "path_exists".into(),
292 Primitive::String("path_exists(file_path)-> bool, weither a path exists or not".into()),
293
294 ),
295 (
296 "rename_file_or_directory".into(),
297 Primitive::String("rename_file_or_directory(src_path, dest_path), Rename file".into()),
298 ),
299 (
300 "read_dir".into(),
301 Primitive::String(
302 "read_dir(path) -> [string], Read directory, returning the full path for each entries".into(),
303 ),
304 ),
305 ])))
306}