use base64::Engine;
use serde_json::{Map, Value};
use crate::client::Sandbox;
use crate::error::SolariError;
use crate::types::{FsEntry, FsStat};
pub struct Files<'a> {
pub(crate) sb: &'a Sandbox,
}
impl<'a> Files<'a> {
async fn call(&self, method: &str, params: Value) -> Result<Value, SolariError> {
self.sb.channel().connect().await?;
self.sb.channel().call(method, params).await
}
pub async fn read(&self, path: &str) -> Result<Vec<u8>, SolariError> {
let r = self.call("fs.read", json_obj(&[("path", Value::from(path))])).await?;
let b64 = r.get("base64").and_then(Value::as_str).unwrap_or("");
base64::engine::general_purpose::STANDARD
.decode(b64)
.map_err(|e| SolariError::Other(format!("fs.read bad base64: {e}")))
}
pub async fn read_text(&self, path: &str) -> Result<String, SolariError> {
let bytes = self.read(path).await?;
Ok(String::from_utf8_lossy(&bytes).into_owned())
}
pub async fn write(
&self,
path: &str,
data: impl AsRef<[u8]>,
mode: Option<i64>,
) -> Result<(), SolariError> {
let b64 = base64::engine::general_purpose::STANDARD.encode(data.as_ref());
let mut p = Map::new();
p.insert("path".into(), Value::from(path));
p.insert("base64".into(), Value::String(b64));
if let Some(m) = mode {
p.insert("mode".into(), Value::from(m));
}
self.call("fs.write", Value::Object(p)).await.map(|_| ())
}
pub async fn list(&self, path: &str) -> Result<Vec<FsEntry>, SolariError> {
let r = self.call("fs.list", json_obj(&[("path", Value::from(path))])).await?;
let entries = r.get("entries").cloned().unwrap_or(Value::Array(vec![]));
serde_json::from_value(entries)
.map_err(|e| SolariError::Other(format!("fs.list bad entries: {e}")))
}
pub async fn stat(&self, path: &str) -> Result<FsStat, SolariError> {
let r = self.call("fs.stat", json_obj(&[("path", Value::from(path))])).await?;
serde_json::from_value(r).map_err(|e| SolariError::Other(format!("fs.stat bad: {e}")))
}
pub async fn mkdir(&self, path: &str) -> Result<(), SolariError> {
self.call("fs.mkdir", json_obj(&[("path", Value::from(path))]))
.await
.map(|_| ())
}
pub async fn remove(&self, path: &str, recursive: bool) -> Result<(), SolariError> {
self.call(
"fs.remove",
json_obj(&[("path", Value::from(path)), ("recursive", Value::from(recursive))]),
)
.await
.map(|_| ())
}
pub async fn rename(&self, from: &str, to: &str) -> Result<(), SolariError> {
self.call(
"fs.rename",
json_obj(&[("from", Value::from(from)), ("to", Value::from(to))]),
)
.await
.map(|_| ())
}
}
fn json_obj(pairs: &[(&str, Value)]) -> Value {
let mut m = Map::new();
for (k, v) in pairs {
m.insert((*k).to_string(), v.clone());
}
Value::Object(m)
}