use lazy_static::lazy_static;
use oclif::{term::ERR_YELLOW, CliError};
use thiserror::Error;
use std::{
io,
sync::atomic::{AtomicBool, Ordering},
};
lazy_static! {
static ref DEBUG: AtomicBool = AtomicBool::new(false);
}
pub fn get_debug() -> bool {
DEBUG.load(Ordering::Relaxed)
}
pub fn set_debug() {
DEBUG.store(true, Ordering::Relaxed);
}
macro_rules! _debug {
($desc:literal, $val:expr) => {{
if $crate::utils::get_debug() {
oclif::term::TERM_ERR.write_line(&format!(
"{} {} {}",
oclif::term::ERR_GREEN.apply_to("debug"),
oclif::term::ERR_MAGENTA.apply_to($desc),
$val
))?;
oclif::term::TERM_ERR.flush()?;
}
}};
}
macro_rules! _info {
($desc:literal, $val:expr) => {{
oclif::term::TERM_ERR.write_line(&format!(
"{} {} {}",
oclif::term::ERR_GREEN.apply_to("info"),
oclif::term::ERR_MAGENTA.apply_to($desc),
$val
))?;
oclif::term::TERM_ERR.flush()?;
}};
}
macro_rules! _warn {
($desc:literal, $val:expr) => {{
oclif::term::TERM_ERR.write_line(&format!(
"{} {} {}",
oclif::term::ERR_YELLOW.apply_to("warn"),
oclif::term::ERR_MAGENTA.apply_to($desc),
$val
))?;
oclif::term::TERM_ERR.flush()?;
}};
}
pub(crate) use _debug as debug;
pub(crate) use _info as info;
pub(crate) use _warn as warn;
#[derive(Error, Debug)]
pub enum Error {
#[error("package {id} is not inside workspace {ws}")]
PackageNotInWorkspace { id: String, ws: String },
#[error("unable to find package {id}")]
PackageNotFound { id: String },
#[error("did not find any public packages (use -a to include private packages)")]
NoPublicPackages,
#[error("did not find any package")]
EmptyWorkspace,
#[error("package {0}'s manifest has no parent directory")]
ManifestHasNoParent(String),
#[error("unable to read metadata specified in Cargo.toml: {0}")]
BadMetadata(serde_json::Error),
#[error("command needs to be run from the workspace root")]
MustBeRunFromWorkspaceRoot,
#[error("unable to verify package {0}")]
Verify(String),
#[error("unable to publish package {0}")]
Publish(String),
#[error("unable to update Cargo.lock")]
Update,
#[error("{0} value must contain '%n'")]
MustContainPercentN(String),
#[error("unable to create crate")]
Create,
#[error("path already exists")]
PathAlreadyExists,
#[error("member path is not inside workspace root")]
InvalidMemberPath,
#[error("the workspace already contains a package with this name")]
DuplicatePackageName,
#[error("path for crate is in workspace.exclude list ({0})")]
InWorkspaceExclude(String),
#[error("given path {0} is not a folder")]
WorkspaceRootNotDir(String),
#[error("unable to initialize workspace: {0}")]
Init(String),
#[error("unable to run cargo command with args {args:?}, got {err}")]
Cargo { err: io::Error, args: Vec<String> },
#[error("unable to run git command with args {args:?}, got {err}")]
Git { err: io::Error, args: Vec<String> },
#[error("child command failed to exit successfully")]
Bail,
#[error("not a git repository")]
NotGit,
#[error("no commits in this repository")]
NoCommits,
#[error("not on a git branch")]
NotBranch,
#[error("remote {remote} not found or branch {branch} not in {remote}")]
NoRemote { remote: String, branch: String },
#[error("local branch {branch} is behind upstream {upstream}")]
BehindRemote { upstream: String, branch: String },
#[error("not allowed to run on branch {branch} because it doesn't match pattern {pattern}")]
BranchNotAllowed { branch: String, pattern: String },
#[error("unable to add files to git index, out = {0}, err = {1}")]
NotAdded(String, String),
#[error("unable to commit to git, out = {0}, err = {1}")]
NotCommitted(String, String),
#[error("unable to tag {0}, out = {1}, err = {2}")]
NotTagged(String, String, String),
#[error("unable to push to remote, out = {0}, err = {1}")]
NotPushed(String, String),
#[error("no changes detected")]
NoChanges,
#[error("could not understand 'cargo config get' output: {0}")]
BadConfigGetOutput(String),
#[error("crates index error: {0}")]
CratesRegistry(#[from] tame_index::Error),
#[error("unsupported crates index type")]
UnsupportedCratesIndexType,
#[error("crates index error: {0}")]
CratesReqwest(#[from] tame_index::external::reqwest::Error),
#[error("the workspace manifest has bad format: {0}")]
WorkspaceBadFormat(String),
#[error("{0}")]
Semver(#[from] semver::ReqParseError),
#[error("{0}")]
Glob(#[from] glob::GlobError),
#[error("{0}")]
GlobPattern(#[from] glob::PatternError),
#[error("{0}")]
Globset(#[from] globset::Error),
#[error("{0}")]
Serde(#[from] serde_json::Error),
#[error("{0}")]
Io(#[from] io::Error),
#[error("cannot convert command output to string, {0}")]
FromUtf8(#[from] std::string::FromUtf8Error),
#[error("{0}")]
Toml(#[from] toml_edit::TomlError),
}
impl CliError for Error {
fn color(self) -> Self {
match self {
Self::PackageNotInWorkspace { id, ws } => Self::PackageNotInWorkspace {
id: format!("{}", ERR_YELLOW.apply_to(id)),
ws: format!("{}", ERR_YELLOW.apply_to(ws)),
},
Self::PackageNotFound { id } => Self::PackageNotFound {
id: format!("{}", ERR_YELLOW.apply_to(id)),
},
Self::Verify(pkg) => Self::Verify(format!("{}", ERR_YELLOW.apply_to(pkg))),
Self::Publish(pkg) => Self::Publish(format!("{}", ERR_YELLOW.apply_to(pkg))),
Self::MustContainPercentN(val) => {
Self::MustContainPercentN(format!("{}", ERR_YELLOW.apply_to(val)))
}
Self::WorkspaceRootNotDir(path) => {
Self::WorkspaceRootNotDir(format!("{}", ERR_YELLOW.apply_to(path)))
}
Self::NoRemote { remote, branch } => Self::NoRemote {
remote: format!("{}", ERR_YELLOW.apply_to(remote)),
branch: format!("{}", ERR_YELLOW.apply_to(branch)),
},
Self::BehindRemote { upstream, branch } => Self::BehindRemote {
upstream: format!("{}", ERR_YELLOW.apply_to(upstream)),
branch: format!("{}", ERR_YELLOW.apply_to(branch)),
},
Self::BranchNotAllowed { branch, pattern } => Self::BranchNotAllowed {
branch: format!("{}", ERR_YELLOW.apply_to(branch)),
pattern: format!("{}", ERR_YELLOW.apply_to(pattern)),
},
Self::NotTagged(tag, out, err) => {
Self::NotTagged(format!("{}", ERR_YELLOW.apply_to(tag)), out, err)
}
_ => self,
}
}
}