Skip to main content

aver/services/
disk.rs

1/// Disk service — file-system I/O.
2///
3/// Eight methods covering the full CRUD surface for files and directories:
4///   readText   — read a file as a UTF-8 string
5///   writeText  — write (overwrite) a file
6///   appendText — append to a file, creating it if absent
7///   exists     — check whether a path exists (returns Bool, not Result)
8///   delete     — remove a **file** (Err if given a directory — use deleteDir)
9///   deleteDir  — recursively remove a **directory** (Err if given a file)
10///   listDir    — list entry names in a directory
11///   makeDir    — create a directory and all missing parents (mkdir -p)
12///
13/// All methods require `! [Disk]`.
14use std::collections::HashMap;
15
16use crate::value::{RuntimeError, Value, list_from_vec};
17
18pub fn register(global: &mut HashMap<String, Value>) {
19    let mut members = HashMap::new();
20    for method in &[
21        "readText",
22        "writeText",
23        "appendText",
24        "exists",
25        "delete",
26        "deleteDir",
27        "listDir",
28        "makeDir",
29    ] {
30        members.insert(
31            method.to_string(),
32            Value::Builtin(format!("Disk.{}", method)),
33        );
34    }
35    global.insert(
36        "Disk".to_string(),
37        Value::Namespace {
38            name: "Disk".to_string(),
39            members,
40        },
41    );
42}
43
44pub fn effects(name: &str) -> &'static [&'static str] {
45    match name {
46        "Disk.readText" => &["Disk.readText"],
47        "Disk.writeText" => &["Disk.writeText"],
48        "Disk.appendText" => &["Disk.appendText"],
49        "Disk.exists" => &["Disk.exists"],
50        "Disk.delete" => &["Disk.delete"],
51        "Disk.deleteDir" => &["Disk.deleteDir"],
52        "Disk.listDir" => &["Disk.listDir"],
53        "Disk.makeDir" => &["Disk.makeDir"],
54        _ => &[],
55    }
56}
57
58/// Returns `Some(result)` when `name` is owned by this service, `None` otherwise.
59pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
60    match name {
61        "Disk.readText" => Some(read_text(args)),
62        "Disk.writeText" => Some(write_text(args)),
63        "Disk.appendText" => Some(append_text(args)),
64        "Disk.exists" => Some(exists(args)),
65        "Disk.delete" => Some(delete(args)),
66        "Disk.deleteDir" => Some(delete_dir(args)),
67        "Disk.listDir" => Some(list_dir(args)),
68        "Disk.makeDir" => Some(make_dir(args)),
69        _ => None,
70    }
71}
72
73// ─── Implementations ──────────────────────────────────────────────────────────
74
75fn read_text(args: &[Value]) -> Result<Value, RuntimeError> {
76    let path = one_str_arg("Disk.readText", args)?;
77    match aver_rt::read_text(&path) {
78        Ok(text) => Ok(Value::Ok(Box::new(Value::Str(text)))),
79        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
80    }
81}
82
83fn write_text(args: &[Value]) -> Result<Value, RuntimeError> {
84    let (path, content) = two_str_args("Disk.writeText", args)?;
85    match aver_rt::write_text(&path, &content) {
86        Ok(_) => Ok(Value::Ok(Box::new(Value::Unit))),
87        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
88    }
89}
90
91fn append_text(args: &[Value]) -> Result<Value, RuntimeError> {
92    let (path, content) = two_str_args("Disk.appendText", args)?;
93    match aver_rt::append_text(&path, &content) {
94        Ok(_) => Ok(Value::Ok(Box::new(Value::Unit))),
95        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
96    }
97}
98
99fn exists(args: &[Value]) -> Result<Value, RuntimeError> {
100    let path = one_str_arg("Disk.exists", args)?;
101    Ok(Value::Bool(aver_rt::path_exists(&path)))
102}
103
104fn delete(args: &[Value]) -> Result<Value, RuntimeError> {
105    let path = one_str_arg("Disk.delete", args)?;
106    match aver_rt::delete_file(&path) {
107        Ok(_) => Ok(Value::Ok(Box::new(Value::Unit))),
108        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
109    }
110}
111
112fn delete_dir(args: &[Value]) -> Result<Value, RuntimeError> {
113    let path = one_str_arg("Disk.deleteDir", args)?;
114    match aver_rt::delete_dir(&path) {
115        Ok(_) => Ok(Value::Ok(Box::new(Value::Unit))),
116        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
117    }
118}
119
120fn list_dir(args: &[Value]) -> Result<Value, RuntimeError> {
121    let path = one_str_arg("Disk.listDir", args)?;
122    match aver_rt::list_dir(&path) {
123        Ok(entries) => Ok(Value::Ok(Box::new(list_from_vec(
124            entries.into_iter().map(Value::Str).collect(),
125        )))),
126        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
127    }
128}
129
130fn make_dir(args: &[Value]) -> Result<Value, RuntimeError> {
131    let path = one_str_arg("Disk.makeDir", args)?;
132    match aver_rt::make_dir(&path) {
133        Ok(_) => Ok(Value::Ok(Box::new(Value::Unit))),
134        Err(e) => Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
135    }
136}
137
138// ─── Argument helpers ─────────────────────────────────────────────────────────
139
140fn one_str_arg(fn_name: &str, args: &[Value]) -> Result<String, RuntimeError> {
141    match args {
142        [Value::Str(s)] => Ok(s.clone()),
143        [_] => Err(RuntimeError::Error(format!(
144            "{}: path must be a String",
145            fn_name
146        ))),
147        _ => Err(RuntimeError::Error(format!(
148            "{}() takes 1 argument (path), got {}",
149            fn_name,
150            args.len()
151        ))),
152    }
153}
154
155fn two_str_args(fn_name: &str, args: &[Value]) -> Result<(String, String), RuntimeError> {
156    match args {
157        [Value::Str(a), Value::Str(b)] => Ok((a.clone(), b.clone())),
158        [_, _] => Err(RuntimeError::Error(format!(
159            "{}: both arguments must be Strings",
160            fn_name
161        ))),
162        _ => Err(RuntimeError::Error(format!(
163            "{}() takes 2 arguments (path, content), got {}",
164            fn_name,
165            args.len()
166        ))),
167    }
168}