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
10pub struct LocalExecutor {
12 fs: Arc<dyn FileSystem>,
13}
14
15impl LocalExecutor {
16 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}