#[macro_use]
extern crate clap;
#[macro_use]
extern crate thiserror;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
use clap::Clap;
use human_panic::setup_panic;
mod commands;
mod config;
mod remote;
mod repository;
use commands::*;
const DEFAULT_CHANNEL: &'static str = "main";
const PROTOCOL_VERSION: usize = 3;
#[derive(Clap, Debug)]
#[clap(version = crate_version!(), author = crate_authors!())]
pub struct Opts {
#[clap(subcommand)]
pub subcmd: SubCommand,
}
#[derive(Clap, Debug)]
pub enum SubCommand {
#[clap(name = "init")]
Init(Init),
#[clap(name = "clone")]
Clone(Clone),
#[clap(name = "record", alias = "rec")]
Record(Record),
#[clap(name = "diff")]
Diff(Diff),
#[clap(name = "log")]
Log(Log),
#[clap(name = "push")]
Push(Push),
#[clap(name = "pull")]
Pull(Pull),
#[clap(name = "change")]
Change(Change),
#[clap(name = "channel")]
Channel(Channel),
#[clap(name = "protocol", setting = clap::AppSettings::Hidden)]
Protocol(Protocol),
#[cfg(feature = "git")]
#[clap(name = "git")]
Git(Git),
#[clap(name = "mv")]
Mv(Mv),
#[clap(name = "ls")]
Ls(Ls),
#[clap(name = "add")]
Add(Add),
#[clap(name = "reset")]
Reset(Reset),
#[cfg(debug_assertions)]
#[clap(name = "debug")]
Debug(Debug),
#[clap(name = "fork")]
Fork(Fork),
#[clap(name = "unrecord", alias = "unrec", alias = "un")]
Unrecord(Unrecord),
#[clap(name = "apply")]
Apply(Apply),
#[clap(name = "remote")]
Remote(Remote),
#[clap(name = "archive")]
Archive(Archive),
}
#[derive(Debug, Error)]
pub enum Error {
#[error("No Pijul repository found")]
NoRepoRoot,
#[error("Cannot access working directory")]
CannotAccessWorkingDirectory,
#[error("Already in a repository")]
AlreadyInARepo,
#[error("No such channel: {}", channel)]
NoSuchChannel { channel: String },
#[error("Protocol error: {:?}", line)]
ProtocolError { line: Vec<u8> },
#[error("Not authenticated")]
NotAuthenticated,
#[error("No change message")]
NoChangeMessage,
#[error("Incorrect remote: {}", name)]
IncorrectRemote { name: String },
#[error("Unknown host key")]
UnknownHostKey,
#[error("Cannot record a binary change interactively. Use -a")]
RecordBinaryChange,
#[error("Unknown remote type")]
UnknownRemoteType,
#[error("No global config directory")]
NoGlobalConfigDir,
#[error("Could not parse global config")]
CouldNotParseGlobal,
#[error("Cannot dry-reset multiple files")]
CannotDryReset,
#[error("Remote error: {}", msg)]
Remote { msg: String },
#[error("Remote exited with status {}", status)]
RemoteExit { status: u32 },
#[error("Missing remote")]
MissingRemote,
#[error("State not found in remote: {:?}", state)]
StateNotFound { state: libanu::pristine::Merkle },
#[error("Missing dependencies for change {:?}", h)]
MissingDep { h: libanu::pristine::Hash },
#[error("Ambiguous path: {:?}", path)]
AmbiguousPath { path: String },
#[error("No prefixes given. Use `.` to record the current directory.")]
NoRecordPrefixes,
#[error("HTTP error: {}", status.as_str())]
Http { status: reqwest::StatusCode },
}
#[tokio::main]
async fn main() {
if !cfg!(debug_assertions) {
setup_panic!();
}
env_logger::init();
let opts: Opts = Opts::parse();
if let Err(e) = run(opts).await {
eprintln!("Error: {}", e);
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::Channel(channel) => channel.run(),
SubCommand::Protocol(protocol) => protocol.run(),
#[cfg(feature = "git")]
SubCommand::Git(git) => git.run(),
SubCommand::Mv(mv) => mv.run(),
SubCommand::Ls(ls) => ls.run(),
SubCommand::Add(add) => add.run(),
SubCommand::Reset(reset) => reset.run(),
#[cfg(debug_assertions)]
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,
}
}
pub fn current_dir() -> Result<std::path::PathBuf, Error> {
std::env::current_dir().map_err(|_| Error::CannotAccessWorkingDirectory)
}