mod local;
use std::collections::BTreeMap;
use std::io;
use bytes::Bytes;
use ferrin_spec::BoxFuture;
use ferrin_spec::BoxStream;
pub use local::LocalProcessSandbox;
use tokio_util::sync::CancellationToken;
pub type ByteStream = BoxStream<'static, io::Result<Bytes>>;
#[derive(Debug, Clone)]
pub struct ReadFileOptions {
pub path: String,
pub cancellation: CancellationToken,
}
impl ReadFileOptions {
#[must_use]
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
cancellation: CancellationToken::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ReadTextFileOptions {
pub path: String,
pub encoding: Option<String>,
pub start_line: Option<usize>,
pub end_line: Option<usize>,
pub cancellation: CancellationToken,
}
impl ReadTextFileOptions {
#[must_use]
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
encoding: None,
start_line: None,
end_line: None,
cancellation: CancellationToken::new(),
}
}
#[must_use]
pub fn lines(mut self, start_line: usize, end_line: usize) -> Self {
self.start_line = Some(start_line);
self.end_line = Some(end_line);
self
}
}
#[derive(Debug)]
pub struct WriteFileOptions<C> {
pub path: String,
pub content: C,
pub cancellation: CancellationToken,
}
impl<C> WriteFileOptions<C> {
#[must_use]
pub fn new(path: impl Into<String>, content: C) -> Self {
Self {
path: path.into(),
content,
cancellation: CancellationToken::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ProcessOptions {
pub command: String,
pub working_directory: Option<String>,
pub env: BTreeMap<String, String>,
pub cancellation: CancellationToken,
}
impl ProcessOptions {
#[must_use]
pub fn new(command: impl Into<String>) -> Self {
Self {
command: command.into(),
working_directory: None,
env: BTreeMap::new(),
cancellation: CancellationToken::new(),
}
}
#[must_use]
pub fn in_directory(mut self, directory: impl Into<String>) -> Self {
self.working_directory = Some(directory.into());
self
}
#[must_use]
pub fn env(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.env.insert(name.into(), value.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcessResult {
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
pub trait SandboxProcess: Send {
fn pid(&self) -> Option<u32>;
fn take_stdout(&mut self) -> Option<ByteStream>;
fn take_stderr(&mut self) -> Option<ByteStream>;
fn wait(&mut self) -> BoxFuture<'_, io::Result<i32>>;
fn kill(&mut self) -> BoxFuture<'_, io::Result<()>>;
}
pub trait Sandbox: Send + Sync {
fn description(&self) -> &str;
fn read_file(&self, options: ReadFileOptions) -> BoxFuture<'_, io::Result<Option<ByteStream>>>;
fn read_binary_file(
&self,
options: ReadFileOptions,
) -> BoxFuture<'_, io::Result<Option<Bytes>>>;
fn read_text_file(
&self,
options: ReadTextFileOptions,
) -> BoxFuture<'_, io::Result<Option<String>>>;
fn write_file(&self, options: WriteFileOptions<ByteStream>) -> BoxFuture<'_, io::Result<()>>;
fn write_binary_file(&self, options: WriteFileOptions<Bytes>) -> BoxFuture<'_, io::Result<()>>;
fn write_text_file(&self, options: WriteFileOptions<String>) -> BoxFuture<'_, io::Result<()>>;
fn spawn(&self, options: ProcessOptions) -> BoxFuture<'_, io::Result<Box<dyn SandboxProcess>>>;
fn run(&self, options: ProcessOptions) -> BoxFuture<'_, io::Result<ProcessResult>>;
}