use std::collections::VecDeque;
use std::error::Error;
use std::fmt::{Display, Error as FmtError};
use std::io::Error as IoError;
use std::process;
use std::sync::Arc;
use clap::Command;
use mlua::{Error as LuaError, FromLua, Function, Lua, Table, UserData, UserDataMethods, Value};
#[derive(Debug, Clone)]
pub struct Builder {
pub description: Option<String>,
pub name: String,
steps: VecDeque<Step>,
}
impl Builder {
#[must_use]
pub fn make_command(&self) -> Command {
self.description.as_ref().map_or_else(
|| Command::new(&self.name),
|description| Command::new(&self.name).about(description),
)
}
#[must_use]
pub const fn new() -> Self {
Self {
description: None,
name: String::new(),
steps: VecDeque::new(),
}
}
#[must_use]
pub fn with_name(name: String) -> Self {
Self {
name,
..Self::new()
}
}
pub fn apply_through_lua(self, func: &Function) -> Result<Self, LuaError> {
func.call::<Self>(self)
}
pub fn add_sh_step(&mut self, sh_str: String) {
self.steps.push_back(Step::Sh(sh_str));
}
pub fn set_description(&mut self, description: String) {
self.description = Some(description);
}
pub fn provide_description(&mut self, description: String) -> bool {
if self.description.is_none() {
self.description = Some(description);
true
} else {
false
}
}
pub fn execute(self) -> Result<(), IoError> {
for step in self.steps {
step.execute()?;
}
Ok(())
}
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct BuilderFromLuaError(pub &'static str);
impl From<Value> for BuilderFromLuaError {
fn from(found: Value) -> Self {
Self(found.type_name())
}
}
impl Error for BuilderFromLuaError {}
impl Display for BuilderFromLuaError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), FmtError> {
write!(f, "Expected a builder.Builder found a {}", self.0)
}
}
impl FromLua for Builder {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self, LuaError> {
match value {
Value::UserData(any_user_data) => any_user_data.take(),
found => Err(LuaError::ExternalError(Arc::new(BuilderFromLuaError(
found.type_name(),
)))),
}
}
}
impl UserData for Builder {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("add_sh_step", |_lua, builder, shell: String| {
builder.add_sh_step(shell);
Ok(())
});
methods.add_method_mut(
"provide_description",
|_lua, builder, description: String| {
builder
.provide_description(description)
.ok_or(LuaError::external(
"Called provide_description on a builder.Builder with a description",
))
},
);
}
}
#[derive(Debug, Clone)]
pub enum Step {
Sh(String),
}
impl Step {
pub fn execute(&self) -> Result<(), IoError> {
match self {
Self::Sh(script) => {
let _exit = process::Command::new("sh")
.arg("-c")
.arg(script)
.spawn()?
.wait()?;
Ok(())
}
}
}
}
pub fn collect_builders_from_table(table: &Table) -> Vec<Builder> {
fn collect_builder((key, func): (String, Function)) -> Option<Builder> {
func.call::<Builder>(Builder::with_name(key)).ok()
}
let mut res = table
.pairs::<String, Function>()
.flatten()
.filter_map(collect_builder)
.collect::<Vec<Builder>>();
res.sort_by(|a, b| a.name.cmp(&b.name));
res
}