Skip to main content

fslite_command/
local.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use fslite_core::{FileSystem, FsResult, RequestContext};
5use futures::StreamExt;
6
7use crate::executor::Executor;
8use crate::{Command, CommandOutput};
9
10/// Executes commands directly against an in-process `FileSystem` backend.
11pub struct LocalExecutor {
12    fs: Arc<dyn FileSystem>,
13}
14
15impl LocalExecutor {
16    /// Wraps a backend for local, in-process execution.
17    pub fn new(fs: Arc<dyn FileSystem>) -> Self {
18        Self { fs }
19    }
20}
21
22async fn drain(stream: fslite_core::ByteStream) -> FsResult<Vec<u8>> {
23    let mut stream = stream;
24    let mut bytes = Vec::new();
25    while let Some(chunk) = stream.next().await {
26        bytes.extend_from_slice(&chunk?);
27    }
28    Ok(bytes)
29}
30
31#[async_trait]
32impl Executor for LocalExecutor {
33    async fn execute(&self, ctx: &RequestContext, command: Command) -> FsResult<CommandOutput> {
34        Ok(match command {
35            Command::WorkspaceUsage => CommandOutput::Usage(self.fs.workspace_usage(ctx).await?),
36            Command::Stat { path, options } => {
37                CommandOutput::Node(self.fs.stat(ctx, &path, options).await?)
38            }
39            Command::Exists { path, options } => {
40                CommandOutput::Exists(self.fs.exists(ctx, &path, options).await?)
41            }
42            Command::ReadDir { path, page } => {
43                CommandOutput::Nodes(self.fs.read_dir(ctx, &path, page).await?)
44            }
45            Command::Tree {
46                path,
47                options,
48                page,
49            } => CommandOutput::Tree(self.fs.tree(ctx, &path, options, page).await?),
50            Command::Mkdir { path, options } => {
51                CommandOutput::Node(self.fs.mkdir(ctx, &path, options).await?)
52            }
53            Command::Read { path, options } => {
54                let file = self.fs.read(ctx, &path, options).await?;
55                let logical_length = file.logical_length;
56                let revision = file.revision;
57                let range = file.range;
58                let bytes = drain(file.into_stream()).await?;
59                CommandOutput::Content {
60                    logical_length,
61                    revision,
62                    range,
63                    bytes,
64                }
65            }
66            Command::Write {
67                path,
68                bytes,
69                options,
70            } => {
71                let source = fslite_core::WriteSource::from_bytes(bytes);
72                CommandOutput::Node(self.fs.write(ctx, &path, source, options).await?)
73            }
74            Command::WriteAt {
75                path,
76                offset,
77                bytes,
78                options,
79            } => {
80                let source = fslite_core::WriteSource::from_bytes(bytes);
81                CommandOutput::Node(
82                    self.fs
83                        .write_at(ctx, &path, offset, source, options)
84                        .await?,
85                )
86            }
87            Command::Append {
88                path,
89                bytes,
90                options,
91            } => {
92                let source = fslite_core::WriteSource::from_bytes(bytes);
93                CommandOutput::Node(self.fs.append(ctx, &path, source, options).await?)
94            }
95            Command::Truncate {
96                path,
97                length,
98                options,
99            } => CommandOutput::Node(self.fs.truncate(ctx, &path, length, options).await?),
100            Command::Touch { path, options } => {
101                CommandOutput::Node(self.fs.touch(ctx, &path, options).await?)
102            }
103            Command::Copy { from, to, options } => {
104                CommandOutput::Node(self.fs.copy(ctx, &from, &to, options).await?)
105            }
106            Command::Move { from, to, options } => {
107                CommandOutput::Node(self.fs.move_path(ctx, &from, &to, options).await?)
108            }
109            Command::Remove { path, options } => {
110                self.fs.remove(ctx, &path, options).await?;
111                CommandOutput::Unit
112            }
113            Command::Symlink {
114                target,
115                link,
116                options,
117            } => CommandOutput::Node(self.fs.symlink(ctx, &target, &link, options).await?),
118            Command::ReadLink { path } => {
119                CommandOutput::LinkTarget(self.fs.read_link(ctx, &path).await?)
120            }
121            Command::Trash { path, options } => {
122                CommandOutput::Trash(self.fs.trash(ctx, &path, options).await?)
123            }
124            Command::ListTrash { page } => {
125                CommandOutput::TrashList(self.fs.list_trash(ctx, page).await?)
126            }
127            Command::Restore {
128                trash,
129                destination,
130                options,
131            } => CommandOutput::Node(
132                self.fs
133                    .restore(ctx, trash, destination.as_ref(), options)
134                    .await?,
135            ),
136            Command::Purge { trash } => {
137                self.fs.purge(ctx, trash).await?;
138                CommandOutput::Unit
139            }
140            Command::SetAttribute {
141                path,
142                key,
143                value,
144                options,
145            } => CommandOutput::Node(
146                self.fs
147                    .set_attribute(ctx, &path, &key, &value, options)
148                    .await?,
149            ),
150            Command::RemoveAttribute { path, key, options } => {
151                CommandOutput::Node(self.fs.remove_attribute(ctx, &path, &key, options).await?)
152            }
153            Command::Glob { pattern, page } => {
154                CommandOutput::Nodes(self.fs.glob(ctx, &pattern, page).await?)
155            }
156            Command::Find { query, page } => {
157                CommandOutput::Nodes(self.fs.find(ctx, query, page).await?)
158            }
159            Command::SearchContent { query, page } => {
160                CommandOutput::SearchMatches(self.fs.search_content(ctx, query, page).await?)
161            }
162            Command::Changes { after, page } => {
163                CommandOutput::Changes(self.fs.changes(ctx, after, page).await?)
164            }
165            Command::Batch(operations) => {
166                CommandOutput::Batch(self.fs.batch(ctx, operations).await?)
167            }
168        })
169    }
170}