1use base64::Engine;
5use serde_json::{Map, Value};
6
7use crate::client::Sandbox;
8use crate::error::SolariError;
9use crate::types::{FsEntry, FsStat};
10
11pub struct Files<'a> {
13 pub(crate) sb: &'a Sandbox,
14}
15
16impl<'a> Files<'a> {
17 async fn call(&self, method: &str, params: Value) -> Result<Value, SolariError> {
18 self.sb.channel().connect().await?;
19 self.sb.channel().call(method, params).await
20 }
21
22 pub async fn read(&self, path: &str) -> Result<Vec<u8>, SolariError> {
24 let r = self.call("fs.read", json_obj(&[("path", Value::from(path))])).await?;
25 let b64 = r.get("base64").and_then(Value::as_str).unwrap_or("");
26 base64::engine::general_purpose::STANDARD
27 .decode(b64)
28 .map_err(|e| SolariError::Other(format!("fs.read bad base64: {e}")))
29 }
30
31 pub async fn read_text(&self, path: &str) -> Result<String, SolariError> {
33 let bytes = self.read(path).await?;
34 Ok(String::from_utf8_lossy(&bytes).into_owned())
35 }
36
37 pub async fn write(
39 &self,
40 path: &str,
41 data: impl AsRef<[u8]>,
42 mode: Option<i64>,
43 ) -> Result<(), SolariError> {
44 let b64 = base64::engine::general_purpose::STANDARD.encode(data.as_ref());
45 let mut p = Map::new();
46 p.insert("path".into(), Value::from(path));
47 p.insert("base64".into(), Value::String(b64));
48 if let Some(m) = mode {
49 p.insert("mode".into(), Value::from(m));
50 }
51 self.call("fs.write", Value::Object(p)).await.map(|_| ())
52 }
53
54 pub async fn list(&self, path: &str) -> Result<Vec<FsEntry>, SolariError> {
56 let r = self.call("fs.list", json_obj(&[("path", Value::from(path))])).await?;
57 let entries = r.get("entries").cloned().unwrap_or(Value::Array(vec![]));
58 serde_json::from_value(entries)
59 .map_err(|e| SolariError::Other(format!("fs.list bad entries: {e}")))
60 }
61
62 pub async fn stat(&self, path: &str) -> Result<FsStat, SolariError> {
64 let r = self.call("fs.stat", json_obj(&[("path", Value::from(path))])).await?;
65 serde_json::from_value(r).map_err(|e| SolariError::Other(format!("fs.stat bad: {e}")))
66 }
67
68 pub async fn mkdir(&self, path: &str) -> Result<(), SolariError> {
70 self.call("fs.mkdir", json_obj(&[("path", Value::from(path))]))
71 .await
72 .map(|_| ())
73 }
74
75 pub async fn remove(&self, path: &str, recursive: bool) -> Result<(), SolariError> {
77 self.call(
78 "fs.remove",
79 json_obj(&[("path", Value::from(path)), ("recursive", Value::from(recursive))]),
80 )
81 .await
82 .map(|_| ())
83 }
84
85 pub async fn rename(&self, from: &str, to: &str) -> Result<(), SolariError> {
87 self.call(
88 "fs.rename",
89 json_obj(&[("from", Value::from(from)), ("to", Value::from(to))]),
90 )
91 .await
92 .map(|_| ())
93 }
94}
95
96fn json_obj(pairs: &[(&str, Value)]) -> Value {
97 let mut m = Map::new();
98 for (k, v) in pairs {
99 m.insert((*k).to_string(), v.clone());
100 }
101 Value::Object(m)
102}