os-xtask-utils 0.0.0

Program executation for xtask in os or bootloader project
Documentation
#![deny(warnings, unsafe_code)]

mod binutils;
mod cargo;
pub mod dir;
mod git;
mod make;
mod qemu;
mod tar;

pub use binutils::BinUtil;
pub use cargo::Cargo;
pub use git::Git;
pub use make::Make;
pub use qemu::Qemu;
pub use tar::Tar;

use std::{
    ffi::{OsStr, OsString},
    path::Path,
    process::{Command, ExitStatus, Output},
};

pub trait CommandExt: AsRef<Command> + AsMut<Command> {
    #[inline]
    fn arg(&mut self, s: impl AsRef<OsStr>) -> &mut Self {
        self.as_mut().arg(s);
        self
    }

    #[inline]
    fn args<I, S>(&mut self, args: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        self.as_mut().args(args);
        self
    }

    #[inline]
    fn optional<T>(&mut self, option: &Option<T>, op: impl FnOnce(&mut Self, &T)) -> &mut Self {
        if let Some(val) = option {
            op(self, val);
        }
        self
    }

    #[inline]
    fn conditional(&mut self, condition: bool, op: impl FnOnce(&mut Self)) -> &mut Self {
        if condition {
            op(self);
        }
        self
    }

    #[inline]
    fn option(&mut self, option: Option<impl AsRef<OsStr>>) -> &mut Self {
        if let Some(arg) = option {
            self.as_mut().arg(arg);
        }
        self
    }

    #[inline]
    fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
        self.as_mut().current_dir(dir);
        self
    }

    #[inline]
    fn env(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
        self.as_mut().env(key, val);
        self
    }

    #[inline]
    fn status(&mut self) -> ExitStatus {
        self.as_mut().status().unwrap()
    }

    fn info(&self) -> OsString {
        let cmd = self.as_ref();
        let mut msg = OsString::new();
        if let Some(dir) = cmd.get_current_dir() {
            msg.push("cd ");
            msg.push(dir);
            msg.push(" && ");
        }
        msg.push(cmd.get_program());
        for a in cmd.get_args() {
            msg.push(" ");
            msg.push(a);
        }
        for (k, v) in cmd.get_envs() {
            msg.push(" ");
            msg.push(k);
            if let Some(v) = v {
                msg.push("=");
                msg.push(v);
            }
        }
        msg
    }

    #[inline]
    fn invoke(&mut self) {
        let status = self.status();
        if !status.success() {
            panic!(
                "Failed with code {}: {:?}",
                status.code().unwrap(),
                self.info()
            );
        }
    }

    #[inline]
    fn output(&mut self) -> Output {
        let output = self.as_mut().output().unwrap();
        if !output.status.success() {
            panic!(
                "Failed with code {}: {:?}",
                output.status.code().unwrap(),
                self.info()
            );
        }
        output
    }
}

ext!(def; Ext);

impl Ext {
    #[inline]
    pub fn new(program: impl AsRef<OsStr>) -> Self {
        Self(Command::new(program))
    }
}

mod m {
    #[macro_export]
    macro_rules! ext {
        (def; $name:ident) => {
            pub struct $name(std::process::Command);

            ext!($name);
        };

        ($ty:ty) => {
            impl AsRef<Command> for $ty {
                fn as_ref(&self) -> &Command {
                    &self.0
                }
            }

            impl AsMut<Command> for $ty {
                fn as_mut(&mut self) -> &mut Command {
                    &mut self.0
                }
            }

            impl $crate::CommandExt for $ty {}
        };
    }
}