graphix_package_fs/
lib.rs1use arcstr::ArcStr;
2use compact_str::CompactString;
3use graphix_compiler::errf;
4use graphix_package_core::{
5 deftype, CachedArgs, CachedArgsAsync, CachedVals, EvalCached, EvalCachedAsync,
6};
7use netidx_value::Value;
8use parking_lot::Mutex;
9use poolshark::local::LPooled;
10use std::{
11 cell::RefCell,
12 fmt,
13 path::{Path, PathBuf},
14 sync::Arc,
15};
16use tempfile::TempDir;
17
18pub(crate) mod dir;
19pub(crate) mod file;
20pub(crate) mod metadata;
21pub(crate) mod watch;
22
23#[derive(Debug)]
24enum Name {
25 Prefix(ArcStr),
26 Suffix(ArcStr),
27}
28
29pub(crate) struct TempDirArgs {
30 dir: Option<ArcStr>,
31 name: Option<Name>,
32 t: Arc<Mutex<Option<TempDir>>>,
33}
34
35impl fmt::Debug for TempDirArgs {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "{{ dir: {:?}, name: {:?} }}", self.dir, self.name)
38 }
39}
40
41#[derive(Debug, Default)]
42pub(crate) struct GxTempDirEv {
43 current: Arc<Mutex<Option<TempDir>>>,
44}
45
46impl EvalCachedAsync for GxTempDirEv {
47 const NAME: &str = "fs_tempdir";
48 deftype!(
49 r#"fn(?#in:[null, string],
50 ?#name:[null, `Prefix(string), `Suffix(string)],
51 Any)
52 -> Result<string, `IOError(string)>"#
53 );
54 type Args = TempDirArgs;
55
56 fn prepare_args(&mut self, cached: &CachedVals) -> Option<Self::Args> {
57 if cached.0.iter().any(|v| v.is_none()) {
58 None
59 } else {
60 let dir = cached.get::<Option<ArcStr>>(0).flatten();
61 let name = cached
62 .get::<Option<(ArcStr, ArcStr)>>(1)
63 .and_then(|v| v)
64 .and_then(|(tag, v)| match &*tag {
65 "Prefix" => Some(Name::Prefix(v)),
66 "Suffix" => Some(Name::Suffix(v)),
67 _ => None,
68 });
69 let _ = cached.get::<Value>(2)?;
70 Some(TempDirArgs { dir, name, t: self.current.clone() })
71 }
72 }
73
74 fn eval(args: Self::Args) -> impl Future<Output = Value> + Send {
75 async move {
76 let td = tokio::task::spawn_blocking(|| match (args.dir, args.name) {
77 (None, None) => TempDir::new(),
78 (None, Some(Name::Prefix(pfx))) => TempDir::with_prefix(&*pfx),
79 (None, Some(Name::Suffix(sfx))) => TempDir::with_suffix(&*sfx),
80 (Some(dir), None) => TempDir::new_in(&*dir),
81 (Some(dir), Some(Name::Prefix(pfx))) => {
82 TempDir::with_prefix_in(&*pfx, &*dir)
83 }
84 (Some(dir), Some(Name::Suffix(sfx))) => {
85 TempDir::with_suffix_in(&*sfx, &*dir)
86 }
87 })
88 .await;
89 match td {
90 Err(e) => errf!("IOError", "failed to spawn create temp dir {e:?}"),
91 Ok(Err(e)) => errf!("IOError", "failed to create temp dir {e:?}"),
92 Ok(Ok(td)) => {
93 use std::fmt::Write;
94 let mut buf = CompactString::new("");
95 write!(buf, "{}", td.path().display()).unwrap();
96 *args.t.lock() = Some(td);
97 Value::String(ArcStr::from(buf.as_str()))
98 }
99 }
100 }
101 }
102}
103
104pub(crate) type GxTempDir = CachedArgsAsync<GxTempDirEv>;
105
106pub(crate) fn convert_path(path: &Path) -> ArcStr {
107 thread_local! {
108 static BUF: RefCell<String> = RefCell::new(String::new());
109 }
110 BUF.with_borrow_mut(|buf| {
111 buf.clear();
112 use std::fmt::Write;
113 write!(buf, "{}", path.display()).unwrap();
114 ArcStr::from(buf.as_str())
115 })
116}
117
118#[derive(Debug, Default)]
119pub(crate) struct JoinPathEv;
120
121impl EvalCached for JoinPathEv {
122 const NAME: &str = "fs_join_path";
123 deftype!("fn(string, @args: [string, Array<string>]) -> string");
124
125 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
126 let mut parts: LPooled<Vec<ArcStr>> = LPooled::take();
127 for part in from.0.iter() {
128 match part {
129 None => return None,
130 Some(Value::String(s)) => parts.push(s.clone()),
131 Some(Value::Array(a)) => {
132 for part in a.iter() {
133 match part {
134 Value::String(s) => parts.push(s.clone()),
135 _ => return None,
136 }
137 }
138 }
139 _ => return None,
140 }
141 }
142 thread_local! {
143 static BUF: RefCell<PathBuf> = RefCell::new(PathBuf::new());
144 }
145 BUF.with_borrow_mut(|path| {
146 path.clear();
147 for part in parts.drain(..) {
148 path.push(&*part)
149 }
150 Some(Value::String(convert_path(&path)))
151 })
152 }
153}
154
155pub(crate) type JoinPath = CachedArgs<JoinPathEv>;
156
157#[cfg(test)]
158mod test;
159
160graphix_derive::defpackage! {
161 builtins => [
162 GxTempDir,
163 JoinPath,
164 metadata::IsFile,
165 metadata::IsDir,
166 metadata::Metadata,
167 watch::SetGlobals,
168 watch::WatchBuiltIn,
169 watch::WatchFullBuiltIn,
170 file::ReadAll,
171 file::ReadAllBin,
172 file::WriteAll,
173 file::WriteAllBin,
174 file::RemoveFile,
175 dir::ReadDir,
176 dir::CreateDir,
177 dir::RemoveDir,
178 ],
179}