#![cfg_attr(docsrs, feature(doc_cfg))]
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("SerializationError: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("YamlError: {0}")]
YamlError(String),
#[cfg(feature = "hbs")]
#[cfg_attr(docsrs, doc(cfg(feature = "hbs")))]
#[error("Registering template failed with error: {0}")]
HbsTemplateError(#[from] handlebars::TemplateError),
#[cfg(feature = "hbs")]
#[cfg_attr(docsrs, doc(cfg(feature = "hbs")))]
#[error("Renderer error: {0}")]
HbsRenderError(#[from] handlebars::RenderError),
#[cfg(feature = "rhai")]
#[cfg_attr(docsrs, doc(cfg(feature = "rhai")))]
#[error("Rhai script error: {0}")]
RhaiError(#[from] Box<rhai::EvalAltResult>),
#[cfg(feature = "http")]
#[cfg_attr(docsrs, doc(cfg(feature = "http")))]
#[error("Reqwest error: {0}")]
ReqwestError(#[from] reqwest::Error),
#[error("Json decoding error: {0}")]
JsonError(#[source] serde_json::Error),
#[error("{0} query failed: {1}")]
MethodFailed(String, u16, String),
#[error("Unsupported method")]
UnsupportedMethod,
#[error("Missing script {0}")]
MissingScript(std::path::PathBuf),
#[error("UTF8 error {0}")]
UTF8(#[from] std::string::FromUtf8Error),
#[error("Semver error {0}")]
Semver(#[from] ::semver::Error),
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
#[error("Argon2 password_hash error {0}")]
Argon2hash(#[from] argon2::password_hash::Error),
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
#[error("Bcrypt hash error {0}")]
BcryptError(#[from] bcrypt::BcryptError),
#[error("Stdio error {0}")]
Stdio(#[from] std::io::Error),
#[error("Base64 decode error {0}")]
Base64DecodeError(#[from] base64::DecodeError),
#[error("RAW api error {0}")]
RawHTTP(#[from] ::http::Error),
#[error("ParseIntError {0}")]
ParseInt(#[from] std::num::ParseIntError),
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
#[error("KEY-OPENSSL-001 OpenSSL error {0}")]
OpenSSL(#[from] openssl::error::ErrorStack),
#[error("KEY-ALGO-001 Unsupported key algorithm: {0}")]
UnsupportedKeyAlgorithm(String),
#[error("{0}")]
PasswordSpec(String),
#[error("Error: {0}")]
Other(String),
#[cfg(feature = "oci")]
#[cfg_attr(docsrs, doc(cfg(feature = "oci")))]
#[error("OCI jukebox error {0}")]
OCIDistrib(#[from] oci_client::errors::OciDistributionError),
#[cfg(feature = "oci")]
#[cfg_attr(docsrs, doc(cfg(feature = "oci")))]
#[error("OCI parse error {0}")]
OCIParseError(#[from] oci_client::ParseError),
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
#[error("K8s error: {0}")]
KubeError(#[from] kube::Error),
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
#[error("K8s wait error: {0}")]
KubeWaitError(#[from] kube::runtime::wait::Error),
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
#[error("Elapsed wait error: {0}")]
Elapsed(#[from] tokio::time::error::Elapsed),
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
#[error("Finalizer error: {0}")]
FinalizerError(#[from] Box<kube::runtime::finalizer::Error<Error>>),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[cfg(feature = "rhai")]
#[cfg_attr(docsrs, doc(cfg(feature = "rhai")))]
pub type RhaiRes<T> = std::result::Result<T, Box<rhai::EvalAltResult>>;
pub fn error_chain(err: &(dyn std::error::Error + 'static)) -> String {
let mut acc = err.to_string();
let mut source = err.source();
while let Some(e) = source {
let msg = e.to_string();
if !acc.contains(&msg) {
acc.push_str(": ");
acc.push_str(&msg);
}
source = e.source();
}
acc
}
#[cfg(feature = "rhai")]
pub fn rhai_err(e: Error) -> Box<rhai::EvalAltResult> {
error_chain(&e).into()
}
#[cfg(feature = "rhai")]
pub fn rhai_err_str(e: String) -> Box<rhai::EvalAltResult> {
e.into()
}
pub mod chrono;
pub mod client_name;
pub mod hashes;
pub mod password;
pub mod semver;
pub mod yaml;
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
pub mod key;
#[cfg(feature = "rhai")]
#[cfg_attr(docsrs, doc(cfg(feature = "rhai")))]
pub mod engine;
#[cfg(feature = "rhai")]
#[cfg_attr(docsrs, doc(cfg(feature = "rhai")))]
pub mod glob;
#[cfg(feature = "hbs")]
#[cfg_attr(docsrs, doc(cfg(feature = "hbs")))]
pub mod hbs;
#[cfg(feature = "hbs")] mod hbs_json;
#[cfg(feature = "http")]
#[cfg_attr(docsrs, doc(cfg(feature = "http")))]
pub mod http;
#[cfg(feature = "http")]
#[cfg_attr(docsrs, doc(cfg(feature = "http")))]
pub mod http_mock;
#[cfg(feature = "oci")]
#[cfg_attr(docsrs, doc(cfg(feature = "oci")))]
pub mod oci;
#[cfg(feature = "oci")]
#[cfg_attr(docsrs, doc(cfg(feature = "oci")))]
pub mod oci_mock;
#[cfg(feature = "s3")]
#[cfg_attr(docsrs, doc(cfg(feature = "s3")))]
pub mod s3;
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
pub mod k8s;
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
pub mod k8s_mock;
#[cfg(feature = "shell")]
#[cfg_attr(docsrs, doc(cfg(feature = "shell")))]
pub mod shell;
pub use client_name::{client_name_is_set, get_client_name, set_client_name};
pub use semver::Semver;
#[cfg(feature = "rhai")]
#[cfg_attr(docsrs, doc(cfg(feature = "rhai")))]
pub use engine::Script;
#[cfg(feature = "hbs")]
#[cfg_attr(docsrs, doc(cfg(feature = "hbs")))]
pub use hbs::HandleBars;
#[cfg(feature = "k8s")]
#[cfg_attr(docsrs, doc(cfg(feature = "k8s")))]
pub use k8s::update_cache;
#[cfg(test)]
mod tests {
use super::*;
use std::fmt;
#[derive(Debug)]
struct Layered {
msg: &'static str,
source: Option<Box<Layered>>,
}
impl fmt::Display for Layered {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl std::error::Error for Layered {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_deref()
.map(|e| e as &(dyn std::error::Error + 'static))
}
}
#[test]
fn error_chain_walks_every_source() {
let err = Layered {
msg: "error sending request for url (https://gitlab.com/api/v4/projects)",
source: Some(Box::new(Layered {
msg: "dns error: failed to lookup address information",
source: Some(Box::new(Layered {
msg: "Temporary failure in name resolution",
source: None,
})),
})),
};
assert_eq!(
error_chain(&err),
"error sending request for url (https://gitlab.com/api/v4/projects): \
dns error: failed to lookup address information: \
Temporary failure in name resolution"
);
}
#[test]
fn error_chain_collapses_a_duplicate_leading_source() {
let inner = Layered {
msg: "error sending request for url (https://gitlab.com/api/v4/projects)",
source: None,
};
let outer = Layered {
msg: "Reqwest error: error sending request for url (https://gitlab.com/api/v4/projects)",
source: Some(Box::new(Layered {
msg: "error sending request for url (https://gitlab.com/api/v4/projects)",
source: None,
})),
};
assert_eq!(error_chain(&inner), inner.msg);
assert_eq!(
error_chain(&outer),
outer.msg,
"duplicate source line must be collapsed"
);
}
#[test]
fn error_chain_single_error_has_no_source() {
let err = Layered {
msg: "boom",
source: None,
};
assert_eq!(error_chain(&err), "boom");
}
}