mod commands;
use clap::{ColorChoice, Parser};
use env_logger::fmt::Color;
use human_panic::setup_panic;
use pijul_interaction::InteractiveContext;
use std::ffi::OsString;
use std::io::Write;
use crate::commands::*;
#[derive(Parser, Debug)]
#[clap(version, author, color(ColorChoice::Auto), infer_subcommands = true)]
pub struct Opts {
#[clap(subcommand)]
pub subcmd: SubCommand,
#[clap(long, global = true)]
pub no_prompt: bool,
}
#[derive(Parser, Debug)]
pub enum SubCommand {
Init(Init),
Clone(Clone),
Record(Record),
Diff(Diff),
Log(Log),
Push(Push),
Pull(Pull),
Change(Change),
Dependents(Dependents),
Channel(Channel),
#[clap(hide = true)]
Protocol(Protocol),
#[cfg(feature = "git")]
Git(Git),
#[clap(alias = "mv")]
Move(Move),
#[clap(alias = "ls")]
List(List),
Add(Add),
#[clap(alias = "rm")]
Remove(Remove),
Reset(Reset),
Debug(Debug),
Fork(Fork),
Unrecord(Unrecord),
Apply(Apply),
Remote(Remote),
Archive(Archive),
Credit(Credit),
Tag(Tag),
#[clap(alias = "id", alias = "key")]
Identity(IdentityCommand),
Client(Client),
Completion(Completion),
#[clap(external_subcommand)]
ExternalSubcommand(Vec<OsString>),
}
#[test]
fn clap_debug_assert() {
use clap::CommandFactory;
Opts::command().debug_assert();
}
#[tokio::main]
async fn main() {
setup_panic!();
if cfg!(debug_assertions) {
env_logger::init();
} else {
env_logger_init();
}
let opts = Opts::parse();
if opts.no_prompt {
pijul_interaction::set_context(InteractiveContext::NotInteractive);
} else {
pijul_interaction::set_context(InteractiveContext::Terminal);
}
if let Err(e) = run(opts).await {
log::error!("Error: {:#?}", e);
match e.downcast::<std::io::Error>() {
Ok(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {}
Ok(e) => writeln!(std::io::stderr(), "Error: {}", e).unwrap_or(()),
Err(e) => writeln!(std::io::stderr(), "Error: {}", e).unwrap_or(()),
}
std::process::exit(1);
} else {
std::process::exit(0);
}
}
fn env_logger_init() {
let mut builder = env_logger::builder();
builder.filter(Some("pijul::commands::git"), log::LevelFilter::Info);
builder.format(|buf, record| {
let target = record.metadata().target();
if target == "pijul::commands::git" {
let mut level_style = buf.style();
level_style.set_color(Color::Green);
writeln!(
buf,
"{} {}",
level_style.value(record.level()),
record.args()
)
} else {
let mut level_style = buf.style();
level_style.set_color(Color::Black).set_intense(true);
let op = level_style.value("[");
let cl = level_style.value("]");
writeln!(
buf,
"{}{} {} {}{} {}",
op,
buf.timestamp(),
buf.default_styled_level(record.level()),
target,
cl,
record.args()
)
}
});
builder.init();
}
#[cfg(unix)]
fn run_external_command(mut command: Vec<OsString>) -> Result<(), std::io::Error> {
let args = command.split_off(1);
let mut cmd: OsString = "pijul-".into();
cmd.push(&command[0]);
use std::os::unix::process::CommandExt;
let err = std::process::Command::new(&cmd).args(args).exec();
report_external_command_error(&command[0], err);
}
#[cfg(windows)]
fn run_external_command(mut command: Vec<OsString>) -> Result<(), std::io::Error> {
let args = command.split_off(1);
let mut cmd: OsString = "pijul-".into();
cmd.push(&command[0]);
let mut spawned = match std::process::Command::new(&cmd).args(args).spawn() {
Ok(spawned) => spawned,
Err(e) => {
report_external_command_error(&command[0], e);
}
};
let status = spawned.wait()?;
std::process::exit(status.code().unwrap_or(1))
}
fn report_external_command_error(cmd: &OsString, err: std::io::Error) -> ! {
match err.kind() {
std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => {
writeln!(std::io::stderr(), "No such subcommand: {:?}", cmd).unwrap_or(())
}
_ => writeln!(std::io::stderr(), "Error while running {:?}: {}", cmd, err).unwrap_or(()),
}
std::process::exit(1)
}
async fn run(opts: Opts) -> Result<(), anyhow::Error> {
match opts.subcmd {
SubCommand::Log(l) => l.run(),
SubCommand::Init(init) => init.run(),
SubCommand::Clone(clone) => clone.run().await,
SubCommand::Record(record) => record.run().await,
SubCommand::Diff(diff) => diff.run(),
SubCommand::Push(push) => push.run().await,
SubCommand::Pull(pull) => pull.run().await,
SubCommand::Change(change) => change.run(),
SubCommand::Dependents(deps) => deps.run(),
SubCommand::Channel(channel) => channel.run(),
SubCommand::Protocol(protocol) => protocol.run(),
#[cfg(feature = "git")]
SubCommand::Git(git) => git.run(),
SubCommand::Move(move_cmd) => move_cmd.run(),
SubCommand::List(list) => list.run(),
SubCommand::Add(add) => add.run(),
SubCommand::Remove(remove) => remove.run(),
SubCommand::Reset(reset) => reset.run(),
SubCommand::Debug(debug) => debug.run(),
SubCommand::Fork(fork) => fork.run(),
SubCommand::Unrecord(unrecord) => unrecord.run(),
SubCommand::Apply(apply) => apply.run(),
SubCommand::Remote(remote) => remote.run(),
SubCommand::Archive(archive) => archive.run().await,
SubCommand::Credit(credit) => credit.run(),
SubCommand::Tag(tag) => tag.run().await,
SubCommand::Identity(identity_wizard) => identity_wizard.run().await,
SubCommand::Client(client) => client.run().await,
SubCommand::ExternalSubcommand(command) => Ok(run_external_command(command)?),
SubCommand::Completion(completion) => completion.run(),
}
}