adana_std_fs/
lib.rs

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 = &params[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 = &params[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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 2, open_options, true, false)?;
188    let (dest, _) = get_file_from_params(&params, 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(&params, 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/// Api description
248#[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}