#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IsolationLevel {
None,
#[cfg(target_os = "freebsd")]
Capsicum,
#[cfg(target_os = "freebsd")]
CapsicumProcess,
}
impl IsolationLevel {
#[must_use]
pub fn is_available(self) -> bool {
match self {
Self::None => true,
#[cfg(target_os = "freebsd")]
Self::Capsicum => true,
#[cfg(target_os = "freebsd")]
Self::CapsicumProcess => true,
}
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::None => "none",
#[cfg(target_os = "freebsd")]
Self::Capsicum => "capsicum",
#[cfg(target_os = "freebsd")]
Self::CapsicumProcess => "capsicum-process",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SandboxConfig {
isolation_level: IsolationLevel,
log_channel_capacity: usize,
}
impl SandboxConfig {
#[must_use]
pub fn new() -> Self {
Self {
isolation_level: IsolationLevel::None,
log_channel_capacity: 16,
}
}
#[must_use]
pub fn with_isolation_level(mut self, level: IsolationLevel) -> Self {
self.isolation_level = level;
self
}
#[must_use]
pub fn with_log_channel_capacity(mut self, capacity: usize) -> Self {
self.log_channel_capacity = capacity;
self
}
#[must_use]
pub fn isolation_level(self) -> IsolationLevel {
self.isolation_level
}
#[must_use]
pub fn log_channel_capacity(self) -> usize {
self.log_channel_capacity
}
#[must_use]
pub fn is_platform_supported(self) -> bool {
self.isolation_level.is_available()
}
}
impl Default for SandboxConfig {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn none_is_always_available() {
assert!(IsolationLevel::None.is_available());
}
#[test]
fn none_as_str() {
assert_eq!(IsolationLevel::None.as_str(), "none");
}
#[test]
fn none_is_copy_clone_eq_debug_hash() {
let a = IsolationLevel::None;
let b = a;
assert_eq!(a, b);
assert_eq!(format!("{a:?}"), "None");
let mut set = std::collections::HashSet::new();
set.insert(a);
assert!(set.contains(&b));
}
#[cfg(target_os = "freebsd")]
mod freebsd_tests {
use super::*;
#[test]
fn capsicum_is_available_on_freebsd() {
assert!(IsolationLevel::Capsicum.is_available());
}
#[test]
fn capsicum_process_is_available_on_freebsd() {
assert!(IsolationLevel::CapsicumProcess.is_available());
}
#[test]
fn capsicum_as_str() {
assert_eq!(IsolationLevel::Capsicum.as_str(), "capsicum");
}
#[test]
fn capsicum_process_as_str() {
assert_eq!(IsolationLevel::CapsicumProcess.as_str(), "capsicum-process");
}
}
#[test]
fn default_has_none_and_capacity_16() {
let cfg = SandboxConfig::default();
assert_eq!(cfg.isolation_level(), IsolationLevel::None);
assert_eq!(cfg.log_channel_capacity(), 16);
}
#[test]
fn builder_sets_isolation_level() {
let cfg = SandboxConfig::new().with_isolation_level(IsolationLevel::None);
assert_eq!(cfg.isolation_level(), IsolationLevel::None);
}
#[test]
fn builder_sets_log_channel_capacity() {
let cfg = SandboxConfig::new().with_log_channel_capacity(64);
assert_eq!(cfg.log_channel_capacity(), 64);
}
#[test]
fn builder_chains() {
let cfg = SandboxConfig::new()
.with_isolation_level(IsolationLevel::None)
.with_log_channel_capacity(32);
assert_eq!(cfg.isolation_level(), IsolationLevel::None);
assert_eq!(cfg.log_channel_capacity(), 32);
}
#[test]
fn platform_supported_for_none() {
let cfg = SandboxConfig::new().with_isolation_level(IsolationLevel::None);
assert!(cfg.is_platform_supported());
}
#[test]
fn config_is_copy_clone_eq_debug_hash() {
let a = SandboxConfig::new();
let b = a;
assert_eq!(a, b);
assert!(format!("{a:?}").contains("SandboxConfig"));
let mut set = std::collections::HashSet::new();
set.insert(a);
assert!(set.contains(&b));
}
#[test]
fn zero_capacity_is_valid() {
let cfg = SandboxConfig::new().with_log_channel_capacity(0);
assert_eq!(cfg.log_channel_capacity(), 0);
}
#[test]
fn large_capacity_is_valid() {
let cfg = SandboxConfig::new().with_log_channel_capacity(usize::MAX);
assert_eq!(cfg.log_channel_capacity(), usize::MAX);
}
}