use std::path::Path;
#[derive(Default)]
pub struct ArgsBuilder<T1: Emptiable, T2: Emptiable> {
args: CommonArgs,
chroot: T1,
command: T2,
}
#[derive(Default)]
struct CommonArgs {
namespace: Vec<String>,
user: Vec<String>,
sysmounts: Vec<String>,
usermounts: Vec<String>,
tmpfs: Vec<String>,
add_cap: Vec<String>,
variables: Vec<String>,
resolv: Vec<String>,
directory: Vec<String>,
}
pub trait Emptiable {}
#[derive(Default)]
pub struct Emptied;
impl Emptiable for Emptied {}
impl Emptiable for Vec<String> {}
#[allow(dead_code)]
pub type HasNeither = ArgsBuilder<Emptied, Emptied>;
#[allow(dead_code)]
type HasChroot = ArgsBuilder<Vec<String>, Emptied>;
#[allow(dead_code)]
type HasCommand = ArgsBuilder<Emptied, Vec<String>>;
#[allow(dead_code)]
type HasBoth = ArgsBuilder<Vec<String>, Vec<String>>;
impl<T1, T2> ArgsBuilder<T1, T2>
where
T1: Emptiable,
T2: Emptiable,
{
pub fn new() -> HasNeither {
HasNeither::default()
}
fn _mount(src: &Path, dest: &Path, ro: bool) -> Vec<String> {
vec![
(if ro { "--ro-bind" } else { "--bind" }).to_string(),
format!("{}", src.display()),
format!("{}", dest.display()),
]
}
fn _chroot(self, dir: &Path) -> ArgsBuilder<Vec<String>, T2> {
ArgsBuilder::<Vec<String>, T2> {
chroot: Self::_mount(dir, Path::new("/"), false),
command: self.command,
args: self.args,
}
}
fn _command(self, cmd: &str) -> ArgsBuilder<T1, Vec<String>> {
ArgsBuilder::<T1, Vec<String>> {
chroot: self.chroot,
command: cmd.split_whitespace().map(|x| x.to_string()).collect(),
args: self.args,
}
}
fn _array2vec(array: &[&str]) -> Vec<String> {
array.iter().map(|x| x.to_string()).collect()
}
pub fn usercfg(mut self, args: &[&str]) -> Self {
self.args.user = args.iter().map(|x| x.to_string()).collect();
self
}
pub fn namespace(mut self, args: &[&str]) -> Self {
self.args.namespace = args.iter().map(|x| x.to_string()).collect();
self
}
pub fn sysmounts(mut self, args: &[&str]) -> Self {
self.args.sysmounts = args.iter().map(|x| x.to_string()).collect();
self
}
pub fn resolve(mut self) -> Self {
let resolv = Path::new("/etc/resolv.conf");
self.args.resolv = Self::_mount(resolv, resolv, true);
self
}
pub fn capabilities(mut self, caps: &[&str]) -> Self {
for cap in caps {
self.args.add_cap.push("--cap-add".to_string());
self.args.add_cap.push(cap.to_string());
}
self
}
pub fn add_var(mut self, var: &str, val: &str) -> Self {
let mut vec = Self::_array2vec(&["--setenv", var, val]);
self.args.variables.append(&mut vec);
self
}
pub fn tmpfs(mut self, size: usize) -> Self {
if size != 0 {
const GB: usize = 2usize.pow(30);
let size = format!("{}", size * GB);
let args = [
"--perms",
"0775",
"--size",
&size,
"--tmpfs",
"/var/tmp/portage",
];
self.args.tmpfs = Self::_array2vec(&args);
}
self
}
pub fn add_mount(mut self, src: &Path, dest: &Path) -> Self {
let mut mount = Self::_mount(src, dest, true);
self.args.usermounts.append(&mut mount);
self
}
pub fn chdir(mut self, dir: &Path) -> Self {
self.args.directory = vec!["--chdir".to_string(), dir.display().to_string()];
self
}
}
impl HasBoth {
pub fn build(mut self) -> Vec<String> {
let mut x = Vec::new();
x.append(&mut self.args.namespace);
x.append(&mut self.args.add_cap);
x.append(&mut self.args.user);
x.append(&mut self.args.variables);
x.append(&mut self.chroot);
x.append(&mut self.args.sysmounts);
x.append(&mut self.args.tmpfs);
x.append(&mut self.args.resolv);
x.append(&mut self.args.usermounts);
x.append(&mut self.args.directory);
x.append(&mut self.command);
x
}
}
impl HasNeither {
#[allow(dead_code)]
pub fn chroot(self, dir: &Path) -> HasChroot {
self._chroot(dir)
}
#[allow(dead_code)]
pub fn command(self, cmd: &str) -> HasCommand {
self._command(cmd)
}
}
impl HasChroot {
#[allow(dead_code)]
pub fn command(self, cmd: &str) -> HasBoth {
self._command(cmd)
}
}
impl HasCommand {
#[allow(dead_code)]
pub fn chroot(self, dir: &Path) -> HasBoth {
self._chroot(dir)
}
}