mod fs;
mod path;
mod rg;
mod shell;
pub use fs::{DirEntry, FileRead, FileStat, FsError};
pub use path::PathError;
pub use rg::rg_program;
pub use shell::{ExecError, ExecOutput, ExecRequest};
use std::path::{Path, PathBuf};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PathPolicy {
#[default]
Jailed,
Unrestricted,
}
#[derive(Debug, Clone)]
pub struct ExecLimits {
pub default_timeout: Duration,
pub max_timeout: Duration,
pub max_output_bytes: usize,
pub kill_grace: Duration,
}
impl Default for ExecLimits {
fn default() -> Self {
Self {
default_timeout: Duration::from_secs(10),
max_timeout: Duration::from_mins(10),
max_output_bytes: 30_000,
kill_grace: Duration::from_secs(2),
}
}
}
#[derive(Debug, Clone)]
pub struct HostConfig {
pub workspace_root: PathBuf,
pub path_policy: PathPolicy,
pub exec: ExecLimits,
pub shell_program: String,
pub login_shell: bool,
}
impl HostConfig {
pub fn new(workspace_root: impl Into<PathBuf>) -> Self {
Self {
workspace_root: workspace_root.into(),
path_policy: PathPolicy::default(),
exec: ExecLimits::default(),
shell_program: "bash".to_string(),
login_shell: true,
}
}
}
#[derive(Debug, Clone)]
pub struct Host {
pub(crate) workspace_root: PathBuf,
pub(crate) path_policy: PathPolicy,
pub(crate) limits: ExecLimits,
pub(crate) shell_program: String,
pub(crate) login_shell: bool,
}
impl Host {
pub fn new(config: HostConfig) -> Result<Self, PathError> {
let workspace_root = std::fs::canonicalize(&config.workspace_root).map_err(|e| {
PathError::InvalidRoot(format!("{}: {e}", config.workspace_root.display()))
})?;
Ok(Self {
workspace_root,
path_policy: config.path_policy,
limits: config.exec,
shell_program: config.shell_program,
login_shell: config.login_shell,
})
}
#[must_use]
pub fn workspace_root(&self) -> &Path {
&self.workspace_root
}
#[must_use]
pub fn path_policy(&self) -> PathPolicy {
self.path_policy
}
#[must_use]
pub fn limits(&self) -> &ExecLimits {
&self.limits
}
}
#[cfg(test)]
pub(crate) fn test_host(root: &Path, policy: PathPolicy, login: bool) -> Host {
let mut config = HostConfig::new(root);
config.path_policy = policy;
config.login_shell = login;
Host::new(config).expect("test host")
}