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