ferrin_tool/sandbox/
mod.rs1mod local;
9
10use std::collections::BTreeMap;
11use std::io;
12
13use bytes::Bytes;
14use ferrin_spec::BoxFuture;
15use ferrin_spec::BoxStream;
16pub use local::LocalProcessSandbox;
17use tokio_util::sync::CancellationToken;
18
19pub type ByteStream = BoxStream<'static, io::Result<Bytes>>;
21
22#[derive(Debug, Clone)]
24pub struct ReadFileOptions {
25 pub path: String,
27 pub cancellation: CancellationToken,
29}
30
31impl ReadFileOptions {
32 #[must_use]
34 pub fn new(path: impl Into<String>) -> Self {
35 Self {
36 path: path.into(),
37 cancellation: CancellationToken::new(),
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub struct ReadTextFileOptions {
45 pub path: String,
47 pub encoding: Option<String>,
49 pub start_line: Option<usize>,
51 pub end_line: Option<usize>,
53 pub cancellation: CancellationToken,
55}
56
57impl ReadTextFileOptions {
58 #[must_use]
60 pub fn new(path: impl Into<String>) -> Self {
61 Self {
62 path: path.into(),
63 encoding: None,
64 start_line: None,
65 end_line: None,
66 cancellation: CancellationToken::new(),
67 }
68 }
69
70 #[must_use]
72 pub fn lines(mut self, start_line: usize, end_line: usize) -> Self {
73 self.start_line = Some(start_line);
74 self.end_line = Some(end_line);
75 self
76 }
77}
78
79#[derive(Debug)]
81pub struct WriteFileOptions<C> {
82 pub path: String,
84 pub content: C,
86 pub cancellation: CancellationToken,
88}
89
90impl<C> WriteFileOptions<C> {
91 #[must_use]
93 pub fn new(path: impl Into<String>, content: C) -> Self {
94 Self {
95 path: path.into(),
96 content,
97 cancellation: CancellationToken::new(),
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct ProcessOptions {
105 pub command: String,
107 pub working_directory: Option<String>,
109 pub env: BTreeMap<String, String>,
111 pub cancellation: CancellationToken,
113}
114
115impl ProcessOptions {
116 #[must_use]
118 pub fn new(command: impl Into<String>) -> Self {
119 Self {
120 command: command.into(),
121 working_directory: None,
122 env: BTreeMap::new(),
123 cancellation: CancellationToken::new(),
124 }
125 }
126
127 #[must_use]
129 pub fn in_directory(mut self, directory: impl Into<String>) -> Self {
130 self.working_directory = Some(directory.into());
131 self
132 }
133
134 #[must_use]
136 pub fn env(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
137 self.env.insert(name.into(), value.into());
138 self
139 }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct ProcessResult {
145 pub exit_code: i32,
147 pub stdout: String,
149 pub stderr: String,
151}
152
153pub trait SandboxProcess: Send {
158 fn pid(&self) -> Option<u32>;
160 fn take_stdout(&mut self) -> Option<ByteStream>;
162 fn take_stderr(&mut self) -> Option<ByteStream>;
164 fn wait(&mut self) -> BoxFuture<'_, io::Result<i32>>;
166 fn kill(&mut self) -> BoxFuture<'_, io::Result<()>>;
168}
169
170pub trait Sandbox: Send + Sync {
177 fn description(&self) -> &str;
179 fn read_file(&self, options: ReadFileOptions) -> BoxFuture<'_, io::Result<Option<ByteStream>>>;
181 fn read_binary_file(
183 &self,
184 options: ReadFileOptions,
185 ) -> BoxFuture<'_, io::Result<Option<Bytes>>>;
186 fn read_text_file(
188 &self,
189 options: ReadTextFileOptions,
190 ) -> BoxFuture<'_, io::Result<Option<String>>>;
191 fn write_file(&self, options: WriteFileOptions<ByteStream>) -> BoxFuture<'_, io::Result<()>>;
193 fn write_binary_file(&self, options: WriteFileOptions<Bytes>) -> BoxFuture<'_, io::Result<()>>;
195 fn write_text_file(&self, options: WriteFileOptions<String>) -> BoxFuture<'_, io::Result<()>>;
197 fn spawn(&self, options: ProcessOptions) -> BoxFuture<'_, io::Result<Box<dyn SandboxProcess>>>;
199 fn run(&self, options: ProcessOptions) -> BoxFuture<'_, io::Result<ProcessResult>>;
201}