use cotton::prelude::*;
use cotton::problem;
use std::os::unix::fs::PermissionsExt;
mod project;
use project::{Project, CargoMode};
const MODE_USER_EXEC: u32 = 0o100;
#[derive(Subcommand)]
enum ScriptAction {
New {
#[arg(short = 'b', long)]
bare: bool,
#[arg(short = 'n', long)]
no_prebuild: bool,
script: PathBuf,
},
Check {
script: PathBuf,
},
Build {
script: PathBuf,
},
Exec {
script: PathBuf,
arguments: Vec<OsString>,
},
Run {
script: PathBuf,
arguments: Vec<OsString>,
},
Test {
script: PathBuf,
},
Clean {
script: PathBuf,
},
CleanAll,
}
#[derive(Parser)]
struct Cli {
#[structopt(flatten)]
logging: ArgsLogger,
#[structopt(subcommand)]
script_action: ScriptAction,
}
fn write_template<'i>(script: &Path, template: String) -> PResult<()> {
write(script, &template).problem_while("writing template to new script file")?;
let file = File::open(script).unwrap();
let meta = file.metadata().unwrap();
let mut perm = meta.permissions();
perm.set_mode(perm.mode() | MODE_USER_EXEC);
drop(file);
set_permissions(script, perm).problem_while("setting executable permission")?;
Ok(())
}
fn stub_user_env() {
use std::env;
if env::var("USER").is_err() {
env::set_var("USER", "root"); }
}
fn main() -> FinalResult {
init_app_info!();
stub_user_env();
if let Some(script) = std::env::args().skip(1).next().filter(|arg1| PathBuf::from(arg1).is_file()) {
problem::format_panic_to_stderr();
let project = Project::new(PathBuf::from(script))?;
if !project.has_binary() {
project.cargo()?.ensure_built(CargoMode::Silent)?;
}
project.execute(&std::env::args().skip(2).collect::<Vec<_>>()).unwrap();
unreachable!()
}
let Cli {
logging,
script_action,
} = Cli::parse();
setup_logger(logging, vec![module_path!()]);
match script_action {
ScriptAction::New { bare, no_prebuild, script } => {
let project_name = script.file_stem().ok_or_problem("Path has no file name")?.to_str().ok_or_problem("Script stem is not UTF-8 compatible")?;
info!("Generating new script {:?} in {}", project_name, script.display());
if bare {
write_template(&script, format!(include_str!("../templates/bare.rs"), name = project_name))?;
} else {
write_template(&script, format!(include_str!("../templates/cotton.rs"), name = project_name))?;
}
if !no_prebuild {
let project = Project::new(script)?;
let cargo = project.cargo()?;
cargo.ensure_built(CargoMode::Verbose)?;
}
}
ScriptAction::Exec { script, arguments } => {
let project = Project::new(script)?;
if project.has_binary() {
project.execute(&arguments)?;
unreachable!()
}
let cargo = project.cargo()?;
cargo.ensure_built(CargoMode::Verbose)?;
project.execute(&arguments)?;
}
ScriptAction::Run { script, arguments } => {
let project = Project::new(script)?;
let cargo = project.cargo()?;
cargo.ensure_built(CargoMode::Verbose)?;
project.execute(&arguments)?;
}
ScriptAction::Build { script } => {
let project = Project::new(script)?;
let cargo = project.cargo()?;
cargo.ensure_built(CargoMode::Verbose)?;
}
ScriptAction::Check { script } => {
let project = Project::new(script)?;
let cargo = project.cargo()?;
cargo.ensure_updated()?;
cargo.check()?;
}
ScriptAction::Test { script } => {
let project = Project::new(script)?;
let cargo = project.cargo()?;
cargo.ensure_updated()?;
cargo.test()?;
}
ScriptAction::Clean { script } => {
let project = Project::new(script)?;
project.clean()?;
}
ScriptAction::CleanAll => {
Project::clean_all()?;
}
}
Ok(())
}