use crate::core::CoreError;
use crate::db::DbError;
#[derive(Debug, thiserror::Error)]
pub enum CliError {
#[error("invalid assignment \"{0}\": expected KEY=VALUE format")]
InvalidAssignment(String),
#[error("cannot read file \"{0}\": {1}")]
FileNotFound(String, String),
#[error("already initialised: envy.toml exists in this directory")]
AlreadyInitialised,
#[error("project not found in vault \u{2014} was the vault file moved?")]
ProjectNotInVault,
#[error("could not open vault: {0}")]
VaultOpen(String),
#[error("passphrase input failed: {0}")]
PassphraseInput(String),
#[error("no environments could be decrypted \u{2014} check your passphrase")]
NothingImported,
#[error("environment '{0}' not found in vault or artifact")]
EnvNotFound(String),
#[error("envy.enc is unreadable: {0}")]
ArtifactUnreadable(String),
#[error("{0}")]
Core(#[from] crate::core::CoreError),
#[error("output error: {0}")]
Output(String),
#[error(
"no .git directory found above the current project \u{2014} `envy hooks install` requires a git repository"
)]
GitRepoNotFound,
#[error(
"{0} already exists and was not installed by envy \u{2014} re-run with --force to back it up and replace it"
)]
HookConflict(String),
}
pub fn format_core_error(e: &CoreError) -> String {
format!("error: {e}")
}
pub fn format_cli_error(e: &CliError) -> String {
format!("error: {e}")
}
pub fn core_exit_code(e: &CoreError) -> i32 {
match e {
CoreError::ManifestNotFound => 1,
CoreError::ManifestInvalid(_) => 1,
CoreError::ManifestIo(_) => 1,
CoreError::InvalidSecretKey(_) => 2,
CoreError::Db(DbError::NotFound) => 1,
CoreError::Db(_) => 4,
CoreError::Crypto(_) => 4,
}
}
pub fn cli_exit_code(e: &CliError) -> i32 {
match e {
CliError::InvalidAssignment(_) => 2,
CliError::FileNotFound(_, _) => 1,
CliError::AlreadyInitialised => 3,
CliError::ProjectNotInVault => 4,
CliError::VaultOpen(_) => 4,
CliError::PassphraseInput(_) => 2,
CliError::NothingImported => 1,
CliError::EnvNotFound(_) => 3,
CliError::ArtifactUnreadable(_) => 5,
CliError::Core(e) => core_exit_code(e),
CliError::Output(_) => 1,
CliError::GitRepoNotFound => 1,
CliError::HookConflict(_) => 3,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_manifest_not_found() {
let msg = format_core_error(&crate::core::CoreError::ManifestNotFound);
assert!(
msg.starts_with("error: "),
"message must start with 'error: ', got: {msg:?}"
);
assert!(
msg.contains("envy init"),
"message must mention `envy init` to guide the user, got: {msg:?}"
);
}
#[test]
fn exit_code_not_found() {
assert_eq!(
core_exit_code(&crate::core::CoreError::ManifestNotFound),
1,
"ManifestNotFound must map to exit code 1"
);
assert_eq!(
core_exit_code(&crate::core::CoreError::Db(crate::db::DbError::NotFound)),
1,
"Db(NotFound) must map to exit code 1"
);
}
#[test]
fn exit_code_invalid_key() {
assert_eq!(
core_exit_code(&crate::core::CoreError::InvalidSecretKey(String::new())),
2,
"InvalidSecretKey must map to exit code 2"
);
}
#[test]
fn passphrase_input_maps_to_exit_code_2() {
assert_eq!(
cli_exit_code(&CliError::PassphraseInput("some error".into())),
2,
"PassphraseInput must map to exit code 2"
);
}
#[test]
fn env_not_found_maps_to_exit_code_3() {
assert_eq!(
cli_exit_code(&CliError::EnvNotFound("staging".into())),
3,
"EnvNotFound must map to exit code 3"
);
}
#[test]
fn artifact_unreadable_maps_to_exit_code_5() {
assert_eq!(
cli_exit_code(&CliError::ArtifactUnreadable("malformed JSON".into())),
5,
"ArtifactUnreadable must map to exit code 5"
);
}
#[test]
fn nothing_imported_maps_to_exit_code_1() {
assert_eq!(
cli_exit_code(&CliError::NothingImported),
1,
"NothingImported must map to exit code 1"
);
}
}