use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::{self, Display, Formatter};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use crate::ConfigError;
use crate::error::ValidationError;
use crate::loader::SourceKind;
use crate::report::{canonicalize_path_with_aliases, normalize_path, path_matches_pattern};
mod config;
mod field;
mod paths;
mod prefix;
mod validation;
use self::paths::*;
pub use self::prefix::prefixed_metadata;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ConfigMetadata {
fields: Vec<FieldMetadata>,
checks: Vec<ValidationCheck>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct MetadataMatchScore {
segment_count: usize,
specificity: usize,
positional_specificity: Vec<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FieldMetadata {
pub path: String,
pub aliases: Vec<String>,
pub secret: bool,
pub env: Option<String>,
pub env_decode: Option<EnvDecoder>,
pub doc: Option<String>,
pub example: Option<String>,
pub deprecated: Option<String>,
pub has_default: bool,
pub merge: MergeStrategy,
pub merge_explicit: bool,
pub allowed_sources: Option<BTreeSet<SourceKind>>,
pub denied_sources: Option<BTreeSet<SourceKind>>,
pub validations: Vec<ValidationRule>,
pub validation_configs: BTreeMap<String, ValidationRuleConfig>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct EffectiveSourcePolicy {
pub(crate) allowed_sources: Option<BTreeSet<SourceKind>>,
pub(crate) denied_sources: Option<BTreeSet<SourceKind>>,
}
pub(crate) struct EffectiveValidation {
pub(crate) field: FieldMetadata,
pub(crate) rule: ValidationRule,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum EnvDecoder {
Csv,
PathList,
KeyValueMap,
Whitespace,
}
impl Display for EnvDecoder {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Csv => write!(f, "csv"),
Self::PathList => write!(f, "path_list"),
Self::KeyValueMap => write!(f, "key_value_map"),
Self::Whitespace => write!(f, "whitespace"),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MergeStrategy {
#[default]
Merge,
Replace,
Append,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum ValidationLevel {
#[default]
Error,
Warning,
}
impl Display for ValidationLevel {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Error => write!(f, "error"),
Self::Warning => write!(f, "warning"),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ValidationRuleConfig {
pub level: ValidationLevel,
pub message: Option<String>,
pub tags: Vec<String>,
}
impl Display for MergeStrategy {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Merge => write!(f, "merge"),
Self::Replace => write!(f, "replace"),
Self::Append => write!(f, "append"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum ValidationNumber {
Finite(serde_json::Number),
Invalid(String),
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct ValidationValue(pub serde_json::Value);
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ValidationRule {
NonEmpty,
Min(ValidationNumber),
Max(ValidationNumber),
MinLength(usize),
MaxLength(usize),
MinItems(usize),
MaxItems(usize),
MinProperties(usize),
MaxProperties(usize),
MultipleOf(ValidationNumber),
Pattern(String),
UniqueItems,
OneOf(Vec<ValidationValue>),
Hostname,
Url,
Email,
IpAddr,
SocketAddr,
AbsolutePath,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ValidationCheck {
AtLeastOneOf { paths: Vec<String> },
ExactlyOneOf { paths: Vec<String> },
MutuallyExclusive { paths: Vec<String> },
RequiredWith { path: String, requires: Vec<String> },
RequiredIf {
path: String,
equals: ValidationValue,
requires: Vec<String>,
},
}
pub trait TierMetadata {
#[must_use]
fn metadata() -> ConfigMetadata {
ConfigMetadata::default()
}
#[must_use]
fn secret_paths() -> Vec<String> {
Self::metadata().secret_paths()
}
}
impl<T> TierMetadata for super::Secret<T> {
fn metadata() -> ConfigMetadata {
ConfigMetadata::from_fields([FieldMetadata::new("").secret()])
}
}
impl TierMetadata for String {}
impl TierMetadata for bool {}
impl TierMetadata for char {}
impl TierMetadata for u8 {}
impl TierMetadata for u16 {}
impl TierMetadata for u32 {}
impl TierMetadata for u64 {}
impl TierMetadata for u128 {}
impl TierMetadata for usize {}
impl TierMetadata for i8 {}
impl TierMetadata for i16 {}
impl TierMetadata for i32 {}
impl TierMetadata for i64 {}
impl TierMetadata for i128 {}
impl TierMetadata for isize {}
impl TierMetadata for f32 {}
impl TierMetadata for f64 {}
impl TierMetadata for Duration {}
impl TierMetadata for SystemTime {}
impl TierMetadata for PathBuf {}
impl TierMetadata for IpAddr {}
impl TierMetadata for Ipv4Addr {}
impl TierMetadata for Ipv6Addr {}
impl TierMetadata for SocketAddr {}
impl TierMetadata for SocketAddrV4 {}
impl TierMetadata for SocketAddrV6 {}
impl<T> TierMetadata for Option<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
T::metadata()
}
}
impl<T> TierMetadata for Vec<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), T::metadata())
}
}
impl<T, const N: usize> TierMetadata for [T; N]
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), T::metadata())
}
}
impl<T> TierMetadata for BTreeSet<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), T::metadata())
}
}
impl<T> TierMetadata for HashSet<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), T::metadata())
}
}
impl<K, V> TierMetadata for BTreeMap<K, V>
where
V: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), V::metadata())
}
}
impl<K, V, S> TierMetadata for HashMap<K, V, S>
where
V: TierMetadata,
{
fn metadata() -> ConfigMetadata {
prefixed_metadata("*", Vec::new(), V::metadata())
}
}
impl<T> TierMetadata for Box<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
T::metadata()
}
}
impl<T> TierMetadata for Arc<T>
where
T: TierMetadata,
{
fn metadata() -> ConfigMetadata {
T::metadata()
}
}
impl IntoIterator for ConfigMetadata {
type Item = FieldMetadata;
type IntoIter = std::vec::IntoIter<FieldMetadata>;
fn into_iter(self) -> Self::IntoIter {
self.fields.into_iter()
}
}