use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
pub type VariableName = Cow<'static, str>;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EnvironmentError {
NotUnicode,
Read {
message: String,
},
}
impl EnvironmentError {
#[must_use]
pub fn not_unicode() -> Self {
Self::NotUnicode
}
#[must_use]
pub fn read(message: impl Into<String>) -> Self {
Self::Read {
message: message.into(),
}
}
}
impl Display for EnvironmentError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::NotUnicode => formatter.write_str("value is not valid Unicode"),
Self::Read { .. } => formatter.write_str("adapter read failed"),
}
}
}
impl Error for EnvironmentError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationError {
message: String,
}
impl ValidationError {
#[must_use]
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
}
impl Display for ValidationError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}
impl Error for ValidationError {}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BindError {
MissingVariable {
name: VariableName,
},
EmptyVariable {
name: VariableName,
},
Environment {
name: VariableName,
source: EnvironmentError,
},
InvalidBoolean {
name: VariableName,
},
ParseVariable {
name: VariableName,
expected: &'static str,
},
Validation {
name: VariableName,
message: String,
},
ValueTooLarge {
name: VariableName,
max_bytes: usize,
},
}
impl BindError {
#[must_use]
pub fn missing(name: impl Into<VariableName>) -> Self {
Self::MissingVariable { name: name.into() }
}
#[must_use]
pub fn empty(name: impl Into<VariableName>) -> Self {
Self::EmptyVariable { name: name.into() }
}
#[must_use]
pub fn environment(name: impl Into<VariableName>, source: EnvironmentError) -> Self {
Self::Environment {
name: name.into(),
source,
}
}
#[must_use]
pub fn invalid_boolean(name: impl Into<VariableName>) -> Self {
Self::InvalidBoolean { name: name.into() }
}
#[must_use]
pub fn parse(name: impl Into<VariableName>, expected: &'static str) -> Self {
Self::ParseVariable {
name: name.into(),
expected,
}
}
#[must_use]
pub fn validation(name: impl Into<VariableName>, error: ValidationError) -> Self {
Self::Validation {
name: name.into(),
message: error.message,
}
}
#[must_use]
pub fn validation_with_sensitivity(
name: impl Into<VariableName>,
error: ValidationError,
sensitive: bool,
) -> Self {
if sensitive {
Self::Validation {
name: name.into(),
message: "validation failed for sensitive value".to_owned(),
}
} else {
Self::validation(name, error)
}
}
#[must_use]
pub fn value_too_large(name: impl Into<VariableName>, max_bytes: usize) -> Self {
Self::ValueTooLarge {
name: name.into(),
max_bytes,
}
}
#[must_use]
pub fn variable_name(&self) -> &str {
match self {
Self::MissingVariable { name }
| Self::EmptyVariable { name }
| Self::Environment { name, .. }
| Self::InvalidBoolean { name }
| Self::ParseVariable { name, .. }
| Self::Validation { name, .. }
| Self::ValueTooLarge { name, .. } => name,
}
}
#[must_use]
pub fn error_code(&self) -> &'static str {
match self {
Self::MissingVariable { .. } => "missing_variable",
Self::EmptyVariable { .. } => "empty_variable",
Self::Environment { .. } => "environment_error",
Self::InvalidBoolean { .. } => "invalid_boolean",
Self::ParseVariable { .. } => "parse_variable",
Self::Validation { .. } => "validation_failed",
Self::ValueTooLarge { .. } => "value_too_large",
}
}
}
impl Display for BindError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::MissingVariable { name } => {
write!(formatter, "missing environment variable {name}")
}
Self::EmptyVariable { name } => {
write!(formatter, "environment variable {name} must not be empty")
}
Self::Environment { name, source } => {
write!(
formatter,
"environment variable {name} read failed: {source}"
)
}
Self::InvalidBoolean { name } => {
write!(
formatter,
"environment variable {name} must be boolean-like"
)
}
Self::ParseVariable { name, expected } => {
write!(
formatter,
"environment variable {name} must parse as {expected}"
)
}
Self::Validation { name, message } => {
write!(
formatter,
"environment variable {name} failed validation: {message}"
)
}
Self::ValueTooLarge { name, max_bytes } => {
write!(
formatter,
"environment variable {name} exceeds maximum allowed size of {max_bytes} bytes"
)
}
}
}
}
impl Error for BindError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Environment { source, .. } => Some(source),
_ => None,
}
}
}