blockworx_opfs/
directory.rs1use js_sys::AsyncIterator;
8use wasm_bindgen::{JsCast as _, JsValue};
9use web_sys::{
10 FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetDirectoryOptions,
11 FileSystemGetFileOptions, FileSystemHandle, FileSystemHandleKind, FileSystemRemoveOptions,
12};
13
14use crate::fault::{faulted, mistaken, settled};
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug)]
18pub enum Missing {
19 IsNothing,
20 IsMade,
21}
22
23#[derive(Clone, Copy, PartialEq, Eq, Debug)]
25pub enum Depth {
26 Entry,
27 Everything,
28}
29
30impl Missing {
31 fn made(self) -> bool {
32 matches!(self, Missing::IsMade)
33 }
34}
35
36pub async fn directory(
42 within: &FileSystemDirectoryHandle,
43 name: &str,
44 missing: Missing,
45) -> std::io::Result<FileSystemDirectoryHandle> {
46 let options = FileSystemGetDirectoryOptions::new();
47 options.set_create(missing.made());
48 let got = settled(within.get_directory_handle_with_options(name, &options)).await?;
49 got.dyn_into().map_err(mistaken("a directory"))
50}
51
52pub async fn file(
57 within: &FileSystemDirectoryHandle,
58 name: &str,
59 missing: Missing,
60) -> std::io::Result<FileSystemFileHandle> {
61 let options = FileSystemGetFileOptions::new();
62 options.set_create(missing.made());
63 let got = settled(within.get_file_handle_with_options(name, &options)).await?;
64 got.dyn_into().map_err(mistaken("a file"))
65}
66
67pub async fn remove(
71 within: &FileSystemDirectoryHandle,
72 name: &str,
73 depth: Depth,
74) -> std::io::Result<()> {
75 let options = FileSystemRemoveOptions::new();
76 options.set_recursive(matches!(depth, Depth::Everything));
77 settled(within.remove_entry_with_options(name, &options)).await?;
78 Ok(())
79}
80
81pub async fn names(of: &FileSystemDirectoryHandle) -> std::io::Result<Vec<String>> {
87 Ok(held(of).await?.iter().map(FileSystemHandle::name).collect())
88}
89
90pub async fn held(of: &FileSystemDirectoryHandle) -> std::io::Result<Vec<FileSystemHandle>> {
95 let mut holds = Vec::new();
96 for handle in walked(of.values()).await? {
97 holds.push(handle.dyn_into().map_err(mistaken("a handle"))?);
98 }
99 Ok(holds)
100}
101
102pub async fn holds(of: &FileSystemDirectoryHandle, name: &str) -> std::io::Result<bool> {
108 Ok(held(of).await?.iter().any(|handle| handle.name() == name))
109}
110
111pub async fn directories(of: &FileSystemDirectoryHandle) -> std::io::Result<Vec<String>> {
117 Ok(held(of)
118 .await?
119 .iter()
120 .filter(|handle| handle.kind() == FileSystemHandleKind::Directory)
121 .map(FileSystemHandle::name)
122 .collect())
123}
124
125async fn walked(over: AsyncIterator) -> std::io::Result<Vec<JsValue>> {
127 let mut walked = Vec::new();
128 loop {
129 let step = settled(over.next().map_err(faulted)?).await?;
130 if js_sys::Reflect::get(&step, &JsValue::from_str("done"))
131 .map_err(faulted)?
132 .is_truthy()
133 {
134 return Ok(walked);
135 }
136 walked.push(js_sys::Reflect::get(&step, &JsValue::from_str("value")).map_err(faulted)?);
137 }
138}
139
140pub async fn copy(
149 from: &FileSystemDirectoryHandle,
150 into: &FileSystemDirectoryHandle,
151) -> std::io::Result<()> {
152 let mut pending = vec![(from.clone(), into.clone())];
153 while let Some((from, into)) = pending.pop() {
154 for handle in held(&from).await? {
155 let name = handle.name();
156 if handle.kind() == FileSystemHandleKind::Directory {
157 pending.push((
158 directory(&from, &name, Missing::IsNothing).await?,
159 directory(&into, &name, Missing::IsMade).await?,
160 ));
161 continue;
162 }
163 let bytes = crate::file::read(&file(&from, &name, Missing::IsNothing).await?).await?;
164 crate::file::write(&file(&into, &name, Missing::IsMade).await?, &bytes).await?;
165 }
166 }
167 Ok(())
168}