ferridriver_script/bindings/
artifacts.rs1use std::sync::Arc;
13
14use rquickjs::JsLifetime;
15use rquickjs::class::Trace;
16
17use crate::error::ScriptError;
18use crate::fs::PathSandbox;
19
20#[derive(JsLifetime, Trace)]
21#[rquickjs::class(rename = "Artifacts")]
22pub struct ArtifactsJs {
23 #[qjs(skip_trace)]
24 sandbox: Arc<PathSandbox>,
25}
26
27impl ArtifactsJs {
28 #[must_use]
29 pub fn new(sandbox: Arc<PathSandbox>) -> Self {
30 Self { sandbox }
31 }
32
33 fn io_err(op: &'static str, msg: String) -> rquickjs::Error {
34 rquickjs::Error::new_from_js_message("artifacts", op, msg)
35 }
36
37 fn sandbox_err(err: &ScriptError) -> rquickjs::Error {
38 rquickjs::Error::new_from_js_message("artifacts", "sandbox", err.message.clone())
39 }
40}
41
42#[rquickjs::methods]
43impl ArtifactsJs {
44 #[qjs(get, rename = "root")]
46 pub fn root(&self) -> String {
47 self.sandbox.root().to_string_lossy().into_owned()
48 }
49
50 #[qjs(rename = "write")]
52 pub async fn write(&self, name: String, contents: String) -> rquickjs::Result<()> {
53 let sb = self.sandbox.clone();
54 let resolved = sb.resolve_write(&name).map_err(|e| Self::sandbox_err(&e))?;
55 tokio::fs::write(&resolved, contents)
56 .await
57 .map_err(|e| Self::io_err("write", e.to_string()))
58 }
59
60 #[qjs(rename = "writeBytes")]
63 pub async fn write_bytes(&self, name: String, bytes: Vec<u8>) -> rquickjs::Result<()> {
64 let sb = self.sandbox.clone();
65 let resolved = sb.resolve_write(&name).map_err(|e| Self::sandbox_err(&e))?;
66 tokio::fs::write(&resolved, bytes)
67 .await
68 .map_err(|e| Self::io_err("writeBytes", e.to_string()))
69 }
70
71 #[qjs(rename = "read")]
73 pub async fn read(&self, name: String) -> rquickjs::Result<String> {
74 let sb = self.sandbox.clone();
75 let resolved = sb.resolve_read(&name).map_err(|e| Self::sandbox_err(&e))?;
76 tokio::fs::read_to_string(&resolved)
77 .await
78 .map_err(|e| Self::io_err("read", e.to_string()))
79 }
80
81 #[qjs(rename = "readBytes")]
83 pub async fn read_bytes(&self, name: String) -> rquickjs::Result<Vec<u8>> {
84 let sb = self.sandbox.clone();
85 let resolved = sb.resolve_read(&name).map_err(|e| Self::sandbox_err(&e))?;
86 tokio::fs::read(&resolved)
87 .await
88 .map_err(|e| Self::io_err("readBytes", e.to_string()))
89 }
90
91 #[qjs(rename = "list")]
93 pub async fn list(&self) -> rquickjs::Result<Vec<String>> {
94 let root = self.sandbox.root().to_path_buf();
95 let mut entries = tokio::fs::read_dir(&root)
96 .await
97 .map_err(|e| Self::io_err("list", e.to_string()))?;
98 let mut names = Vec::new();
99 while let Some(entry) = entries
100 .next_entry()
101 .await
102 .map_err(|e| Self::io_err("list", e.to_string()))?
103 {
104 names.push(entry.file_name().to_string_lossy().into_owned());
105 }
106 Ok(names)
107 }
108
109 #[qjs(rename = "readdir")]
111 pub async fn readdir(&self, subpath: String) -> rquickjs::Result<Vec<String>> {
112 let sb = self.sandbox.clone();
113 let resolved = sb.resolve_read(&subpath).map_err(|e| Self::sandbox_err(&e))?;
114 let mut entries = tokio::fs::read_dir(&resolved)
115 .await
116 .map_err(|e| Self::io_err("readdir", e.to_string()))?;
117 let mut names = Vec::new();
118 while let Some(entry) = entries
119 .next_entry()
120 .await
121 .map_err(|e| Self::io_err("readdir", e.to_string()))?
122 {
123 names.push(entry.file_name().to_string_lossy().into_owned());
124 }
125 Ok(names)
126 }
127
128 #[qjs(rename = "exists")]
134 pub async fn exists(&self, name: String) -> rquickjs::Result<bool> {
135 match self.sandbox.resolve_read(&name) {
136 Ok(resolved) => Ok(tokio::fs::try_exists(&resolved).await.unwrap_or(false)),
137 Err(_) => Ok(false),
138 }
139 }
140
141 #[qjs(rename = "remove")]
143 pub async fn remove(&self, name: String) -> rquickjs::Result<bool> {
144 let sb = self.sandbox.clone();
145 let Ok(resolved) = sb.resolve_read(&name) else {
146 return Ok(false);
147 };
148 match tokio::fs::remove_file(&resolved).await {
149 Ok(()) => Ok(true),
150 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
151 Err(e) => Err(Self::io_err("remove", e.to_string())),
152 }
153 }
154}