docs: added documentation, enforced documentation
All checks were successful
CI / build (push) Successful in 7m21s

This commit is contained in:
2026-01-01 18:37:40 +01:00
parent 5e1b0988fd
commit b3365afe5b
10 changed files with 113 additions and 29 deletions

View File

@@ -10,6 +10,7 @@ use super::schroot::SchrootDriver;
use super::ssh::SshDriver;
use super::unshare::UnshareDriver;
/// A ContextDriver is the interface for the logic happening inside a context
pub trait ContextDriver {
fn ensure_available(&self, src: &Path, dest_root: &str) -> io::Result<PathBuf>;
fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()>;
@@ -41,34 +42,52 @@ pub trait ContextDriver {
#[serde(tag = "type")]
#[derive(Default)]
pub enum ContextConfig {
/// Local context: actions executed locally
#[serde(rename = "local")]
#[default]
Local,
/// SSH context: actions over an SSH connection
#[serde(rename = "ssh")]
Ssh {
/// Host for the SSH connection
host: String,
/// User for the SSH connection
user: Option<String>,
/// TCP port for the SSH connection
port: Option<u16>,
},
/// Schroot context: using `schroot`
#[serde(rename = "schroot")]
Schroot {
/// Name of the schroot
name: String,
/// Optional parent context for the Schroot context
parent: Option<String>,
},
/// Unshare context: chroot with dropped permissions (using `unshare`)
#[serde(rename = "unshare")]
Unshare {
/// Path to use for chrooting
path: String,
/// Optional parent context for the Unshare context
parent: Option<String>,
},
}
/// A context, allowing to run commands, read and write files, etc
pub struct Context {
/// Configuration for the context
pub config: ContextConfig,
/// Parent context for the context
///
/// For example, you could have a chroot context over an ssh connection
pub parent: Option<Arc<Context>>,
/// ContextDriver for the context, implementing the logic for actions
driver: Mutex<Option<Box<dyn ContextDriver + Send + Sync>>>,
}
impl Context {
/// Create a context from configuration
pub fn new(config: ContextConfig) -> Self {
let parent = match &config {
ContextConfig::Schroot {
@@ -97,6 +116,7 @@ impl Context {
}
}
/// Create a context with an explicit parent context
pub fn with_parent(config: ContextConfig, parent: Arc<Context>) -> Self {
Self {
config,
@@ -105,6 +125,7 @@ impl Context {
}
}
/// Make a command inside context
pub fn command<S: AsRef<OsStr>>(&self, program: S) -> ContextCommand<'_> {
ContextCommand {
context: self,
@@ -126,6 +147,7 @@ impl Context {
.ensure_available(src, dest_root)
}
/// Create a temp directory inside context
pub fn create_temp_dir(&self) -> io::Result<String> {
self.driver().as_ref().unwrap().create_temp_dir()
}
@@ -143,18 +165,22 @@ impl Context {
self.driver().as_ref().unwrap().list_files(path)
}
/// Copy a path inside context
pub fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
self.driver().as_ref().unwrap().copy_path(src, dest)
}
/// Read a file inside context
pub fn read_file(&self, path: &Path) -> io::Result<String> {
self.driver().as_ref().unwrap().read_file(path)
}
/// Write a file inside context
pub fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
self.driver().as_ref().unwrap().write_file(path, content)
}
/// Create and obtain a specific driver for the context
pub fn driver(
&self,
) -> std::sync::MutexGuard<'_, Option<Box<dyn ContextDriver + Send + Sync>>> {
@@ -182,6 +208,7 @@ impl Context {
driver_lock
}
/// Clone a context
pub fn clone_raw(&self) -> Self {
Self {
config: self.config.clone(),
@@ -207,12 +234,13 @@ pub struct ContextCommand<'a> {
}
impl<'a> ContextCommand<'a> {
/// Add an argument to current command
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.args.push(arg.as_ref().to_string_lossy().to_string());
self
}
// Support chaining args
/// Add multiple command arguments
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
@@ -224,6 +252,7 @@ impl<'a> ContextCommand<'a> {
self
}
/// Set environment variable for command
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
where
K: AsRef<OsStr>,
@@ -236,6 +265,7 @@ impl<'a> ContextCommand<'a> {
self
}
/// Set multiple environment variables for command
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
where
I: IntoIterator<Item = (K, V)>,
@@ -248,11 +278,13 @@ impl<'a> ContextCommand<'a> {
self
}
/// Set current working directory for command
pub fn current_dir<P: AsRef<OsStr>>(&mut self, dir: P) -> &mut Self {
self.cwd = Some(dir.as_ref().to_string_lossy().to_string());
self
}
/// Run command and obtain exit status
pub fn status(&mut self) -> io::Result<std::process::ExitStatus> {
self.context.driver().as_ref().unwrap().run(
&self.program,
@@ -262,7 +294,7 @@ impl<'a> ContextCommand<'a> {
)
}
// Capture output
/// Run command, capturing output
pub fn output(&mut self) -> io::Result<std::process::Output> {
self.context.driver().as_ref().unwrap().run_output(
&self.program,

View File

@@ -26,6 +26,7 @@ impl Default for Config {
}
}
/// Helper managing contexts
pub struct ContextManager {
context: RwLock<Arc<Context>>,
config_path: PathBuf,
@@ -67,10 +68,12 @@ impl ContextManager {
})
}
/// Obtain current ContextManager configuration
pub fn get_config(&self) -> std::sync::RwLockReadGuard<'_, Config> {
self.config.read().unwrap()
}
/// Make a ContextManager using a specific configuration path
pub fn with_path(path: PathBuf) -> Self {
let config = Config::default();
Self {
@@ -80,6 +83,7 @@ impl ContextManager {
}
}
/// Save current context configuration to disk
pub fn save(&self) -> io::Result<()> {
let config = self.config.read().unwrap();
let content = serde_json::to_string_pretty(&*config)
@@ -97,6 +101,7 @@ impl ContextManager {
Context::new(context_config)
}
/// List contexts from configuration
pub fn list_contexts(&self) -> Vec<String> {
self.config
.read()
@@ -107,6 +112,7 @@ impl ContextManager {
.collect()
}
/// Add a context to configuration
pub fn add_context(&self, name: &str, config: ContextConfig) -> io::Result<()> {
self.config
.write()
@@ -116,6 +122,7 @@ impl ContextManager {
self.save()
}
/// Remove context from configuration
pub fn remove_context(&self, name: &str) -> io::Result<()> {
let mut config = self.config.write().unwrap();
if name == "local" {
@@ -137,6 +144,7 @@ impl ContextManager {
Ok(())
}
/// Set current context from name (modifying configuration)
pub fn set_current(&self, name: &str) -> io::Result<()> {
let mut config = self.config.write().unwrap();
if config.contexts.contains_key(name) {
@@ -153,14 +161,18 @@ impl ContextManager {
}
}
/// Set current context, without modifying configuration
pub fn set_current_ephemeral(&self, context: Context) {
*self.context.write().unwrap() = context.into();
}
/// Obtain current context handle
pub fn current(&self) -> Arc<Context> {
self.context.read().unwrap().clone()
}
/// Obtain current context name
/// Will not work for ephemeral context (obtained from config)
pub fn current_name(&self) -> String {
self.config.read().unwrap().context.clone()
}

View File

@@ -9,10 +9,12 @@ pub use api::{Context, ContextCommand, ContextConfig};
pub use manager::ContextManager;
use std::sync::Arc;
/// Obtain global context manager
pub fn manager() -> &'static ContextManager {
&manager::MANAGER
}
/// Obtain current context
pub fn current() -> Arc<Context> {
manager::MANAGER.current()
}