#![doc = include_str!("../README.md")]
use std::error::Error as StdError;
pub use tanzim_source::{OptionValue, Options, Source};
pub mod closure;
#[cfg(feature = "env")]
pub mod env;
#[cfg(feature = "file")]
pub mod file;
#[cfg(feature = "http")]
pub mod http;
#[derive(Debug, Clone, PartialEq)]
pub struct Payload {
pub source: Source,
pub name: Option<String>,
pub format: Option<String>,
pub content: Vec<u8>,
}
impl Payload {
pub fn normalize(mut self) -> Self {
if let Some(name) = self.name {
if name.is_empty() {
self.name = None;
} else {
self.name = Some(name.to_lowercase());
}
}
if let Some(format) = self.format {
if format.is_empty() {
self.format = None;
} else {
self.format = Some(format.to_lowercase());
}
}
self
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{loader} configuration loader could not find {item} at `{resource}`")]
NotFound {
loader: String,
resource: String,
item: String,
},
#[error("{loader} configuration loader has no access to `{resource}`")]
NoAccess {
loader: String,
resource: String,
source: Box<dyn StdError + Send + Sync>,
},
#[error(
"{loader} configuration loader reached timeout `{timeout_in_seconds}s` for `{resource}`"
)]
Timeout {
loader: String,
resource: String,
timeout_in_seconds: u64,
source: Box<dyn StdError + Send + Sync>,
},
#[error("{loader} configuration loader invalid option `{key}`: {reason}")]
InvalidOption {
loader: String,
key: String,
reason: String,
},
#[error("{loader} configuration loader invalid resource `{resource}`: {reason}")]
InvalidResource {
loader: String,
resource: String,
reason: String,
},
#[error(
"{loader} configuration loader found duplicate configurations `{resource}/{name}.({format_1}|{format_2})`"
)]
Duplicate {
loader: String,
resource: String,
name: String,
format_1: String,
format_2: String,
},
#[error("{loader} configuration loader could not {description} `{resource}`")]
Load {
loader: String,
resource: String,
description: String,
source: Box<dyn StdError + Send + Sync>,
},
#[error(transparent)]
Other(#[from] Box<dyn StdError + Send + Sync>),
}
pub trait Load {
fn name(&self) -> &str;
fn supported_source_list(&self) -> Vec<String>;
fn load(&self, source: Source) -> Result<Vec<Payload>, Error>;
}
#[cfg(test)]
mod tests {
use super::*;
use tanzim_source::SourceBuilder;
fn test_source() -> Source {
SourceBuilder::new().with_source("test").build().unwrap()
}
#[test]
fn payload_normalize_clears_empty_name() {
let payload = Payload {
source: test_source(),
name: Some(String::new()),
format: Some("json".into()),
content: b"{}".to_vec(),
}
.normalize();
assert!(payload.name.is_none());
}
#[test]
fn payload_normalize_lowercases_name() {
let payload = Payload {
source: test_source(),
name: Some("FOO".into()),
format: None,
content: Vec::new(),
}
.normalize();
assert_eq!(payload.name, Some("foo".into()));
}
}