exp: cross #2
This commit is contained in:
@@ -1,19 +1,37 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::local::LocalDriver;
|
||||
use super::ssh::SshDriver;
|
||||
use super::schroot::SchrootDriver;
|
||||
use super::ssh::SshDriver;
|
||||
use super::unshare::UnshareDriver;
|
||||
|
||||
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<()>;
|
||||
fn list_files(&self, path: &Path) -> io::Result<Vec<PathBuf>>;
|
||||
fn run(&self, program: &str, args: &[String], env: &[(String, String)]) -> io::Result<std::process::ExitStatus>;
|
||||
fn run_output(&self, program: &str, args: &[String], env: &[(String, String)]) -> io::Result<std::process::Output>;
|
||||
fn prepare_work_dir(&self) -> io::Result<String>;
|
||||
fn run(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
cwd: Option<&str>,
|
||||
) -> io::Result<std::process::ExitStatus>;
|
||||
fn run_output(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
cwd: Option<&str>,
|
||||
) -> io::Result<std::process::Output>;
|
||||
fn create_temp_dir(&self) -> io::Result<String>;
|
||||
fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()>;
|
||||
fn read_file(&self, path: &Path) -> io::Result<String>;
|
||||
fn write_file(&self, path: &Path, content: &str) -> io::Result<()>;
|
||||
}
|
||||
|
||||
/// Represents an execution environment (Local or via SSH).
|
||||
@@ -35,19 +53,55 @@ pub enum ContextConfig {
|
||||
#[serde(rename = "schroot")]
|
||||
Schroot {
|
||||
name: String,
|
||||
}
|
||||
parent: Option<String>,
|
||||
},
|
||||
#[serde(rename = "unshare")]
|
||||
Unshare {
|
||||
path: String,
|
||||
parent: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
config: ContextConfig,
|
||||
driver: RefCell<Option<Box<dyn ContextDriver>>>,
|
||||
pub config: ContextConfig,
|
||||
pub parent: Option<Arc<Context>>,
|
||||
driver: Mutex<Option<Box<dyn ContextDriver + Send + Sync>>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(config: ContextConfig) -> Self {
|
||||
let parent = match &config {
|
||||
ContextConfig::Schroot {
|
||||
parent: Some(parent_name),
|
||||
..
|
||||
}
|
||||
| ContextConfig::Unshare {
|
||||
parent: Some(parent_name),
|
||||
..
|
||||
} => {
|
||||
let config_lock = crate::context::manager::MANAGER.get_config();
|
||||
let parent_config = config_lock
|
||||
.contexts
|
||||
.get(parent_name)
|
||||
.cloned()
|
||||
.expect("Parent context not found");
|
||||
Some(Arc::new(Context::new(parent_config)))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Self {
|
||||
config,
|
||||
driver: RefCell::new(None),
|
||||
parent,
|
||||
driver: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_parent(config: ContextConfig, parent: Arc<Context>) -> Self {
|
||||
Self {
|
||||
config,
|
||||
parent: Some(parent),
|
||||
driver: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +111,7 @@ impl Context {
|
||||
program: program.as_ref().to_string_lossy().to_string(),
|
||||
args: Vec::new(),
|
||||
env: Vec::new(),
|
||||
cwd: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +120,14 @@ impl Context {
|
||||
/// If Local: Returns the absolute path of `src`.
|
||||
/// If Remote: Copies `src` to `dest_root` on the remote and returns the path to the copied entity.
|
||||
pub fn ensure_available(&self, src: &Path, dest_root: &str) -> io::Result<PathBuf> {
|
||||
self.driver().ensure_available(src, dest_root)
|
||||
self.driver()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.ensure_available(src, dest_root)
|
||||
}
|
||||
|
||||
pub fn prepare_work_dir(&self) -> io::Result<String> {
|
||||
self.driver().prepare_work_dir()
|
||||
pub fn create_temp_dir(&self) -> io::Result<String> {
|
||||
self.driver().as_ref().unwrap().create_temp_dir()
|
||||
}
|
||||
|
||||
/// Retrieve a file or directory from the context to the local filesystem.
|
||||
@@ -77,31 +135,59 @@ impl Context {
|
||||
/// The `src` path is on the context, `dest` is on the local machine.
|
||||
/// If `src` is a directory, it is copied recursively.
|
||||
pub fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
|
||||
self.driver().retrieve_path(src, dest)
|
||||
self.driver().as_ref().unwrap().retrieve_path(src, dest)
|
||||
}
|
||||
|
||||
/// List files in a directory on the context.
|
||||
pub fn list_files(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
self.driver().list_files(path)
|
||||
self.driver().as_ref().unwrap().list_files(path)
|
||||
}
|
||||
|
||||
fn driver(&self) -> Ref<Box<dyn ContextDriver>> {
|
||||
if self.driver.borrow().is_none() {
|
||||
let driver: Box<dyn ContextDriver> = match &self.config {
|
||||
pub fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
|
||||
self.driver().as_ref().unwrap().copy_path(src, dest)
|
||||
}
|
||||
|
||||
pub fn read_file(&self, path: &Path) -> io::Result<String> {
|
||||
self.driver().as_ref().unwrap().read_file(path)
|
||||
}
|
||||
|
||||
pub fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
|
||||
self.driver().as_ref().unwrap().write_file(path, content)
|
||||
}
|
||||
|
||||
pub fn driver(
|
||||
&self,
|
||||
) -> std::sync::MutexGuard<'_, Option<Box<dyn ContextDriver + Send + Sync>>> {
|
||||
let mut driver_lock = self.driver.lock().unwrap();
|
||||
if driver_lock.is_none() {
|
||||
let driver: Box<dyn ContextDriver + Send + Sync> = match &self.config {
|
||||
ContextConfig::Local => Box::new(LocalDriver),
|
||||
ContextConfig::Ssh { host, user, port } => Box::new(SshDriver {
|
||||
host: host.clone(),
|
||||
user: user.clone(),
|
||||
port: *port,
|
||||
}),
|
||||
ContextConfig::Schroot { name } => Box::new(SchrootDriver {
|
||||
ContextConfig::Schroot { name, .. } => Box::new(SchrootDriver {
|
||||
name: name.clone(),
|
||||
session: RefCell::new(None),
|
||||
session: std::sync::Mutex::new(None),
|
||||
parent: self.parent.clone(),
|
||||
}),
|
||||
ContextConfig::Unshare { path, .. } => Box::new(UnshareDriver {
|
||||
path: path.clone(),
|
||||
parent: self.parent.clone(),
|
||||
}),
|
||||
};
|
||||
*self.driver.borrow_mut() = Some(driver);
|
||||
*driver_lock = Some(driver);
|
||||
}
|
||||
driver_lock
|
||||
}
|
||||
|
||||
pub fn clone_raw(&self) -> Self {
|
||||
Self {
|
||||
config: self.config.clone(),
|
||||
parent: self.parent.clone(),
|
||||
driver: std::sync::Mutex::new(None),
|
||||
}
|
||||
Ref::map(self.driver.borrow(), |opt| opt.as_ref().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +203,7 @@ pub struct ContextCommand<'a> {
|
||||
program: String,
|
||||
args: Vec<String>,
|
||||
env: Vec<(String, String)>,
|
||||
cwd: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> ContextCommand<'a> {
|
||||
@@ -161,12 +248,27 @@ impl<'a> ContextCommand<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn status(&mut self) -> io::Result<std::process::ExitStatus> {
|
||||
self.context.driver().run(&self.program, &self.args, &self.env)
|
||||
self.context.driver().as_ref().unwrap().run(
|
||||
&self.program,
|
||||
&self.args,
|
||||
&self.env,
|
||||
self.cwd.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
// Capture output
|
||||
pub fn output(&mut self) -> io::Result<std::process::Output> {
|
||||
self.context.driver().run_output(&self.program, &self.args, &self.env)
|
||||
self.context.driver().as_ref().unwrap().run_output(
|
||||
&self.program,
|
||||
&self.args,
|
||||
&self.env,
|
||||
self.cwd.as_deref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user