@wasm-oj/server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/THIRD_PARTY_NOTICES.md +302 -0
- package/crates/runtime-core/Cargo.lock +5099 -0
- package/crates/runtime-core/Cargo.toml +66 -0
- package/crates/runtime-core/README.md +47 -0
- package/crates/runtime-core/src/bin/wasm-oj-compiler.rs +418 -0
- package/crates/runtime-core/src/bin/wasm-oj-runner.rs +294 -0
- package/crates/runtime-core/src/capabilities.rs +118 -0
- package/crates/runtime-core/src/compiler.rs +658 -0
- package/crates/runtime-core/src/contract.rs +5 -0
- package/crates/runtime-core/src/deterministic.rs +1051 -0
- package/crates/runtime-core/src/error.rs +58 -0
- package/crates/runtime-core/src/filesystem.rs +547 -0
- package/crates/runtime-core/src/filesystem_quota.rs +167 -0
- package/crates/runtime-core/src/go_compiler_session.rs +297 -0
- package/crates/runtime-core/src/interactive.rs +1019 -0
- package/crates/runtime-core/src/judge_package.rs +1539 -0
- package/crates/runtime-core/src/lib.rs +98 -0
- package/crates/runtime-core/src/memory.rs +84 -0
- package/crates/runtime-core/src/meter.rs +549 -0
- package/crates/runtime-core/src/module_imports.rs +149 -0
- package/crates/runtime-core/src/module_policy.rs +714 -0
- package/crates/runtime-core/src/output.rs +204 -0
- package/crates/runtime-core/src/run/mod.rs +208 -0
- package/crates/runtime-core/src/run/native.rs +260 -0
- package/crates/runtime-core/src/run/web.rs +229 -0
- package/crates/runtime-core/src/run/web_runtime.rs +109 -0
- package/crates/runtime-core/src/types.rs +268 -0
- package/crates/runtime-core/src/web.rs +83 -0
- package/dist/chunks/go-toolchain-Dbt-lp2L.js +426 -0
- package/dist/chunks/java-toolchain-DajoRCHu.js +44 -0
- package/dist/chunks/python-toolchain-Dx834o2A.js +4 -0
- package/dist/chunks/rust-toolchain-CJ3sMxPE.js +252 -0
- package/dist/chunks/toolchains-C6KuA1yM.js +224 -0
- package/dist/go-stage.mjs +193 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.js +4393 -0
- package/dist/java-stage.mjs +111 -0
- package/dist/python-stage.mjs +90 -0
- package/dist/rustc-stage.mjs +301 -0
- package/dist/server-build-stage.mjs +2564 -0
- package/dist/server-runner-stage.mjs +155 -0
- package/licenses/fflate-MIT.txt +21 -0
- package/licenses/runtime-core-dependencies.html +6253 -0
- package/licenses/runtime-core-dependencies.json +3041 -0
- package/licenses/wasmer-sdk-MIT.txt +21 -0
- package/licenses/wasmer-sdk-dependencies.html +6901 -0
- package/licenses/wasmer-sdk-dependencies.json +3013 -0
- package/package.json +70 -0
- package/rust-toolchain.toml +5 -0
- package/testdata/wojjdg02-v2-text.hex +1 -0
- package/vendor/shared-buffer/Cargo.toml +22 -0
- package/vendor/shared-buffer/LICENSE_APACHE.md +176 -0
- package/vendor/shared-buffer/LICENSE_MIT.md +25 -0
- package/vendor/shared-buffer/README.md +34 -0
- package/vendor/shared-buffer/src/lib.rs +58 -0
- package/vendor/shared-buffer/src/mmap.rs +250 -0
- package/vendor/shared-buffer/src/owned.rs +389 -0
- package/vendor/virtual-fs/Cargo.toml +181 -0
- package/vendor/virtual-fs/LICENSE +25 -0
- package/vendor/virtual-fs/src/arc_box_file.rs +142 -0
- package/vendor/virtual-fs/src/arc_file.rs +182 -0
- package/vendor/virtual-fs/src/arc_fs.rs +68 -0
- package/vendor/virtual-fs/src/buffer_file.rs +103 -0
- package/vendor/virtual-fs/src/builder.rs +232 -0
- package/vendor/virtual-fs/src/combine_file.rs +101 -0
- package/vendor/virtual-fs/src/cow_file.rs +345 -0
- package/vendor/virtual-fs/src/dual_write_file.rs +113 -0
- package/vendor/virtual-fs/src/empty_fs.rs +81 -0
- package/vendor/virtual-fs/src/filesystems.rs +108 -0
- package/vendor/virtual-fs/src/host_fs.rs +1390 -0
- package/vendor/virtual-fs/src/lib.rs +782 -0
- package/vendor/virtual-fs/src/limiter.rs +252 -0
- package/vendor/virtual-fs/src/mem_fs/file.rs +1799 -0
- package/vendor/virtual-fs/src/mem_fs/file_opener.rs +941 -0
- package/vendor/virtual-fs/src/mem_fs/filesystem.rs +2134 -0
- package/vendor/virtual-fs/src/mem_fs/mod.rs +245 -0
- package/vendor/virtual-fs/src/mem_fs/offloaded_file.rs +474 -0
- package/vendor/virtual-fs/src/mem_fs/stdio.rs +318 -0
- package/vendor/virtual-fs/src/mount_fs.rs +2225 -0
- package/vendor/virtual-fs/src/null_file.rs +87 -0
- package/vendor/virtual-fs/src/ops.rs +364 -0
- package/vendor/virtual-fs/src/overlay_fs.rs +2216 -0
- package/vendor/virtual-fs/src/passthru_fs.rs +119 -0
- package/vendor/virtual-fs/src/pipe.rs +603 -0
- package/vendor/virtual-fs/src/random_file.rs +88 -0
- package/vendor/virtual-fs/src/special_file.rs +108 -0
- package/vendor/virtual-fs/src/static_file.rs +133 -0
- package/vendor/virtual-fs/src/static_fs.rs +460 -0
- package/vendor/virtual-fs/src/tmp_fs.rs +95 -0
- package/vendor/virtual-fs/src/trace_fs.rs +258 -0
- package/vendor/virtual-fs/src/webc_volume_fs.rs +829 -0
- package/vendor/virtual-fs/src/zero_file.rs +90 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//! NullFile is a special file for `/dev/null`, which returns 0 for all
|
|
2
|
+
//! operations except writing.
|
|
3
|
+
|
|
4
|
+
use std::io::{self, *};
|
|
5
|
+
use std::pin::Pin;
|
|
6
|
+
use std::task::{Context, Poll};
|
|
7
|
+
|
|
8
|
+
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
|
9
|
+
|
|
10
|
+
use crate::{CloneableVirtualFile, VirtualFile};
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Clone, Default)]
|
|
13
|
+
pub struct NullFile {}
|
|
14
|
+
|
|
15
|
+
impl AsyncSeek for NullFile {
|
|
16
|
+
fn start_seek(self: Pin<&mut Self>, _position: SeekFrom) -> io::Result<()> {
|
|
17
|
+
Ok(())
|
|
18
|
+
}
|
|
19
|
+
fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
|
20
|
+
Poll::Ready(Ok(0))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl AsyncWrite for NullFile {
|
|
25
|
+
fn poll_write(
|
|
26
|
+
self: Pin<&mut Self>,
|
|
27
|
+
_cx: &mut Context<'_>,
|
|
28
|
+
buf: &[u8],
|
|
29
|
+
) -> Poll<io::Result<usize>> {
|
|
30
|
+
Poll::Ready(Ok(buf.len()))
|
|
31
|
+
}
|
|
32
|
+
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
33
|
+
Poll::Ready(Ok(()))
|
|
34
|
+
}
|
|
35
|
+
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
36
|
+
Poll::Ready(Ok(()))
|
|
37
|
+
}
|
|
38
|
+
fn poll_write_vectored(
|
|
39
|
+
self: Pin<&mut Self>,
|
|
40
|
+
_cx: &mut Context<'_>,
|
|
41
|
+
bufs: &[IoSlice<'_>],
|
|
42
|
+
) -> Poll<io::Result<usize>> {
|
|
43
|
+
Poll::Ready(Ok(bufs.len()))
|
|
44
|
+
}
|
|
45
|
+
fn is_write_vectored(&self) -> bool {
|
|
46
|
+
false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl AsyncRead for NullFile {
|
|
51
|
+
fn poll_read(
|
|
52
|
+
self: Pin<&mut Self>,
|
|
53
|
+
_cx: &mut Context<'_>,
|
|
54
|
+
_buf: &mut tokio::io::ReadBuf<'_>,
|
|
55
|
+
) -> Poll<io::Result<()>> {
|
|
56
|
+
Poll::Ready(Ok(()))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl VirtualFile for NullFile {
|
|
61
|
+
fn last_accessed(&self) -> u64 {
|
|
62
|
+
0
|
|
63
|
+
}
|
|
64
|
+
fn last_modified(&self) -> u64 {
|
|
65
|
+
0
|
|
66
|
+
}
|
|
67
|
+
fn created_time(&self) -> u64 {
|
|
68
|
+
0
|
|
69
|
+
}
|
|
70
|
+
fn size(&self) -> u64 {
|
|
71
|
+
0
|
|
72
|
+
}
|
|
73
|
+
fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
|
|
74
|
+
Ok(())
|
|
75
|
+
}
|
|
76
|
+
fn unlink(&mut self) -> crate::Result<()> {
|
|
77
|
+
Ok(())
|
|
78
|
+
}
|
|
79
|
+
fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
|
|
80
|
+
Poll::Ready(Ok(8192))
|
|
81
|
+
}
|
|
82
|
+
fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
|
|
83
|
+
Poll::Ready(Ok(8192))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
impl CloneableVirtualFile for NullFile {}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
//! Common [`FileSystem`] operations.
|
|
2
|
+
#![allow(dead_code)] // Most of these helpers are used during testing
|
|
3
|
+
|
|
4
|
+
use std::{
|
|
5
|
+
collections::VecDeque,
|
|
6
|
+
path::{Path, PathBuf},
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
use futures::future::BoxFuture;
|
|
10
|
+
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
11
|
+
|
|
12
|
+
use crate::{DirEntry, FileSystem, FsError};
|
|
13
|
+
|
|
14
|
+
/// Does this item exists?
|
|
15
|
+
pub fn exists<F>(fs: &F, path: impl AsRef<Path>) -> bool
|
|
16
|
+
where
|
|
17
|
+
F: FileSystem + ?Sized,
|
|
18
|
+
{
|
|
19
|
+
fs.metadata(path.as_ref()).is_ok()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Does this path refer to a directory?
|
|
23
|
+
pub fn is_dir<F>(fs: &F, path: impl AsRef<Path>) -> bool
|
|
24
|
+
where
|
|
25
|
+
F: FileSystem + ?Sized,
|
|
26
|
+
{
|
|
27
|
+
match fs.metadata(path.as_ref()) {
|
|
28
|
+
Ok(meta) => meta.is_dir(),
|
|
29
|
+
Err(_) => false,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Does this path refer to a file?
|
|
34
|
+
pub fn is_file<F>(fs: &F, path: impl AsRef<Path>) -> bool
|
|
35
|
+
where
|
|
36
|
+
F: FileSystem + ?Sized,
|
|
37
|
+
{
|
|
38
|
+
match fs.metadata(path.as_ref()) {
|
|
39
|
+
Ok(meta) => meta.is_file(),
|
|
40
|
+
Err(_) => false,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Make sure a directory (and all its parents) exist.
|
|
45
|
+
///
|
|
46
|
+
/// This is analogous to [`std::fs::create_dir_all()`].
|
|
47
|
+
pub fn create_dir_all<F>(fs: &F, path: impl AsRef<Path>) -> Result<(), FsError>
|
|
48
|
+
where
|
|
49
|
+
F: FileSystem + ?Sized,
|
|
50
|
+
{
|
|
51
|
+
let path = path.as_ref();
|
|
52
|
+
if let Some(parent) = path.parent() {
|
|
53
|
+
create_dir_all(fs, parent)?;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if let Ok(metadata) = fs.metadata(path) {
|
|
57
|
+
if metadata.is_dir() {
|
|
58
|
+
return Ok(());
|
|
59
|
+
}
|
|
60
|
+
if metadata.is_file() {
|
|
61
|
+
return Err(FsError::BaseNotDirectory);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fs.create_dir(path)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static WHITEOUT_PREFIX: &str = ".wh.";
|
|
69
|
+
|
|
70
|
+
/// Creates a white out file which hides it from secondary file systems
|
|
71
|
+
pub fn create_white_out<F>(fs: &F, path: impl AsRef<Path>) -> Result<(), FsError>
|
|
72
|
+
where
|
|
73
|
+
F: FileSystem + ?Sized,
|
|
74
|
+
{
|
|
75
|
+
if let Some(filename) = path.as_ref().file_name() {
|
|
76
|
+
let mut path = path.as_ref().to_owned();
|
|
77
|
+
path.set_file_name(format!("{}{}", WHITEOUT_PREFIX, filename.to_string_lossy()));
|
|
78
|
+
|
|
79
|
+
if let Some(parent) = path.parent() {
|
|
80
|
+
create_dir_all(fs, parent).ok();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fs.new_open_options()
|
|
84
|
+
.create_new(true)
|
|
85
|
+
.truncate(true)
|
|
86
|
+
.write(true)
|
|
87
|
+
.open(path)?;
|
|
88
|
+
Ok(())
|
|
89
|
+
} else {
|
|
90
|
+
Err(FsError::EntryNotFound)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Removes a white out file from the primary
|
|
95
|
+
pub fn remove_white_out<F>(fs: &F, path: impl AsRef<Path>)
|
|
96
|
+
where
|
|
97
|
+
F: FileSystem + ?Sized,
|
|
98
|
+
{
|
|
99
|
+
if let Some(filename) = path.as_ref().file_name() {
|
|
100
|
+
let mut path = path.as_ref().to_owned();
|
|
101
|
+
path.set_file_name(format!("{}{}", WHITEOUT_PREFIX, filename.to_string_lossy()));
|
|
102
|
+
fs.remove_file(&path).ok();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Returns true if the path has been hidden by a whiteout file
|
|
107
|
+
pub fn has_white_out<F>(fs: &F, path: impl AsRef<Path>) -> bool
|
|
108
|
+
where
|
|
109
|
+
F: FileSystem + ?Sized,
|
|
110
|
+
{
|
|
111
|
+
if let Some(filename) = path.as_ref().file_name() {
|
|
112
|
+
let mut path = path.as_ref().to_owned();
|
|
113
|
+
path.set_file_name(format!("{}{}", WHITEOUT_PREFIX, filename.to_string_lossy()));
|
|
114
|
+
fs.metadata(&path).is_ok()
|
|
115
|
+
} else {
|
|
116
|
+
false
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Returns true if the path is a whiteout file
|
|
121
|
+
pub fn is_white_out(path: impl AsRef<Path>) -> Option<PathBuf> {
|
|
122
|
+
if let Some(filename) = path.as_ref().file_name()
|
|
123
|
+
&& let Some(filename) = filename.to_string_lossy().strip_prefix(WHITEOUT_PREFIX)
|
|
124
|
+
{
|
|
125
|
+
let mut path = path.as_ref().to_owned();
|
|
126
|
+
path.set_file_name(filename);
|
|
127
|
+
return Some(path);
|
|
128
|
+
}
|
|
129
|
+
None
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Copies the reference of a file from one file system to another
|
|
133
|
+
pub fn copy_reference<'a>(
|
|
134
|
+
source: &'a (impl FileSystem + ?Sized),
|
|
135
|
+
destination: &'a (impl FileSystem + ?Sized),
|
|
136
|
+
path: &'a Path,
|
|
137
|
+
) -> BoxFuture<'a, Result<(), std::io::Error>> {
|
|
138
|
+
Box::pin(async { copy_reference_ext(source, destination, path, path).await })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Copies the reference of a file from one file system to another
|
|
142
|
+
pub fn copy_reference_ext<'a>(
|
|
143
|
+
source: &'a (impl FileSystem + ?Sized),
|
|
144
|
+
destination: &'a (impl FileSystem + ?Sized),
|
|
145
|
+
from: &Path,
|
|
146
|
+
to: &Path,
|
|
147
|
+
) -> BoxFuture<'a, Result<(), std::io::Error>> {
|
|
148
|
+
let from = from.to_owned();
|
|
149
|
+
let to = to.to_owned();
|
|
150
|
+
Box::pin(async move {
|
|
151
|
+
let src = source.new_open_options().read(true).open(from)?;
|
|
152
|
+
let mut dst = destination
|
|
153
|
+
.new_open_options()
|
|
154
|
+
.create(true)
|
|
155
|
+
.write(true)
|
|
156
|
+
.truncate(true)
|
|
157
|
+
.open(to)?;
|
|
158
|
+
|
|
159
|
+
dst.copy_reference(src).await?;
|
|
160
|
+
Ok(())
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Move a file across filesystem boundaries by copying it into the destination
|
|
165
|
+
/// and then removing the original.
|
|
166
|
+
pub fn move_across_filesystems<'a>(
|
|
167
|
+
source: &'a (impl FileSystem + ?Sized),
|
|
168
|
+
destination: &'a (impl FileSystem + ?Sized),
|
|
169
|
+
from: &'a Path,
|
|
170
|
+
to: &'a Path,
|
|
171
|
+
) -> BoxFuture<'a, crate::Result<()>> {
|
|
172
|
+
let from = from.to_owned();
|
|
173
|
+
let to = to.to_owned();
|
|
174
|
+
Box::pin(async move {
|
|
175
|
+
let metadata = source.metadata(&from)?;
|
|
176
|
+
if metadata.is_dir() {
|
|
177
|
+
return Err(FsError::InvalidInput);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
copy_reference_ext(source, destination, &from, &to)
|
|
181
|
+
.await
|
|
182
|
+
.map_err(FsError::from)?;
|
|
183
|
+
|
|
184
|
+
if let Err(error) = source.remove_file(&from) {
|
|
185
|
+
tracing::warn!(
|
|
186
|
+
?from,
|
|
187
|
+
?to,
|
|
188
|
+
?error,
|
|
189
|
+
"Failed to remove file after cross-filesystem rename"
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
Ok(())
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Asynchronously write some bytes to a file.
|
|
198
|
+
///
|
|
199
|
+
/// This is analogous to [`std::fs::write()`].
|
|
200
|
+
pub async fn write<F>(
|
|
201
|
+
fs: &F,
|
|
202
|
+
path: impl AsRef<Path> + Send,
|
|
203
|
+
data: impl AsRef<[u8]> + Send,
|
|
204
|
+
) -> Result<(), FsError>
|
|
205
|
+
where
|
|
206
|
+
F: FileSystem + ?Sized,
|
|
207
|
+
{
|
|
208
|
+
let path = path.as_ref();
|
|
209
|
+
let data = data.as_ref();
|
|
210
|
+
|
|
211
|
+
let mut f = fs
|
|
212
|
+
.new_open_options()
|
|
213
|
+
.create(true)
|
|
214
|
+
.truncate(true)
|
|
215
|
+
.write(true)
|
|
216
|
+
.open(path)?;
|
|
217
|
+
|
|
218
|
+
f.write_all(data).await?;
|
|
219
|
+
f.flush().await?;
|
|
220
|
+
|
|
221
|
+
Ok(())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/// Asynchronously read a file's contents into memory.
|
|
225
|
+
///
|
|
226
|
+
/// This is analogous to [`std::fs::read()`].
|
|
227
|
+
pub async fn read<F>(fs: &F, path: impl AsRef<Path> + Send) -> Result<Vec<u8>, FsError>
|
|
228
|
+
where
|
|
229
|
+
F: FileSystem + ?Sized,
|
|
230
|
+
{
|
|
231
|
+
let mut f = fs.new_open_options().read(true).open(path.as_ref())?;
|
|
232
|
+
let mut buffer = Vec::new();
|
|
233
|
+
f.read_to_end(&mut buffer).await?;
|
|
234
|
+
|
|
235
|
+
Ok(buffer)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// Asynchronously read a file's contents into memory as a string.
|
|
239
|
+
///
|
|
240
|
+
/// This is analogous to [`std::fs::read_to_string()`].
|
|
241
|
+
pub async fn read_to_string<F>(fs: &F, path: impl AsRef<Path> + Send) -> Result<String, FsError>
|
|
242
|
+
where
|
|
243
|
+
F: FileSystem + ?Sized,
|
|
244
|
+
{
|
|
245
|
+
let mut f = fs.new_open_options().read(true).open(path.as_ref())?;
|
|
246
|
+
let mut buffer = String::new();
|
|
247
|
+
f.read_to_string(&mut buffer).await?;
|
|
248
|
+
|
|
249
|
+
Ok(buffer)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// Update a file's modification and access times, creating the file if it
|
|
253
|
+
/// doesn't already exist.
|
|
254
|
+
pub fn touch<F>(fs: &F, path: impl AsRef<Path> + Send) -> Result<(), FsError>
|
|
255
|
+
where
|
|
256
|
+
F: FileSystem + ?Sized,
|
|
257
|
+
{
|
|
258
|
+
let _ = fs.new_open_options().create(true).write(true).open(path)?;
|
|
259
|
+
|
|
260
|
+
Ok(())
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// Recursively iterate over all paths inside a directory, ignoring any
|
|
264
|
+
/// errors that may occur along the way.
|
|
265
|
+
pub fn walk<F>(fs: &F, path: impl AsRef<Path>) -> Box<dyn Iterator<Item = DirEntry> + '_>
|
|
266
|
+
where
|
|
267
|
+
F: FileSystem + ?Sized,
|
|
268
|
+
{
|
|
269
|
+
let path = path.as_ref();
|
|
270
|
+
let mut dirs_to_visit: VecDeque<_> = fs
|
|
271
|
+
.read_dir(path)
|
|
272
|
+
.ok()
|
|
273
|
+
.into_iter()
|
|
274
|
+
.flatten()
|
|
275
|
+
.filter_map(|result| result.ok())
|
|
276
|
+
.collect();
|
|
277
|
+
|
|
278
|
+
Box::new(std::iter::from_fn(move || {
|
|
279
|
+
let next = dirs_to_visit.pop_back()?;
|
|
280
|
+
|
|
281
|
+
if let Ok(children) = fs.read_dir(&next.path) {
|
|
282
|
+
dirs_to_visit.extend(children.flatten());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
Some(next)
|
|
286
|
+
}))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[cfg(test)]
|
|
290
|
+
mod tests {
|
|
291
|
+
use super::*;
|
|
292
|
+
use crate::mem_fs::FileSystem as MemFS;
|
|
293
|
+
use tokio::io::AsyncReadExt;
|
|
294
|
+
|
|
295
|
+
#[tokio::test]
|
|
296
|
+
async fn write() {
|
|
297
|
+
let fs = MemFS::default();
|
|
298
|
+
|
|
299
|
+
super::write(&fs, "/file.txt", b"Hello, World!")
|
|
300
|
+
.await
|
|
301
|
+
.unwrap();
|
|
302
|
+
|
|
303
|
+
let mut contents = String::new();
|
|
304
|
+
fs.new_open_options()
|
|
305
|
+
.read(true)
|
|
306
|
+
.open("/file.txt")
|
|
307
|
+
.unwrap()
|
|
308
|
+
.read_to_string(&mut contents)
|
|
309
|
+
.await
|
|
310
|
+
.unwrap();
|
|
311
|
+
assert_eq!(contents, "Hello, World!");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#[tokio::test]
|
|
315
|
+
async fn read() {
|
|
316
|
+
let fs = MemFS::default();
|
|
317
|
+
fs.new_open_options()
|
|
318
|
+
.create(true)
|
|
319
|
+
.write(true)
|
|
320
|
+
.open("/file.txt")
|
|
321
|
+
.unwrap()
|
|
322
|
+
.write_all(b"Hello, World!")
|
|
323
|
+
.await
|
|
324
|
+
.unwrap();
|
|
325
|
+
|
|
326
|
+
let contents = super::read_to_string(&fs, "/file.txt").await.unwrap();
|
|
327
|
+
assert_eq!(contents, "Hello, World!");
|
|
328
|
+
|
|
329
|
+
let contents = super::read(&fs, "/file.txt").await.unwrap();
|
|
330
|
+
assert_eq!(contents, b"Hello, World!");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#[tokio::test]
|
|
334
|
+
async fn create_dir_all() {
|
|
335
|
+
let fs = MemFS::default();
|
|
336
|
+
super::write(&fs, "/file.txt", b"").await.unwrap();
|
|
337
|
+
|
|
338
|
+
assert!(!super::exists(&fs, "/really/nested/directory"));
|
|
339
|
+
super::create_dir_all(&fs, "/really/nested/directory").unwrap();
|
|
340
|
+
assert!(super::exists(&fs, "/really/nested/directory"));
|
|
341
|
+
|
|
342
|
+
// It's okay to create the same directory multiple times
|
|
343
|
+
super::create_dir_all(&fs, "/really/nested/directory").unwrap();
|
|
344
|
+
|
|
345
|
+
// You can't create a directory on top of a file
|
|
346
|
+
assert_eq!(
|
|
347
|
+
super::create_dir_all(&fs, "/file.txt").unwrap_err(),
|
|
348
|
+
FsError::BaseNotDirectory
|
|
349
|
+
);
|
|
350
|
+
assert_eq!(
|
|
351
|
+
super::create_dir_all(&fs, "/file.txt/invalid/path").unwrap_err(),
|
|
352
|
+
FsError::BaseNotDirectory
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
#[tokio::test]
|
|
357
|
+
async fn touch() {
|
|
358
|
+
let fs = MemFS::default();
|
|
359
|
+
|
|
360
|
+
super::touch(&fs, "/file.txt").unwrap();
|
|
361
|
+
|
|
362
|
+
assert_eq!(super::read(&fs, "/file.txt").await.unwrap(), b"");
|
|
363
|
+
}
|
|
364
|
+
}
|