use std::fmt;
use std::panic::Location;
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ConfigSource {
Defaults,
Env(String),
File(PathBuf),
}
impl ConfigSource {
#[must_use]
pub fn as_path(&self) -> Option<&Path> {
match self {
Self::File(p) => Some(p.as_path()),
_ => None,
}
}
#[must_use]
pub fn as_env_prefix(&self) -> Option<&str> {
match self {
Self::Env(prefix) => Some(prefix.as_str()),
_ => None,
}
}
#[must_use]
pub fn is_file(&self) -> bool {
matches!(self, Self::File(_))
}
#[must_use]
pub fn is_env(&self) -> bool {
matches!(self, Self::Env(_))
}
#[must_use]
pub fn is_defaults(&self) -> bool {
matches!(self, Self::Defaults)
}
#[must_use]
pub fn kind(&self) -> ConfigSourceKind {
match self {
Self::Defaults => ConfigSourceKind::Defaults,
Self::Env(_) => ConfigSourceKind::Env,
Self::File(_) => ConfigSourceKind::File,
}
}
#[must_use]
pub fn file_format(&self) -> Option<crate::discovery::Format> {
match self {
Self::File(path) => crate::discovery::Format::from_path(path),
_ => None,
}
}
#[must_use]
pub fn env_prefix_kind(&self) -> Option<EnvMetadataTagKind> {
match self {
Self::Env(prefix) if prefix.is_empty() => Some(EnvMetadataTagKind::Bare),
Self::Env(_) => Some(EnvMetadataTagKind::Prefixed),
_ => None,
}
}
#[must_use]
pub fn env_metadata_name(prefix: &str) -> String {
if prefix.is_empty() {
"environment variable(s)".to_owned()
} else {
format!("`{}` environment variable(s)", prefix.to_ascii_uppercase())
}
}
#[must_use]
pub fn strip_env_metadata_name(name: &str) -> Option<EnvMetadataTag<'_>> {
if !name.contains("environment variable") {
return None;
}
if let Some(rest) = name.strip_prefix('`')
&& let Some(end) = rest.find('`')
{
return Some(EnvMetadataTag::Prefixed(&rest[..end]));
}
Some(EnvMetadataTag::Bare)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[non_exhaustive]
pub enum ConfigSourceKind {
Defaults,
Env,
File,
}
impl ConfigSourceKind {
pub const ALL: &'static [Self] = &[Self::Defaults, Self::Env, Self::File];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Defaults => "defaults",
Self::Env => "env",
Self::File => "file",
}
}
}
impl crate::ClosedAxis for ConfigSourceKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for ConfigSourceKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
closed_axis_label_string_surface! {
type = ConfigSourceKind,
parse_error = "unknown config source kind",
expecting = "a canonical ConfigSourceKind lowercase label \
(`defaults`, `env`, `file`; case-insensitive)",
}
pub trait ConfigSourceChain {
fn find_file(&self, path: &Path) -> Option<&ConfigSource>;
fn unique_of_kind(&self, kind: ConfigSourceKind) -> Option<&ConfigSource>;
fn find_env_by_prefix(&self, prefix: &str) -> Option<&ConfigSource>;
fn layer_kind_histogram(&self) -> crate::AxisHistogram<ConfigSourceKind>
where
Self: AsRef<[ConfigSource]>,
{
crate::axis_histogram(self.as_ref().iter().map(ConfigSource::kind))
}
fn file_format_histogram(&self) -> crate::AxisHistogram<crate::discovery::Format>
where
Self: AsRef<[ConfigSource]>,
{
crate::axis_histogram(self.as_ref().iter().filter_map(ConfigSource::file_format))
}
fn env_prefix_kind_histogram(&self) -> crate::AxisHistogram<EnvMetadataTagKind>
where
Self: AsRef<[ConfigSource]>,
{
crate::axis_histogram(
self.as_ref()
.iter()
.filter_map(ConfigSource::env_prefix_kind),
)
}
}
impl ConfigSourceChain for [ConfigSource] {
fn find_file(&self, path: &Path) -> Option<&ConfigSource> {
self.iter().find(|s| s.as_path() == Some(path))
}
fn unique_of_kind(&self, kind: ConfigSourceKind) -> Option<&ConfigSource> {
let mut matches = self.iter().filter(|s| s.kind() == kind);
let first = matches.next()?;
matches.next().is_none().then_some(first)
}
fn find_env_by_prefix(&self, prefix: &str) -> Option<&ConfigSource> {
self.iter().find(|s| {
s.as_env_prefix()
.is_some_and(|p| p.eq_ignore_ascii_case(prefix))
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum EnvMetadataTag<'a> {
Prefixed(&'a str),
Bare,
}
impl EnvMetadataTag<'_> {
#[must_use]
pub fn kind(self) -> EnvMetadataTagKind {
match self {
Self::Prefixed(_) => EnvMetadataTagKind::Prefixed,
Self::Bare => EnvMetadataTagKind::Bare,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum EnvMetadataTagKind {
Prefixed,
Bare,
}
impl EnvMetadataTagKind {
pub const ALL: &'static [Self] = &[Self::Prefixed, Self::Bare];
#[must_use]
pub fn is_prefixed(self) -> bool {
matches!(self, Self::Prefixed)
}
#[must_use]
pub fn is_bare(self) -> bool {
matches!(self, Self::Bare)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Prefixed => "prefixed",
Self::Bare => "bare",
}
}
}
impl crate::ClosedAxis for EnvMetadataTagKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for EnvMetadataTagKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
impl fmt::Display for EnvMetadataTagKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for EnvMetadataTagKind {
type Err = crate::ShikumiError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
<Self as crate::ClosedAxisLabel>::from_canonical_str(s).ok_or_else(|| {
crate::ShikumiError::Parse(format!("unknown env metadata tag kind: {s}"))
})
}
}
impl serde::Serialize for EnvMetadataTagKind {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'de> serde::Deserialize<'de> for EnvMetadataTagKind {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct EnvMetadataTagKindVisitor;
impl serde::de::Visitor<'_> for EnvMetadataTagKindVisitor {
type Value = EnvMetadataTagKind;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(
"a canonical EnvMetadataTagKind lowercase label \
(`prefixed`, `bare`; case-insensitive)",
)
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<EnvMetadataTagKind, E> {
v.parse::<EnvMetadataTagKind>().map_err(E::custom)
}
}
deserializer.deserialize_str(EnvMetadataTagKindVisitor)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FigmentNameTag<'a> {
Format(crate::discovery::FormatMetadataTag<'a>),
Env(EnvMetadataTag<'a>),
}
impl<'a> FigmentNameTag<'a> {
#[must_use]
pub fn classify(name: &'a str) -> Option<Self> {
if let Some(tag) = crate::discovery::Format::parse_metadata_tag(name) {
return Some(Self::Format(tag));
}
if let Some(tag) = ConfigSource::strip_env_metadata_name(name) {
return Some(Self::Env(tag));
}
None
}
#[must_use]
pub fn as_format(self) -> Option<crate::discovery::FormatMetadataTag<'a>> {
match self {
Self::Format(tag) => Some(tag),
Self::Env(_) => None,
}
}
#[must_use]
pub fn as_env(self) -> Option<EnvMetadataTag<'a>> {
match self {
Self::Env(tag) => Some(tag),
Self::Format(_) => None,
}
}
#[must_use]
pub fn kind(self) -> FigmentNameTagKind {
match self {
Self::Format(_) => FigmentNameTagKind::Format,
Self::Env(_) => FigmentNameTagKind::Env,
}
}
#[must_use]
pub fn attribution_axis(self) -> crate::AttributionAxis {
let _ = self.kind();
crate::AttributionAxis::MetadataName
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum FigmentNameTagKind {
Format,
Env,
}
impl FigmentNameTagKind {
pub const ALL: &'static [Self] = &[Self::Format, Self::Env];
#[must_use]
pub fn is_format(self) -> bool {
matches!(self, Self::Format)
}
#[must_use]
pub fn is_env(self) -> bool {
matches!(self, Self::Env)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Format => "format",
Self::Env => "env",
}
}
}
impl crate::ClosedAxis for FigmentNameTagKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for FigmentNameTagKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
closed_axis_label_string_surface! {
type = FigmentNameTagKind,
parse_error = "unknown figment name tag kind",
expecting = "a canonical FigmentNameTagKind lowercase label \
(`format`, `env`; case-insensitive)",
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FigmentSourceTag<'a> {
File(&'a Path),
Code(&'static Location<'static>),
Custom(&'a str),
}
impl<'a> FigmentSourceTag<'a> {
#[must_use]
pub fn classify(source: &'a figment::Source) -> Option<Self> {
if let Some(p) = source.file_path() {
return Some(Self::File(p));
}
if let Some(loc) = source.code_location() {
return Some(Self::Code(loc));
}
if let Some(c) = source.custom() {
return Some(Self::Custom(c));
}
None
}
#[must_use]
pub fn as_file_path(self) -> Option<&'a Path> {
match self {
Self::File(p) => Some(p),
_ => None,
}
}
#[must_use]
pub fn is_code(self) -> bool {
matches!(self.kind(), FigmentSourceKind::Code)
}
#[must_use]
pub fn as_custom(self) -> Option<&'a str> {
match self {
Self::Custom(c) => Some(c),
_ => None,
}
}
#[must_use]
pub fn kind(self) -> FigmentSourceKind {
match self {
Self::File(_) => FigmentSourceKind::File,
Self::Code(_) => FigmentSourceKind::Code,
Self::Custom(_) => FigmentSourceKind::Custom,
}
}
#[must_use]
pub fn attribution_axis(self) -> crate::AttributionAxis {
let _ = self.kind();
crate::AttributionAxis::MetadataSource
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[non_exhaustive]
pub enum FigmentSourceKind {
File,
Code,
Custom,
}
impl FigmentSourceKind {
pub const ALL: &'static [Self] = &[Self::File, Self::Code, Self::Custom];
#[must_use]
pub fn is_file(self) -> bool {
matches!(self, Self::File)
}
#[must_use]
pub fn is_code(self) -> bool {
matches!(self, Self::Code)
}
#[must_use]
pub fn is_custom(self) -> bool {
matches!(self, Self::Custom)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::File => "file",
Self::Code => "code",
Self::Custom => "custom",
}
}
}
impl crate::ClosedAxis for FigmentSourceKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for FigmentSourceKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
closed_axis_label_string_surface! {
type = FigmentSourceKind,
parse_error = "unknown figment source kind",
expecting = "a canonical FigmentSourceKind lowercase label \
(`file`, `code`, `custom`; case-insensitive)",
}
impl fmt::Display for ConfigSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Defaults => f.write_str("defaults"),
Self::Env(prefix) if prefix.is_empty() => f.write_str("env"),
Self::Env(prefix) => write!(f, "env({prefix})"),
Self::File(path) => write!(f, "file({})", path.display()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_display() {
assert_eq!(ConfigSource::Defaults.to_string(), "defaults");
}
#[test]
fn env_display_with_prefix() {
let s = ConfigSource::Env("MYAPP_".to_owned());
assert_eq!(s.to_string(), "env(MYAPP_)");
}
#[test]
fn env_display_empty_prefix() {
assert_eq!(ConfigSource::Env(String::new()).to_string(), "env");
}
#[test]
fn file_display_includes_path() {
let s = ConfigSource::File(PathBuf::from("/etc/app/app.yaml"));
assert_eq!(s.to_string(), "file(/etc/app/app.yaml)");
}
#[test]
fn as_path_returns_path_for_file_only() {
let f = ConfigSource::File(PathBuf::from("/x.yaml"));
assert_eq!(f.as_path(), Some(Path::new("/x.yaml")));
assert_eq!(ConfigSource::Defaults.as_path(), None);
assert_eq!(ConfigSource::Env("X_".to_owned()).as_path(), None);
}
#[test]
fn file_format_reports_extension_format_for_file_only() {
use crate::discovery::Format;
assert_eq!(
ConfigSource::File(PathBuf::from("/etc/app/app.yaml")).file_format(),
Some(Format::Yaml)
);
assert_eq!(
ConfigSource::File(PathBuf::from("app.yml")).file_format(),
Some(Format::Yaml)
);
assert_eq!(
ConfigSource::File(PathBuf::from("app.toml")).file_format(),
Some(Format::Toml)
);
assert_eq!(
ConfigSource::File(PathBuf::from("app.lisp")).file_format(),
Some(Format::Lisp)
);
assert_eq!(
ConfigSource::File(PathBuf::from("app.nix")).file_format(),
Some(Format::Nix)
);
assert_eq!(ConfigSource::Defaults.file_format(), None);
assert_eq!(ConfigSource::Env("APP_".to_owned()).file_format(), None);
}
#[test]
fn file_format_none_for_unrecognized_or_extensionless_file() {
let unknown = ConfigSource::File(PathBuf::from("app.conf"));
assert_eq!(unknown.file_format(), None);
assert!(unknown.is_file());
let no_ext = ConfigSource::File(PathBuf::from("app"));
assert_eq!(no_ext.file_format(), None);
assert!(no_ext.is_file());
}
#[test]
fn file_format_agrees_with_format_from_path_pointwise() {
for path in [
"c.yaml", "c.yml", "c.toml", "c.lisp", "c.el", "c.nix", "c.json", "c",
] {
let src = ConfigSource::File(PathBuf::from(path));
assert_eq!(
src.file_format(),
crate::discovery::Format::from_path(Path::new(path)),
"path: {path}"
);
}
}
#[test]
fn as_env_prefix_returns_prefix_for_env_only() {
let e = ConfigSource::Env("APP_".to_owned());
assert_eq!(e.as_env_prefix(), Some("APP_"));
assert_eq!(ConfigSource::Defaults.as_env_prefix(), None);
assert_eq!(
ConfigSource::File(PathBuf::from("/x")).as_env_prefix(),
None
);
}
#[test]
fn predicates_match_one_variant_each() {
let f = ConfigSource::File(PathBuf::from("/a"));
let e = ConfigSource::Env("E_".to_owned());
let d = ConfigSource::Defaults;
assert!(f.is_file() && !f.is_env() && !f.is_defaults());
assert!(!e.is_file() && e.is_env() && !e.is_defaults());
assert!(!d.is_file() && !d.is_env() && d.is_defaults());
}
#[test]
fn equality_and_hash_distinguish_variants() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ConfigSource::Defaults);
set.insert(ConfigSource::Env("X_".to_owned()));
set.insert(ConfigSource::Env("X_".to_owned())); set.insert(ConfigSource::File(PathBuf::from("/a.yaml")));
assert_eq!(set.len(), 3, "duplicate env entries should collapse");
assert!(set.contains(&ConfigSource::Defaults));
}
#[test]
fn clone_preserves_data() {
let s = ConfigSource::File(PathBuf::from("/a/b.yaml"));
let c = s.clone();
assert_eq!(s, c);
}
fn sample_chain() -> Vec<ConfigSource> {
vec![
ConfigSource::File(PathBuf::from("/etc/app/app.yaml")),
ConfigSource::File(PathBuf::from("/home/u/.config/app/app.yaml")),
ConfigSource::Env("APP_".to_owned()),
]
}
#[test]
fn find_file_returns_matching_file_entry() {
let chain = sample_chain();
let hit = chain
.find_file(Path::new("/home/u/.config/app/app.yaml"))
.expect("a file layer was loaded from that path");
assert_eq!(
hit.as_path(),
Some(Path::new("/home/u/.config/app/app.yaml"))
);
}
#[test]
fn find_file_none_for_unrecorded_path() {
let chain = sample_chain();
assert!(chain.find_file(Path::new("/nope.yaml")).is_none());
}
#[test]
fn find_file_ignores_non_file_layers() {
let chain = [ConfigSource::Defaults, ConfigSource::Env("E_".to_owned())];
assert!(chain.find_file(Path::new("/etc/app/app.yaml")).is_none());
}
#[test]
fn find_file_agrees_with_open_coded_walk_pointwise() {
let chain = sample_chain();
for probe in [
Path::new("/etc/app/app.yaml"),
Path::new("/home/u/.config/app/app.yaml"),
Path::new("/missing.toml"),
] {
let lifted = chain.find_file(probe);
let manual = chain.iter().find(|s| s.as_path() == Some(probe));
assert_eq!(
lifted, manual,
"find_file must match the walk for {probe:?}"
);
}
}
#[test]
fn unique_of_kind_returns_sole_layer() {
let chain = [
ConfigSource::Defaults,
ConfigSource::Env("ONLY_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
];
let env = chain
.unique_of_kind(ConfigSourceKind::Env)
.expect("exactly one env layer");
assert_eq!(env.as_env_prefix(), Some("ONLY_"));
let defaults = chain
.unique_of_kind(ConfigSourceKind::Defaults)
.expect("exactly one defaults layer");
assert!(defaults.is_defaults());
}
#[test]
fn unique_of_kind_none_when_ambiguous() {
let chain = sample_chain();
assert!(chain.unique_of_kind(ConfigSourceKind::File).is_none());
}
#[test]
fn unique_of_kind_none_when_absent() {
let chain = sample_chain();
assert!(chain.unique_of_kind(ConfigSourceKind::Defaults).is_none());
}
#[test]
fn unique_of_kind_agrees_with_open_coded_uniqueness() {
let chains = [
Vec::new(),
vec![ConfigSource::Defaults],
sample_chain(),
vec![
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
],
];
for chain in &chains {
for kind in ConfigSourceKind::ALL.iter().copied() {
let lifted = chain.unique_of_kind(kind);
let mut hits = chain.iter().filter(|s| s.kind() == kind);
let manual = hits.next().filter(|_| hits.next().is_none());
assert_eq!(
lifted, manual,
"unique_of_kind({kind:?}) must match the open-coded uniqueness walk"
);
}
}
}
#[test]
fn find_env_by_prefix_returns_matching_env_entry() {
let chain = sample_chain();
let hit = chain
.find_env_by_prefix("APP_")
.expect("an env layer was recorded with that prefix");
assert_eq!(hit.as_env_prefix(), Some("APP_"));
}
#[test]
fn find_env_by_prefix_matches_case_insensitively() {
let chain = [ConfigSource::Env("myapp_".to_owned())];
let hit = chain
.find_env_by_prefix("MYAPP_")
.expect("ASCII case-insensitive match must locate the layer");
assert_eq!(hit.as_env_prefix(), Some("myapp_"));
let chain = [ConfigSource::Env("MyApp_".to_owned())];
assert!(chain.find_env_by_prefix("MYAPP_").is_some());
assert!(chain.find_env_by_prefix("myapp_").is_some());
assert!(chain.find_env_by_prefix("myApp_").is_some());
}
#[test]
fn find_env_by_prefix_none_for_unrecorded_prefix() {
let chain = sample_chain();
assert!(chain.find_env_by_prefix("OTHER_").is_none());
}
#[test]
fn find_env_by_prefix_ignores_non_env_layers() {
let chain = [
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/etc/APP_/app.yaml")),
];
assert!(chain.find_env_by_prefix("APP_").is_none());
}
#[test]
fn find_env_by_prefix_returns_first_match() {
let first = ConfigSource::Env("DUP_".to_owned());
let second = ConfigSource::Env("DUP_".to_owned());
let chain = [first.clone(), second];
let hit = chain
.find_env_by_prefix("DUP_")
.expect("first matching env layer");
assert!(std::ptr::eq(hit, &chain[0]));
}
#[test]
fn find_env_by_prefix_matches_empty_prefix() {
let chain = [ConfigSource::Env(String::new())];
let hit = chain
.find_env_by_prefix("")
.expect("empty-prefix env layer");
assert_eq!(hit.as_env_prefix(), Some(""));
}
#[test]
fn find_env_by_prefix_agrees_with_open_coded_walk_pointwise() {
let chains = [
Vec::new(),
vec![ConfigSource::Defaults],
sample_chain(),
vec![
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
],
vec![ConfigSource::Env("MixedCase_".to_owned())],
];
for chain in &chains {
for probe in ["APP_", "app_", "A_", "MIXEDCASE_", "OTHER_", ""] {
let lifted = chain.find_env_by_prefix(probe);
let manual = chain.iter().find(|s| {
s.as_env_prefix()
.is_some_and(|p| p.eq_ignore_ascii_case(probe))
});
assert_eq!(
lifted, manual,
"find_env_by_prefix({probe:?}) must match the open-coded walk"
);
}
}
}
#[test]
fn layer_kind_histogram_counts_each_kind_pointwise() {
let chain = sample_chain();
let hist = chain.as_slice().layer_kind_histogram();
assert_eq!(hist.count(ConfigSourceKind::File), 2);
assert_eq!(hist.count(ConfigSourceKind::Env), 1);
assert_eq!(hist.count(ConfigSourceKind::Defaults), 0);
assert_eq!(hist.total(), chain.len());
}
#[test]
fn layer_kind_histogram_empty_chain_is_zero_on_every_cell() {
let chain: [ConfigSource; 0] = [];
let hist = chain.layer_kind_histogram();
for kind in ConfigSourceKind::ALL.iter().copied() {
assert_eq!(
hist.count(kind),
0,
"empty chain must read zero on every kind cell ({kind:?})",
);
}
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
}
#[test]
fn layer_kind_histogram_agrees_with_open_coded_per_kind_count() {
let chains = [
Vec::new(),
vec![ConfigSource::Defaults],
sample_chain(),
vec![
ConfigSource::Defaults,
ConfigSource::Defaults,
ConfigSource::Env("X_".to_owned()),
],
];
for chain in &chains {
let hist = chain.as_slice().layer_kind_histogram();
for kind in ConfigSourceKind::ALL.iter().copied() {
let manual = chain.iter().filter(|s| s.kind() == kind).count();
assert_eq!(
hist.count(kind),
manual,
"layer_kind_histogram({kind:?}) must equal the open-coded \
filter-count over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn layer_kind_histogram_iter_yields_declaration_order() {
let chain = vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::Env("E_".to_owned()),
ConfigSource::Defaults,
];
let pairs: Vec<(ConfigSourceKind, usize)> =
chain.as_slice().layer_kind_histogram().iter().collect();
let values: Vec<ConfigSourceKind> = pairs.iter().map(|(k, _)| *k).collect();
assert_eq!(values, ConfigSourceKind::ALL.to_vec());
}
#[test]
fn layer_kind_histogram_equals_axis_histogram_over_kind_projection() {
let chains = [
sample_chain(),
vec![ConfigSource::Defaults, ConfigSource::Defaults],
vec![
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
ConfigSource::File(PathBuf::from("/x.toml")),
],
];
for chain in &chains {
let lifted = chain.as_slice().layer_kind_histogram();
let generic = crate::axis_histogram(chain.iter().map(ConfigSource::kind));
for kind in ConfigSourceKind::ALL.iter().copied() {
assert_eq!(
lifted.count(kind),
generic.count(kind),
"layer_kind_histogram must equal axis_histogram(kind-projection) \
on {kind:?} over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn file_format_histogram_counts_each_recognized_format_pointwise() {
use crate::discovery::Format;
let chain = sample_chain();
let hist = chain.as_slice().file_format_histogram();
assert_eq!(hist.count(Format::Yaml), 2);
assert_eq!(hist.count(Format::Toml), 0);
assert_eq!(hist.count(Format::Lisp), 0);
assert_eq!(hist.count(Format::Nix), 0);
assert_eq!(hist.total(), 2);
}
#[test]
fn file_format_histogram_covers_every_recognized_format() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.toml")),
ConfigSource::File(PathBuf::from("/c.lisp")),
ConfigSource::File(PathBuf::from("/d.nix")),
];
let hist = chain.as_slice().file_format_histogram();
for format in Format::ALL.iter().copied() {
assert_eq!(
hist.count(format),
1,
"uniform-cover chain must read 1 on every Format cell ({format:?})",
);
}
assert_eq!(hist.total(), Format::ALL.len());
}
#[test]
fn file_format_histogram_empty_chain_is_zero_on_every_cell() {
use crate::discovery::Format;
let chain: [ConfigSource; 0] = [];
let hist = chain.file_format_histogram();
for format in Format::ALL.iter().copied() {
assert_eq!(
hist.count(format),
0,
"empty chain must read zero on every Format cell ({format:?})",
);
}
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
}
#[test]
fn file_format_histogram_ignores_defaults_and_env_layers() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::Env(String::new()),
];
let hist = chain.as_slice().file_format_histogram();
for format in Format::ALL.iter().copied() {
assert_eq!(
hist.count(format),
0,
"Defaults/Env-only chain must read zero on every Format cell ({format:?})",
);
}
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
}
#[test]
fn file_format_histogram_ignores_unrecognized_and_extensionless_files() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::File(PathBuf::from("/etc/cfg.unknownext")),
ConfigSource::File(PathBuf::from("/etc/no_extension")),
ConfigSource::File(PathBuf::from("/etc/.dotfile")),
];
let hist = chain.as_slice().file_format_histogram();
for format in Format::ALL.iter().copied() {
assert_eq!(
hist.count(format),
0,
"unrecognized-extension chain must read zero on {format:?}",
);
}
assert_eq!(hist.total(), 0);
}
#[test]
fn file_format_histogram_agrees_with_open_coded_per_format_count() {
use crate::discovery::Format;
let chains = [
Vec::new(),
vec![ConfigSource::Defaults],
sample_chain(),
vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yml")),
ConfigSource::File(PathBuf::from("/c.toml")),
ConfigSource::File(PathBuf::from("/d.lisp")),
ConfigSource::File(PathBuf::from("/e.unknownext")),
ConfigSource::Env("X_".to_owned()),
],
];
for chain in &chains {
let hist = chain.as_slice().file_format_histogram();
for format in Format::ALL.iter().copied() {
let manual = chain
.iter()
.filter_map(ConfigSource::file_format)
.filter(|f| *f == format)
.count();
assert_eq!(
hist.count(format),
manual,
"file_format_histogram({format:?}) must equal the open-coded \
filter_map+filter count over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn file_format_histogram_iter_yields_format_all_declaration_order() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::File(PathBuf::from("/a.nix")),
ConfigSource::File(PathBuf::from("/b.lisp")),
ConfigSource::File(PathBuf::from("/c.toml")),
ConfigSource::File(PathBuf::from("/d.yaml")),
];
let pairs: Vec<(Format, usize)> = chain.as_slice().file_format_histogram().iter().collect();
let values: Vec<Format> = pairs.iter().map(|(f, _)| *f).collect();
assert_eq!(values, Format::ALL.to_vec());
}
#[test]
fn file_format_histogram_equals_axis_histogram_over_file_format_projection() {
use crate::discovery::Format;
let chains = [
sample_chain(),
vec![
ConfigSource::Defaults,
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/x.toml")),
],
vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.lisp")),
ConfigSource::File(PathBuf::from("/c.unknownext")),
ConfigSource::Env("E_".to_owned()),
],
];
for chain in &chains {
let lifted = chain.as_slice().file_format_histogram();
let generic = crate::axis_histogram(chain.iter().filter_map(ConfigSource::file_format));
for format in Format::ALL.iter().copied() {
assert_eq!(
lifted.count(format),
generic.count(format),
"file_format_histogram must equal \
axis_histogram(file_format-projection) on {format:?} \
over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn file_format_histogram_total_bounded_by_file_layer_count() {
let chains: [Vec<ConfigSource>; 4] = [
sample_chain(),
vec![ConfigSource::Defaults, ConfigSource::Env("E_".to_owned())],
vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.unknownext")),
ConfigSource::File(PathBuf::from("/c.no_extension")),
],
vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.toml")),
ConfigSource::File(PathBuf::from("/c.lisp")),
ConfigSource::File(PathBuf::from("/d.nix")),
],
];
for chain in &chains {
let file_kind_count = chain
.as_slice()
.layer_kind_histogram()
.count(ConfigSourceKind::File);
let format_total = chain.as_slice().file_format_histogram().total();
assert!(
format_total <= file_kind_count,
"file_format_histogram total ({format_total}) must be at most \
layer_kind_histogram(File) ({file_kind_count}) over chain of length {}",
chain.len(),
);
}
let uniform = vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.toml")),
ConfigSource::File(PathBuf::from("/c.lisp")),
ConfigSource::File(PathBuf::from("/d.nix")),
];
let file_kind_count = uniform
.as_slice()
.layer_kind_histogram()
.count(ConfigSourceKind::File);
let format_total = uniform.as_slice().file_format_histogram().total();
assert_eq!(
format_total, file_kind_count,
"uniform-cover chain: file_format_histogram total must equal \
layer_kind_histogram(File)",
);
let mixed = vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.unknownext")),
];
let file_kind_count = mixed
.as_slice()
.layer_kind_histogram()
.count(ConfigSourceKind::File);
let format_total = mixed.as_slice().file_format_histogram().total();
assert!(
format_total < file_kind_count,
"mixed-extension chain: file_format_histogram total ({format_total}) \
must be strictly less than layer_kind_histogram(File) ({file_kind_count})",
);
}
#[test]
fn env_prefix_kind_classifies_empty_prefix_as_bare() {
let s = ConfigSource::Env(String::new());
assert_eq!(s.env_prefix_kind(), Some(EnvMetadataTagKind::Bare));
}
#[test]
fn env_prefix_kind_classifies_non_empty_prefix_as_prefixed() {
for prefix in [
"MYAPP_",
"x_",
"MixedCase_",
"very_long_prefix_with_underscores_",
"A",
] {
let s = ConfigSource::Env(prefix.to_owned());
assert_eq!(
s.env_prefix_kind(),
Some(EnvMetadataTagKind::Prefixed),
"non-empty prefix {prefix:?} must classify as Prefixed",
);
}
}
#[test]
fn env_prefix_kind_is_none_for_non_env_sources() {
assert_eq!(ConfigSource::Defaults.env_prefix_kind(), None);
assert_eq!(
ConfigSource::File(PathBuf::from("/etc/app.yaml")).env_prefix_kind(),
None,
);
assert_eq!(
ConfigSource::File(PathBuf::from("/etc/app.unknownext")).env_prefix_kind(),
None,
);
}
#[test]
fn env_prefix_kind_partitions_env_variants_pointwise() {
let mut reached: Vec<EnvMetadataTagKind> = Vec::new();
for layer in [
ConfigSource::Env(String::new()),
ConfigSource::Env("X_".to_owned()),
] {
let k = layer
.env_prefix_kind()
.expect("every Env layer must project to Some");
assert!(
!reached.contains(&k),
"env_prefix_kind must not project two Env layers to the same kind in this sample",
);
reached.push(k);
}
for kind in EnvMetadataTagKind::ALL {
assert!(
reached.contains(kind),
"EnvMetadataTagKind::{kind:?} must be reachable from some Env layer",
);
}
}
#[test]
fn env_prefix_kind_agrees_with_figment_env_metadata_tag_kind() {
for prefix in ["", "APP_", "MYAPP_", "x_", "MixedCase_"] {
let chain_side = ConfigSource::Env(prefix.to_owned())
.env_prefix_kind()
.expect("every Env layer projects through env_prefix_kind");
let figment_side =
ConfigSource::strip_env_metadata_name(&ConfigSource::env_metadata_name(prefix))
.map(EnvMetadataTag::kind)
.expect("env_metadata_name round-trips through strip_env_metadata_name");
assert_eq!(
chain_side, figment_side,
"env_prefix_kind({prefix:?}) must agree with the figment-side EnvMetadataTag::kind",
);
}
}
#[test]
fn env_prefix_kind_histogram_counts_each_kind_pointwise() {
let chain = sample_chain();
let hist = chain.as_slice().env_prefix_kind_histogram();
assert_eq!(hist.count(EnvMetadataTagKind::Prefixed), 1);
assert_eq!(hist.count(EnvMetadataTagKind::Bare), 0);
assert_eq!(hist.total(), 1);
}
#[test]
fn env_prefix_kind_histogram_covers_every_kind() {
let chain = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("APP_".to_owned()),
];
let hist = chain.as_slice().env_prefix_kind_histogram();
for kind in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
hist.count(kind),
1,
"uniform-cover chain must read 1 on every EnvMetadataTagKind cell ({kind:?})",
);
}
assert_eq!(hist.total(), EnvMetadataTagKind::ALL.len());
}
#[test]
fn env_prefix_kind_histogram_empty_chain_is_zero_on_every_cell() {
let chain: [ConfigSource; 0] = [];
let hist = chain.env_prefix_kind_histogram();
for kind in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
hist.count(kind),
0,
"empty chain must read zero on every EnvMetadataTagKind cell ({kind:?})",
);
}
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
}
#[test]
fn env_prefix_kind_histogram_ignores_defaults_and_file_layers() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
ConfigSource::File(PathBuf::from("/etc/app.unknownext")),
];
let hist = chain.as_slice().env_prefix_kind_histogram();
for kind in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
hist.count(kind),
0,
"Defaults/File-only chain must read zero on every \
EnvMetadataTagKind cell ({kind:?})",
);
}
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
}
#[test]
fn env_prefix_kind_histogram_agrees_with_open_coded_per_kind_count() {
let chains = [
Vec::new(),
vec![ConfigSource::Defaults],
sample_chain(),
vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
ConfigSource::Env(String::new()),
ConfigSource::File(PathBuf::from("/a.yaml")),
],
];
for chain in &chains {
let hist = chain.as_slice().env_prefix_kind_histogram();
for kind in EnvMetadataTagKind::ALL.iter().copied() {
let manual = chain
.iter()
.filter_map(ConfigSource::env_prefix_kind)
.filter(|k| *k == kind)
.count();
assert_eq!(
hist.count(kind),
manual,
"env_prefix_kind_histogram({kind:?}) must equal the open-coded \
filter_map+filter count over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn env_prefix_kind_histogram_iter_yields_declaration_order() {
let chain = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("LATER_".to_owned()),
ConfigSource::Env(String::new()),
];
let pairs: Vec<(EnvMetadataTagKind, usize)> = chain
.as_slice()
.env_prefix_kind_histogram()
.iter()
.collect();
let values: Vec<EnvMetadataTagKind> = pairs.iter().map(|(k, _)| *k).collect();
assert_eq!(values, EnvMetadataTagKind::ALL.to_vec());
}
#[test]
fn env_prefix_kind_histogram_equals_axis_histogram_over_env_prefix_kind_projection() {
let chains = [
sample_chain(),
vec![
ConfigSource::Defaults,
ConfigSource::Defaults,
ConfigSource::Env("X_".to_owned()),
],
vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("A_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
],
];
for chain in &chains {
let lifted = chain.as_slice().env_prefix_kind_histogram();
let generic =
crate::axis_histogram(chain.iter().filter_map(ConfigSource::env_prefix_kind));
for kind in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
lifted.count(kind),
generic.count(kind),
"env_prefix_kind_histogram must equal \
axis_histogram(env_prefix_kind-projection) on {kind:?} \
over chain of length {}",
chain.len(),
);
}
}
}
#[test]
fn layer_kind_histogram_dominant_cell_picks_majority_kind() {
let chain = sample_chain();
assert_eq!(
chain.as_slice().layer_kind_histogram().dominant_cell(),
Some(ConfigSourceKind::File),
);
}
#[test]
fn file_format_histogram_dominant_cell_picks_majority_format() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yaml")),
ConfigSource::File(PathBuf::from("/c.yaml")),
ConfigSource::File(PathBuf::from("/d.toml")),
];
assert_eq!(
chain.as_slice().file_format_histogram().dominant_cell(),
Some(Format::Yaml),
);
}
#[test]
fn env_prefix_kind_histogram_dominant_cell_for_mixed_prefix_chain() {
let chain = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("APP_".to_owned()),
];
assert_eq!(
chain.as_slice().env_prefix_kind_histogram().dominant_cell(),
Some(EnvMetadataTagKind::Prefixed),
);
let bare_majority = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env(String::new()),
ConfigSource::Env("X_".to_owned()),
];
assert_eq!(
bare_majority
.as_slice()
.env_prefix_kind_histogram()
.dominant_cell(),
Some(EnvMetadataTagKind::Bare),
);
}
#[test]
fn chain_histograms_dominant_cell_is_none_on_empty_chain() {
let chain: [ConfigSource; 0] = [];
assert_eq!(chain.layer_kind_histogram().dominant_cell(), None);
assert_eq!(chain.file_format_histogram().dominant_cell(), None);
assert_eq!(chain.env_prefix_kind_histogram().dominant_cell(), None);
}
#[test]
fn layer_kind_histogram_distinct_cells_counts_observed_kinds() {
let chain = sample_chain();
assert_eq!(chain.as_slice().layer_kind_histogram().distinct_cells(), 2);
let defaults_only = vec![ConfigSource::Defaults, ConfigSource::Defaults];
assert_eq!(
defaults_only
.as_slice()
.layer_kind_histogram()
.distinct_cells(),
1,
);
let axis_cover = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
];
assert!(axis_cover.as_slice().layer_kind_histogram().is_full_cover(),);
}
#[test]
fn file_format_histogram_distinct_cells_counts_observed_formats() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yaml")),
ConfigSource::File(PathBuf::from("/c.yaml")),
ConfigSource::File(PathBuf::from("/d.toml")),
];
let hist = chain.as_slice().file_format_histogram();
assert_eq!(hist.distinct_cells(), 2);
assert!(hist.distinct_cells() <= crate::axis_cardinality::<Format>());
assert!(hist.distinct_cells() <= hist.total());
let env_only = vec![
ConfigSource::Env("APP_".to_owned()),
ConfigSource::Env(String::new()),
];
assert_eq!(
env_only.as_slice().file_format_histogram().distinct_cells(),
0,
);
}
#[test]
fn env_prefix_kind_histogram_distinct_cells_counts_observed_prefix_kinds() {
let both = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("APP_".to_owned()),
];
assert!(both.as_slice().env_prefix_kind_histogram().is_full_cover(),);
let prefixed_only = vec![
ConfigSource::Env("APP_".to_owned()),
ConfigSource::Env("APP_".to_owned()),
ConfigSource::Env("OTHER_".to_owned()),
];
assert_eq!(
prefixed_only
.as_slice()
.env_prefix_kind_histogram()
.distinct_cells(),
1,
);
let no_env = vec![
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/x.yaml")),
];
assert_eq!(
no_env
.as_slice()
.env_prefix_kind_histogram()
.distinct_cells(),
0,
);
}
#[test]
fn layer_kind_histogram_recessive_cell_picks_minority_kind() {
let chain = sample_chain();
assert_eq!(
chain.as_slice().layer_kind_histogram().recessive_cell(),
Some(ConfigSourceKind::Env),
);
}
#[test]
fn file_format_histogram_recessive_cell_picks_minority_format() {
use crate::discovery::Format;
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yaml")),
ConfigSource::File(PathBuf::from("/c.yaml")),
ConfigSource::File(PathBuf::from("/d.toml")),
];
assert_eq!(
chain.as_slice().file_format_histogram().recessive_cell(),
Some(Format::Toml),
);
}
#[test]
fn env_prefix_kind_histogram_recessive_cell_for_mixed_prefix_chain() {
let chain = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("APP_".to_owned()),
];
assert_eq!(
chain
.as_slice()
.env_prefix_kind_histogram()
.recessive_cell(),
Some(EnvMetadataTagKind::Prefixed),
);
let bare_majority = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env(String::new()),
ConfigSource::Env("X_".to_owned()),
];
assert_eq!(
bare_majority
.as_slice()
.env_prefix_kind_histogram()
.recessive_cell(),
Some(EnvMetadataTagKind::Prefixed),
);
}
#[test]
fn chain_histograms_recessive_cell_is_none_on_empty_chain() {
let chain: [ConfigSource; 0] = [];
assert_eq!(chain.layer_kind_histogram().recessive_cell(), None);
assert_eq!(chain.file_format_histogram().recessive_cell(), None);
assert_eq!(chain.env_prefix_kind_histogram().recessive_cell(), None);
}
#[test]
fn chain_histograms_dominant_and_recessive_agree_on_uniform_singleton_chain() {
use crate::discovery::Format;
let defaults_only = vec![ConfigSource::Defaults, ConfigSource::Defaults];
let layer_hist = defaults_only.as_slice().layer_kind_histogram();
assert_eq!(layer_hist.dominant_cell(), Some(ConfigSourceKind::Defaults));
assert_eq!(layer_hist.recessive_cell(), layer_hist.dominant_cell());
let yaml_only = vec![
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yaml")),
];
let format_hist = yaml_only.as_slice().file_format_histogram();
assert_eq!(format_hist.dominant_cell(), Some(Format::Yaml));
assert_eq!(format_hist.recessive_cell(), format_hist.dominant_cell());
let bare_only = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env(String::new()),
];
let env_hist = bare_only.as_slice().env_prefix_kind_histogram();
assert_eq!(env_hist.dominant_cell(), Some(EnvMetadataTagKind::Bare));
assert_eq!(env_hist.recessive_cell(), env_hist.dominant_cell());
}
#[test]
fn chain_histograms_spread_is_zero_on_empty_chain() {
let chain: [ConfigSource; 0] = [];
assert_eq!(chain.layer_kind_histogram().spread(), 0);
assert_eq!(chain.file_format_histogram().spread(), 0);
assert_eq!(chain.env_prefix_kind_histogram().spread(), 0);
}
#[test]
fn layer_kind_histogram_spread_reads_distribution_skew() {
let chain = sample_chain();
let hist = chain.as_slice().layer_kind_histogram();
assert_eq!(hist.peak_count(), 2);
assert_eq!(hist.trough_count(), 1);
assert_eq!(hist.spread(), 1);
let defaults_only = vec![ConfigSource::Defaults, ConfigSource::Defaults];
assert_eq!(defaults_only.as_slice().layer_kind_histogram().spread(), 0);
let axis_cover = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
];
assert_eq!(axis_cover.as_slice().layer_kind_histogram().spread(), 0);
}
#[test]
fn layer_kind_histogram_unobserved_lists_unused_layer_kinds() {
use crate::cube::axis_cardinality;
use std::collections::HashSet;
let chain = sample_chain();
let gap: HashSet<ConfigSourceKind> = chain
.as_slice()
.layer_kind_histogram()
.unobserved()
.collect();
assert_eq!(gap, HashSet::from([ConfigSourceKind::Defaults]));
let defaults_only = vec![ConfigSource::Defaults, ConfigSource::Defaults];
let defaults_gap: HashSet<ConfigSourceKind> = defaults_only
.as_slice()
.layer_kind_histogram()
.unobserved()
.collect();
assert_eq!(
defaults_gap,
HashSet::from([ConfigSourceKind::Env, ConfigSourceKind::File]),
);
let axis_cover = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/x.yaml")),
];
assert_eq!(
axis_cover
.as_slice()
.layer_kind_histogram()
.unobserved()
.count(),
0,
);
assert_eq!(
axis_cover
.as_slice()
.layer_kind_histogram()
.distinct_cells(),
axis_cardinality::<ConfigSourceKind>(),
);
}
#[test]
fn file_format_histogram_unobserved_lists_unused_formats() {
use crate::discovery::Format;
use std::collections::HashSet;
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::File(PathBuf::from("/b.yaml")),
ConfigSource::File(PathBuf::from("/c.yaml")),
ConfigSource::File(PathBuf::from("/d.toml")),
];
let gap: HashSet<Format> = chain
.as_slice()
.file_format_histogram()
.unobserved()
.collect();
assert_eq!(gap, HashSet::from([Format::Lisp, Format::Nix]));
let env_only = vec![ConfigSource::Env(String::new())];
let env_only_gap: HashSet<Format> = env_only
.as_slice()
.file_format_histogram()
.unobserved()
.collect();
assert_eq!(env_only_gap, Format::ALL.iter().copied().collect());
}
#[test]
fn env_prefix_kind_histogram_unobserved_lists_unused_prefix_kinds() {
use std::collections::HashSet;
let bare_only = vec![
ConfigSource::Env(String::new()),
ConfigSource::Env(String::new()),
];
let bare_gap: HashSet<EnvMetadataTagKind> = bare_only
.as_slice()
.env_prefix_kind_histogram()
.unobserved()
.collect();
assert_eq!(bare_gap, HashSet::from([EnvMetadataTagKind::Prefixed]));
let prefixed_only = vec![
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
];
let prefixed_gap: HashSet<EnvMetadataTagKind> = prefixed_only
.as_slice()
.env_prefix_kind_histogram()
.unobserved()
.collect();
assert_eq!(prefixed_gap, HashSet::from([EnvMetadataTagKind::Bare]));
let no_env = vec![
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/x.yaml")),
];
let no_env_gap: HashSet<EnvMetadataTagKind> = no_env
.as_slice()
.env_prefix_kind_histogram()
.unobserved()
.collect();
assert_eq!(
no_env_gap,
EnvMetadataTagKind::ALL.iter().copied().collect(),
);
}
#[test]
fn chain_histograms_unobserved_is_full_axis_on_empty_chain() {
use crate::cube::axis_cardinality;
let chain: [ConfigSource; 0] = [];
assert_eq!(
chain.layer_kind_histogram().unobserved_cells(),
axis_cardinality::<ConfigSourceKind>(),
);
assert_eq!(
chain.file_format_histogram().unobserved_cells(),
axis_cardinality::<crate::discovery::Format>(),
);
assert_eq!(
chain.env_prefix_kind_histogram().unobserved_cells(),
axis_cardinality::<EnvMetadataTagKind>(),
);
}
#[test]
fn chain_histograms_distinct_cells_is_zero_on_empty_chain() {
let chain: [ConfigSource; 0] = [];
assert_eq!(chain.layer_kind_histogram().distinct_cells(), 0);
assert_eq!(chain.file_format_histogram().distinct_cells(), 0);
assert_eq!(chain.env_prefix_kind_histogram().distinct_cells(), 0);
}
#[test]
fn env_prefix_kind_histogram_total_equals_env_layer_count() {
let chains: [Vec<ConfigSource>; 5] = [
sample_chain(),
vec![],
vec![ConfigSource::Defaults, ConfigSource::Defaults],
vec![
ConfigSource::Env(String::new()),
ConfigSource::Env("A_".to_owned()),
ConfigSource::Env("B_".to_owned()),
ConfigSource::Env(String::new()),
],
vec![
ConfigSource::Defaults,
ConfigSource::File(PathBuf::from("/a.yaml")),
ConfigSource::Env("E_".to_owned()),
ConfigSource::File(PathBuf::from("/b.unknownext")),
ConfigSource::Env(String::new()),
],
];
for chain in &chains {
let env_kind_count = chain
.as_slice()
.layer_kind_histogram()
.count(ConfigSourceKind::Env);
let env_prefix_total = chain.as_slice().env_prefix_kind_histogram().total();
assert_eq!(
env_prefix_total,
env_kind_count,
"env_prefix_kind_histogram total ({env_prefix_total}) must equal \
layer_kind_histogram(Env) ({env_kind_count}) over chain of length {}",
chain.len(),
);
}
}
#[test]
fn kind_classifies_defaults() {
assert_eq!(ConfigSource::Defaults.kind(), ConfigSourceKind::Defaults);
}
#[test]
fn kind_classifies_env_regardless_of_prefix() {
for prefix in ["", "MYAPP_", "X_", "very_long_prefix_with_underscores_"] {
let s = ConfigSource::Env(prefix.to_owned());
assert_eq!(s.kind(), ConfigSourceKind::Env);
}
}
#[test]
fn kind_classifies_file_regardless_of_path() {
for path in ["/etc/app.yaml", "rel.toml", "/very/deep/path/cfg.lisp"] {
let s = ConfigSource::File(PathBuf::from(path));
assert_eq!(s.kind(), ConfigSourceKind::File);
}
}
#[test]
fn kind_partitions_every_constructible_variant() {
let cases: [(ConfigSource, ConfigSourceKind); 3] = [
(ConfigSource::Defaults, ConfigSourceKind::Defaults),
(ConfigSource::Env("X_".to_owned()), ConfigSourceKind::Env),
(
ConfigSource::File(PathBuf::from("/x")),
ConfigSourceKind::File,
),
];
for (src, expected) in &cases {
assert_eq!(src.kind(), *expected);
}
assert_eq!(
ConfigSource::Env("A_".to_owned()).kind(),
ConfigSource::Env("B_".to_owned()).kind(),
);
assert_eq!(
ConfigSource::File(PathBuf::from("/a")).kind(),
ConfigSource::File(PathBuf::from("/b")).kind(),
);
}
#[test]
fn kind_agrees_with_is_predicates_pointwise() {
for src in [
ConfigSource::Defaults,
ConfigSource::Env("X_".to_owned()),
ConfigSource::File(PathBuf::from("/x")),
] {
assert_eq!(src.is_defaults(), src.kind() == ConfigSourceKind::Defaults);
assert_eq!(src.is_env(), src.kind() == ConfigSourceKind::Env);
assert_eq!(src.is_file(), src.kind() == ConfigSourceKind::File);
}
}
#[test]
fn config_source_kind_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set: HashSet<ConfigSourceKind> = ConfigSourceKind::ALL.iter().copied().collect();
set.insert(ConfigSourceKind::Defaults); assert_eq!(set.len(), ConfigSourceKind::ALL.len());
let k = ConfigSourceKind::Env;
let k2 = k;
let k3 = k;
assert_eq!(k, k2);
assert_eq!(k2, k3);
}
#[test]
fn config_source_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<ConfigSourceKind> = ConfigSourceKind::ALL.iter().copied().collect();
assert_eq!(
unique.len(),
ConfigSourceKind::ALL.len(),
"ConfigSourceKind::ALL must contain no duplicates",
);
}
#[test]
fn config_source_kind_all_covers_every_constructible_variant() {
use std::collections::HashSet;
let produced: HashSet<ConfigSourceKind> = [
ConfigSource::Defaults,
ConfigSource::Env("X_".to_owned()),
ConfigSource::File(PathBuf::from("/x")),
]
.iter()
.map(ConfigSource::kind)
.collect();
let listed: HashSet<ConfigSourceKind> = ConfigSourceKind::ALL.iter().copied().collect();
assert_eq!(
produced, listed,
"ConfigSourceKind::ALL must equal the kind set produced by ConfigSource::kind",
);
}
#[test]
fn config_source_kind_all_cardinality_matches_variant_count() {
let produced_count = [
ConfigSource::Defaults,
ConfigSource::Env(String::new()),
ConfigSource::File(PathBuf::from("/x")),
]
.iter()
.map(ConfigSource::kind)
.collect::<std::collections::HashSet<_>>()
.len();
assert_eq!(
ConfigSourceKind::ALL.len(),
produced_count,
"ALL.len() must equal the canonical kind-partition cardinality",
);
}
#[test]
fn config_source_kind_all_iterates_in_declaration_order() {
assert_eq!(
ConfigSourceKind::ALL,
&[
ConfigSourceKind::Defaults,
ConfigSourceKind::Env,
ConfigSourceKind::File,
],
"ALL must list variants in declaration order",
);
}
#[test]
fn config_source_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(ConfigSourceKind::Defaults.as_str(), "defaults");
assert_eq!(ConfigSourceKind::Env.as_str(), "env");
assert_eq!(ConfigSourceKind::File.as_str(), "file");
}
#[test]
fn config_source_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for k in ConfigSourceKind::ALL.iter().copied() {
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str(k.as_str()),
Some(k),
"trait from_canonical_str must round-trip for {k:?}",
);
}
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str("DEFAULTS"),
Some(ConfigSourceKind::Defaults),
);
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str("Env"),
Some(ConfigSourceKind::Env),
);
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str("FILE"),
Some(ConfigSourceKind::File),
);
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str("default"),
None,
);
assert_eq!(
<ConfigSourceKind as ClosedAxisLabel>::from_canonical_str("http"),
None,
);
}
#[test]
fn config_source_kind_all_covers_every_attribution_rule_layer_kind() {
use std::collections::HashSet;
let rule_kinds: HashSet<ConfigSourceKind> = crate::error::AttributionRule::ALL
.iter()
.map(|r| r.layer_kind())
.collect();
let listed: HashSet<ConfigSourceKind> = ConfigSourceKind::ALL.iter().copied().collect();
assert!(
rule_kinds.is_subset(&listed),
"every AttributionRule::layer_kind() must appear in ConfigSourceKind::ALL: \
rule_kinds={rule_kinds:?}, listed={listed:?}",
);
}
#[test]
fn config_source_kind_ord_matches_all_declaration_order() {
use std::cmp::Ordering;
for window in ConfigSourceKind::ALL.windows(2) {
assert!(
window[0] < window[1],
"ConfigSourceKind::ALL must be strictly increasing under Ord, \
but {:?} >= {:?}",
window[0],
window[1],
);
}
for (i, &a) in ConfigSourceKind::ALL.iter().enumerate() {
for (j, &b) in ConfigSourceKind::ALL.iter().enumerate() {
let expected = i.cmp(&j);
assert_eq!(
a.cmp(&b),
expected,
"ConfigSourceKind::cmp must match ALL-index lex for ({a:?}, {b:?})",
);
assert_eq!(
a.partial_cmp(&b),
Some(expected),
"ConfigSourceKind::partial_cmp must agree with cmp for ({a:?}, {b:?})",
);
if i == j {
assert_eq!(a.cmp(&b), Ordering::Equal, "Ord must be reflexive on {a:?}",);
}
}
}
}
#[test]
fn config_source_kind_btreemap_emits_in_declaration_order() {
use std::collections::BTreeMap;
let mut counts: BTreeMap<ConfigSourceKind, u32> = BTreeMap::new();
counts.insert(ConfigSourceKind::File, 3);
counts.insert(ConfigSourceKind::Defaults, 1);
counts.insert(ConfigSourceKind::Env, 2);
let observed: Vec<ConfigSourceKind> = counts.keys().copied().collect();
assert_eq!(
observed,
ConfigSourceKind::ALL.to_vec(),
"BTreeMap<ConfigSourceKind, _> must emit keys in ALL declaration order",
);
}
#[test]
fn config_source_kind_display_matches_as_str() {
for k in ConfigSourceKind::ALL.iter().copied() {
assert_eq!(
format!("{k}"),
k.as_str(),
"Display must agree with as_str for {k:?}",
);
}
}
#[test]
fn config_source_kind_from_str_round_trips_over_every_variant() {
for k in ConfigSourceKind::ALL {
let rendered = k.to_string();
let parsed: ConfigSourceKind = rendered
.parse()
.expect("FromStr must round-trip Display output");
assert_eq!(parsed, *k, "FromStr must round-trip {k:?}");
}
}
#[test]
fn config_source_kind_from_str_is_case_insensitive() {
assert_eq!(
"DEFAULTS".parse::<ConfigSourceKind>().unwrap(),
ConfigSourceKind::Defaults,
);
assert_eq!(
"Env".parse::<ConfigSourceKind>().unwrap(),
ConfigSourceKind::Env,
);
assert_eq!(
"FILE".parse::<ConfigSourceKind>().unwrap(),
ConfigSourceKind::File,
);
}
#[test]
fn config_source_kind_from_str_unknown_kind_error_carries_label_verbatim() {
for bad in &["default", "http", "vault", "", " env"] {
let err = bad
.parse::<ConfigSourceKind>()
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn config_source_kind_serde_yaml_round_trips_over_every_variant() {
for k in ConfigSourceKind::ALL {
let yaml = serde_yaml::to_string(k).expect("Serialize must succeed");
let parsed: ConfigSourceKind =
serde_yaml::from_str(&yaml).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_yaml round-trip must preserve {k:?}");
}
}
#[test]
fn config_source_kind_serde_json_round_trips_over_every_variant() {
for k in ConfigSourceKind::ALL {
let json = serde_json::to_string(k).expect("Serialize must succeed");
let parsed: ConfigSourceKind =
serde_json::from_str(&json).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_json round-trip must preserve {k:?}");
}
}
#[test]
fn config_source_kind_serde_yaml_is_case_insensitive() {
let cases: &[(&str, ConfigSourceKind)] = &[
("Defaults", ConfigSourceKind::Defaults),
("ENV", ConfigSourceKind::Env),
("File", ConfigSourceKind::File),
];
for (input, expected) in cases {
let parsed: ConfigSourceKind =
serde_yaml::from_str(input).expect("case-insensitive Deserialize must succeed");
assert_eq!(
parsed, *expected,
"serde_yaml must parse case-insensitively for input {input:?}",
);
}
}
#[test]
fn config_source_kind_serde_yaml_unknown_kind_error_carries_label_verbatim() {
for bad in &["http", "vault", "default", "configmap"] {
let err = serde_yaml::from_str::<ConfigSourceKind>(bad)
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered serde error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn env_metadata_name_empty_prefix_yields_bare_shape() {
assert_eq!(
ConfigSource::env_metadata_name(""),
"environment variable(s)"
);
}
#[test]
fn env_metadata_name_uppercases_prefix_to_match_figment() {
assert_eq!(
ConfigSource::env_metadata_name("myapp_"),
"`MYAPP_` environment variable(s)"
);
assert_eq!(
ConfigSource::env_metadata_name("MyApp_"),
"`MYAPP_` environment variable(s)"
);
assert_eq!(
ConfigSource::env_metadata_name("APP_"),
"`APP_` environment variable(s)"
);
}
#[test]
fn strip_env_metadata_name_recognizes_prefixed_shape() {
let tag = ConfigSource::strip_env_metadata_name("`MYAPP_` environment variable(s)");
assert_eq!(tag, Some(EnvMetadataTag::Prefixed("MYAPP_")));
}
#[test]
fn strip_env_metadata_name_recognizes_bare_shape() {
let tag = ConfigSource::strip_env_metadata_name("environment variable(s)");
assert_eq!(tag, Some(EnvMetadataTag::Bare));
}
#[test]
fn strip_env_metadata_name_accepts_singular_form() {
let tag = ConfigSource::strip_env_metadata_name("`X_` environment variable");
assert_eq!(tag, Some(EnvMetadataTag::Prefixed("X_")));
}
#[test]
fn strip_env_metadata_name_rejects_unrelated_strings() {
for name in [
"",
"/etc/app/app.yaml",
"lisp: /etc/app.lisp",
"nix: /etc/app.nix",
"yaml",
"`MYAPP_` something else",
"envvar `X_` typo",
] {
assert!(
ConfigSource::strip_env_metadata_name(name).is_none(),
"unrelated metadata name `{name}` must not match env tag"
);
}
}
#[test]
fn env_metadata_name_round_trip_for_prefixed_form() {
for prefix in ["MYAPP_", "TOBIRA_", "X_", "FOO_BAR_"] {
let name = ConfigSource::env_metadata_name(prefix);
let tag = ConfigSource::strip_env_metadata_name(&name)
.expect("constructor output must round-trip through inverse");
assert_eq!(tag, EnvMetadataTag::Prefixed(prefix));
}
}
#[test]
fn env_metadata_name_round_trip_for_bare_form() {
let name = ConfigSource::env_metadata_name("");
let tag = ConfigSource::strip_env_metadata_name(&name).expect("bare must round-trip");
assert_eq!(tag, EnvMetadataTag::Bare);
}
#[test]
fn strip_env_metadata_name_borrows_into_input() {
let name = ConfigSource::env_metadata_name("MYAPP_");
let EnvMetadataTag::Prefixed(prefix) =
ConfigSource::strip_env_metadata_name(&name).expect("prefixed shape must match")
else {
panic!("expected prefixed variant");
};
let name_start = name.as_ptr() as usize;
let name_end = name_start + name.len();
let prefix_start = prefix.as_ptr() as usize;
assert!(
prefix_start >= name_start && prefix_start < name_end,
"prefix must borrow into input"
);
}
#[test]
fn strip_env_metadata_name_round_trips_through_figment_emission() {
use figment::Provider;
for prefix in ["MYAPP_", "TOBIRA_", "X_"] {
let env = figment::providers::Env::prefixed(prefix);
let md = env.metadata();
let name: &str = md.name.as_ref();
let tag = ConfigSource::strip_env_metadata_name(name)
.expect("figment Env metadata-name must match env-tag shape");
assert_eq!(tag, EnvMetadataTag::Prefixed(prefix));
}
}
#[test]
fn env_metadata_name_matches_figment_emission_byte_for_byte() {
use figment::Provider;
for prefix in ["MYAPP_", "TOBIRA_", "X_"] {
let figment_name = figment::providers::Env::prefixed(prefix)
.metadata()
.name
.into_owned();
let shikumi_name = ConfigSource::env_metadata_name(prefix);
assert_eq!(
figment_name, shikumi_name,
"shikumi's env_metadata_name must match figment's emission"
);
}
}
#[test]
fn strip_env_metadata_name_disjoint_from_format_strip() {
use crate::discovery::Format;
for f in Format::ALL.iter().filter(|f| f.has_shikumi_provider()) {
let name = f.metadata_name(Path::new("/etc/app.cfg"));
assert!(
ConfigSource::strip_env_metadata_name(&name).is_none(),
"format tag `{name}` must not be recognized as env tag"
);
}
for prefix in ["MYAPP_", ""] {
let name = ConfigSource::env_metadata_name(prefix);
assert!(
Format::strip_metadata_name(&name).is_none(),
"env tag `{name}` must not be recognized as format tag"
);
}
}
#[test]
fn figment_source_tag_classifies_file_path() {
let src = figment::Source::File(PathBuf::from("/etc/app/app.yaml"));
let tag = FigmentSourceTag::classify(&src).expect("File source must classify");
assert_eq!(tag, FigmentSourceTag::File(Path::new("/etc/app/app.yaml")));
assert_eq!(tag.as_file_path(), Some(Path::new("/etc/app/app.yaml")));
assert!(!tag.is_code());
assert_eq!(tag.as_custom(), None);
}
#[test]
fn figment_source_tag_classifies_code_location() {
use figment::Provider;
let provider = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let md = provider.metadata();
let src = md.source.as_ref().expect("Serialized attaches a source");
let tag = FigmentSourceTag::classify(src).expect("Code source must classify");
assert!(
matches!(tag, FigmentSourceTag::Code(_)),
"expected Code variant, got {tag:?}"
);
assert!(tag.is_code());
assert_eq!(tag.as_file_path(), None);
assert_eq!(tag.as_custom(), None);
}
#[test]
fn figment_source_tag_classifies_custom() {
let src = figment::Source::Custom("ftp://configs.example.com/app.yaml".to_owned());
let tag = FigmentSourceTag::classify(&src).expect("Custom source must classify");
assert_eq!(
tag,
FigmentSourceTag::Custom("ftp://configs.example.com/app.yaml")
);
assert_eq!(tag.as_custom(), Some("ftp://configs.example.com/app.yaml"));
assert!(!tag.is_code());
assert_eq!(tag.as_file_path(), None);
}
#[test]
fn figment_source_tag_borrows_into_input_for_file() {
let src = figment::Source::File(PathBuf::from("/etc/app/app.yaml"));
let FigmentSourceTag::File(borrowed) =
FigmentSourceTag::classify(&src).expect("File classify")
else {
panic!("expected File variant");
};
let figment::Source::File(ref owned) = src else {
unreachable!()
};
let owned_start = owned.as_os_str().as_encoded_bytes().as_ptr() as usize;
let owned_end = owned_start + owned.as_os_str().as_encoded_bytes().len();
let borrowed_start = borrowed.as_os_str().as_encoded_bytes().as_ptr() as usize;
assert!(
borrowed_start >= owned_start && borrowed_start < owned_end,
"path must borrow into source"
);
}
#[test]
fn figment_source_tag_borrows_into_input_for_custom() {
let src = figment::Source::Custom("vault://kv/app".to_owned());
let FigmentSourceTag::Custom(c) =
FigmentSourceTag::classify(&src).expect("Custom classify")
else {
panic!("expected Custom variant");
};
let figment::Source::Custom(ref s) = src else {
unreachable!()
};
let s_start = s.as_ptr() as usize;
let s_end = s_start + s.len();
let c_start = c.as_ptr() as usize;
assert!(
c_start >= s_start && c_start < s_end,
"custom slice must borrow into source"
);
}
#[test]
fn figment_source_tag_classify_round_trips_through_yaml_provider() {
use figment::providers::Format as _;
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("c.yaml");
std::fs::write(&file, "k: v\n").unwrap();
let figment = figment::Figment::new().merge(figment::providers::Yaml::file(&file));
let value: figment::value::Value = figment.find_value("k").unwrap();
let tag = figment.get_metadata(value.tag()).unwrap();
let src = tag.source.as_ref().expect("Yaml::file attaches a source");
let classified = FigmentSourceTag::classify(src).expect("Yaml file source must classify");
assert_eq!(classified, FigmentSourceTag::File(file.as_path()));
}
#[test]
fn figment_source_tag_classify_round_trips_through_serialized_provider() {
use figment::Provider;
let prov = figment::providers::Serialized::defaults(serde_json::json!({"name": "default"}));
let md = prov.metadata();
let src = md
.source
.as_ref()
.expect("Serialized attaches a Source::Code");
let tag = FigmentSourceTag::classify(src).expect("Code source must classify");
assert!(tag.is_code(), "Serialized must classify as Code");
}
#[test]
fn figment_source_tag_variants_are_disjoint() {
let file_src = figment::Source::File(PathBuf::from("/x"));
let custom_src = figment::Source::Custom("c".to_owned());
let file_tag = FigmentSourceTag::classify(&file_src).unwrap();
let custom_tag = FigmentSourceTag::classify(&custom_src).unwrap();
assert!(file_tag.as_file_path().is_some() && !file_tag.is_code());
assert!(file_tag.as_custom().is_none());
assert!(custom_tag.as_custom().is_some() && !custom_tag.is_code());
assert!(custom_tag.as_file_path().is_none());
}
#[test]
fn figment_name_tag_classifies_format_metadata_name() {
use crate::discovery::Format;
let path = Path::new("/etc/app/app.cfg");
for f in Format::ALL.iter().filter(|f| f.has_shikumi_provider()) {
let name = f.metadata_name(path);
let tag = FigmentNameTag::classify(&name).expect("format tag must classify");
let inner = tag.as_format().expect("expected Format variant");
assert_eq!(inner.format, *f);
assert_eq!(inner.path, path);
assert!(tag.as_env().is_none(), "Format must not also be Env");
}
}
#[test]
fn figment_name_tag_classifies_env_prefixed() {
let name = ConfigSource::env_metadata_name("MYAPP_");
let tag = FigmentNameTag::classify(&name).expect("env-prefixed must classify");
assert_eq!(tag, FigmentNameTag::Env(EnvMetadataTag::Prefixed("MYAPP_")));
assert_eq!(tag.as_env(), Some(EnvMetadataTag::Prefixed("MYAPP_")));
assert!(tag.as_format().is_none(), "Env must not also be Format");
}
#[test]
fn figment_name_tag_classifies_env_bare() {
let name = ConfigSource::env_metadata_name("");
let tag = FigmentNameTag::classify(&name).expect("env-bare must classify");
assert_eq!(tag, FigmentNameTag::Env(EnvMetadataTag::Bare));
assert_eq!(tag.as_env(), Some(EnvMetadataTag::Bare));
assert!(tag.as_format().is_none());
}
#[test]
fn figment_name_tag_returns_none_for_unrelated() {
for name in [
"",
"/etc/app/app.yaml", "/var/lib/app.toml",
"default", "yaml", "json: /x.cfg", "envvar `X_`", ] {
assert!(
FigmentNameTag::classify(name).is_none(),
"unrelated metadata name `{name}` must not classify"
);
}
}
#[test]
fn figment_name_tag_variants_are_disjoint() {
use crate::discovery::Format;
for f in Format::ALL.iter().filter(|f| f.has_shikumi_provider()) {
let name = f.metadata_name(Path::new("/etc/app.cfg"));
let tag = FigmentNameTag::classify(&name).expect("format tag classifies");
assert!(tag.as_format().is_some());
assert!(tag.as_env().is_none());
}
for prefix in ["MYAPP_", ""] {
let name = ConfigSource::env_metadata_name(prefix);
let tag = FigmentNameTag::classify(&name).expect("env tag classifies");
assert!(tag.as_env().is_some());
assert!(tag.as_format().is_none());
}
}
#[test]
fn figment_name_tag_format_borrows_into_input() {
use crate::discovery::Format;
let name = Format::Nix.metadata_name(Path::new("/etc/app/app.nix"));
let tag = FigmentNameTag::classify(&name).expect("classify");
let FigmentNameTag::Format(inner) = tag else {
panic!("expected Format variant");
};
let name_start = name.as_ptr() as usize;
let name_end = name_start + name.len();
let path_bytes = inner.path.as_os_str().as_encoded_bytes();
let path_start = path_bytes.as_ptr() as usize;
assert!(
path_start >= name_start && path_start < name_end,
"Format.path must borrow into input"
);
}
#[test]
fn figment_name_tag_env_borrows_into_input() {
let name = ConfigSource::env_metadata_name("BORROW_");
let tag = FigmentNameTag::classify(&name).expect("classify");
let FigmentNameTag::Env(EnvMetadataTag::Prefixed(prefix)) = tag else {
panic!("expected Env(Prefixed) variant");
};
let name_start = name.as_ptr() as usize;
let name_end = name_start + name.len();
let prefix_start = prefix.as_ptr() as usize;
assert!(
prefix_start >= name_start && prefix_start < name_end,
"Env(Prefixed) must borrow into input"
);
}
#[test]
fn figment_name_tag_round_trips_through_figment_env_emission() {
use figment::Provider;
for prefix in ["MYAPP_", "TOBIRA_", "X_"] {
let env = figment::providers::Env::prefixed(prefix);
let md = env.metadata();
let name: &str = md.name.as_ref();
let tag = FigmentNameTag::classify(name).expect("figment Env name must classify");
assert_eq!(tag, FigmentNameTag::Env(EnvMetadataTag::Prefixed(prefix)));
}
}
#[test]
fn figment_name_tag_round_trips_through_format_emission() {
use crate::discovery::Format;
for f in Format::ALL.iter().filter(|f| f.has_shikumi_provider()) {
let path = Path::new("/etc/app/app.cfg");
let name = f.metadata_name(path);
let tag = FigmentNameTag::classify(&name).expect("format-emitted name classifies");
let inner = tag.as_format().expect("Format variant");
assert_eq!(inner.format, *f);
assert_eq!(inner.path, path);
}
}
#[test]
fn figment_name_tag_is_copy_and_hashable() {
use std::collections::HashSet;
let n1 = ConfigSource::env_metadata_name("X_");
let t1 = FigmentNameTag::classify(&n1).unwrap();
let t2 = t1;
let t3 = t1;
assert_eq!(t1, t2);
assert_eq!(t2, t3);
let mut set = HashSet::new();
let np = ConfigSource::env_metadata_name("MYAPP_");
let nb = ConfigSource::env_metadata_name("");
let nf = crate::discovery::Format::Nix.metadata_name(Path::new("/a.nix"));
set.insert(FigmentNameTag::classify(&np).unwrap());
set.insert(FigmentNameTag::classify(&nb).unwrap());
set.insert(FigmentNameTag::classify(&nf).unwrap());
assert_eq!(set.len(), 3);
}
#[test]
fn figment_source_kind_classifies_each_variant() {
let file_src = figment::Source::File(PathBuf::from("/etc/app/app.yaml"));
let file_tag = FigmentSourceTag::classify(&file_src).unwrap();
assert_eq!(file_tag.kind(), FigmentSourceKind::File);
use figment::Provider;
let prov = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let md = prov.metadata();
let code_src = md.source.as_ref().unwrap();
let code_tag = FigmentSourceTag::classify(code_src).unwrap();
assert_eq!(code_tag.kind(), FigmentSourceKind::Code);
let custom_src = figment::Source::Custom("vault://kv/app".to_owned());
let custom_tag = FigmentSourceTag::classify(&custom_src).unwrap();
assert_eq!(custom_tag.kind(), FigmentSourceKind::Custom);
}
#[test]
fn figment_source_kind_is_data_free() {
for path in ["/a", "/very/long/path/to/cfg.yaml", "rel.toml"] {
let src = figment::Source::File(PathBuf::from(path));
assert_eq!(
FigmentSourceTag::classify(&src).unwrap().kind(),
FigmentSourceKind::File,
);
}
for s in ["", "a", "vault://kv/x", "ftp://configs"] {
let src = figment::Source::Custom(s.to_owned());
assert_eq!(
FigmentSourceTag::classify(&src).unwrap().kind(),
FigmentSourceKind::Custom,
);
}
}
#[test]
fn figment_source_kind_agrees_with_predicates_pointwise() {
use figment::Provider;
let file_src = figment::Source::File(PathBuf::from("/x"));
let custom_src = figment::Source::Custom("c".to_owned());
let prov = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let md = prov.metadata();
let code_src = md.source.as_ref().unwrap();
for tag in [
FigmentSourceTag::classify(&file_src).unwrap(),
FigmentSourceTag::classify(code_src).unwrap(),
FigmentSourceTag::classify(&custom_src).unwrap(),
] {
assert_eq!(tag.is_code(), tag.kind() == FigmentSourceKind::Code);
assert_eq!(
tag.as_file_path().is_some(),
tag.kind() == FigmentSourceKind::File,
);
assert_eq!(
tag.as_custom().is_some(),
tag.kind() == FigmentSourceKind::Custom,
);
assert_eq!(tag.kind().is_file(), tag.kind() == FigmentSourceKind::File);
assert_eq!(tag.kind().is_code(), tag.kind() == FigmentSourceKind::Code);
assert_eq!(
tag.kind().is_custom(),
tag.kind() == FigmentSourceKind::Custom,
);
}
}
#[test]
fn figment_source_kind_is_static_and_copy_and_hashable() {
use std::collections::HashSet;
let mut set: HashSet<FigmentSourceKind> = HashSet::new();
for k in FigmentSourceKind::ALL.iter().copied() {
set.insert(k);
}
set.insert(FigmentSourceKind::File); assert_eq!(set.len(), FigmentSourceKind::ALL.len());
let k = FigmentSourceKind::Code;
let k2 = k;
let k3 = k;
assert_eq!(k, k2);
assert_eq!(k2, k3);
fn assert_static<T: 'static>() {}
assert_static::<FigmentSourceKind>();
}
#[test]
fn figment_source_kind_partitions_disjointly() {
use figment::Provider;
let prov = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let md = prov.metadata();
let cases: [(FigmentSourceTag<'_>, FigmentSourceKind); 3] = [
(
FigmentSourceTag::File(Path::new("/x")),
FigmentSourceKind::File,
),
(
FigmentSourceTag::Code(
md.source
.as_ref()
.and_then(figment::Source::code_location)
.unwrap(),
),
FigmentSourceKind::Code,
),
(FigmentSourceTag::Custom("c"), FigmentSourceKind::Custom),
];
for (tag, expected) in cases {
assert_eq!(tag.kind(), expected);
}
assert_eq!(
FigmentSourceTag::File(Path::new("/a")).kind(),
FigmentSourceTag::File(Path::new("/b")).kind(),
);
}
#[test]
fn figment_source_tag_attribution_axis_is_always_metadata_source() {
use crate::AttributionAxis;
use figment::Provider;
let file_src = figment::Source::File(PathBuf::from("/etc/app.yaml"));
let custom_src = figment::Source::Custom("c".to_owned());
let prov = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let md = prov.metadata();
let code_src = md.source.as_ref().unwrap();
for tag in [
FigmentSourceTag::classify(&file_src).unwrap(),
FigmentSourceTag::classify(code_src).unwrap(),
FigmentSourceTag::classify(&custom_src).unwrap(),
] {
assert_eq!(tag.attribution_axis(), AttributionAxis::MetadataSource);
}
}
#[test]
fn figment_source_kind_round_trips_through_classify() {
let file = figment::Source::File(PathBuf::from("/x.yaml"));
let custom = figment::Source::Custom("y".to_owned());
assert_eq!(
FigmentSourceTag::classify(&file).unwrap().kind(),
FigmentSourceKind::File,
);
assert_eq!(
FigmentSourceTag::classify(&custom).unwrap().kind(),
FigmentSourceKind::Custom,
);
}
fn canonical_figment_source_kind_samples() -> Vec<(figment::Source, FigmentSourceKind)> {
use figment::Provider;
let prov = figment::providers::Serialized::defaults(serde_json::json!({"k": "v"}));
let code_src = prov.metadata().source.expect("Serialized attaches Source");
vec![
(
figment::Source::File(PathBuf::from("/etc/app/app.yaml")),
FigmentSourceKind::File,
),
(code_src, FigmentSourceKind::Code),
(
figment::Source::Custom("vault://kv/app".to_owned()),
FigmentSourceKind::Custom,
),
]
}
#[test]
fn figment_source_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<FigmentSourceKind> = FigmentSourceKind::ALL.iter().copied().collect();
assert_eq!(
set.len(),
FigmentSourceKind::ALL.len(),
"FigmentSourceKind::ALL must contain no duplicates; got: {:?}",
FigmentSourceKind::ALL,
);
}
#[test]
fn figment_source_kind_all_covers_every_constructible_tag() {
use std::collections::HashSet;
let declared: HashSet<FigmentSourceKind> = FigmentSourceKind::ALL.iter().copied().collect();
let observed: HashSet<FigmentSourceKind> = canonical_figment_source_kind_samples()
.iter()
.map(|(src, _)| FigmentSourceTag::classify(src).unwrap().kind())
.collect();
assert!(
observed.is_subset(&declared),
"FigmentSourceTag::kind image must lie in FigmentSourceKind::ALL; \
observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn figment_source_kind_all_equals_tag_kind_image() {
use std::collections::HashSet;
let declared: HashSet<FigmentSourceKind> = FigmentSourceKind::ALL.iter().copied().collect();
let observed: HashSet<FigmentSourceKind> = canonical_figment_source_kind_samples()
.iter()
.map(|(src, _)| FigmentSourceTag::classify(src).unwrap().kind())
.collect();
assert_eq!(
observed, declared,
"FigmentSourceTag::kind image must equal FigmentSourceKind::ALL",
);
}
#[test]
fn figment_source_kind_all_cardinality_matches_partition() {
use std::collections::HashSet;
let cells: HashSet<FigmentSourceKind> = canonical_figment_source_kind_samples()
.iter()
.map(|(src, _)| FigmentSourceTag::classify(src).unwrap().kind())
.collect();
assert_eq!(
FigmentSourceKind::ALL.len(),
cells.len(),
"FigmentSourceKind::ALL cardinality must match partition cell count",
);
}
#[test]
fn figment_source_kind_all_declaration_order_is_file_code_custom() {
assert_eq!(
FigmentSourceKind::ALL,
&[
FigmentSourceKind::File,
FigmentSourceKind::Code,
FigmentSourceKind::Custom,
],
);
}
#[test]
fn figment_source_kind_all_partition_is_file_xor_code_xor_custom() {
let samples = canonical_figment_source_kind_samples();
for kind in FigmentSourceKind::ALL.iter().copied() {
let witnessing_src = samples
.iter()
.find(|(src, _)| FigmentSourceTag::classify(src).unwrap().kind() == kind)
.map(|(src, _)| src)
.expect("every kind cell must be witnessed by some tag");
let tag = FigmentSourceTag::classify(witnessing_src).unwrap();
match kind {
FigmentSourceKind::File => {
assert!(tag.kind().is_file());
assert!(!tag.kind().is_code());
assert!(!tag.kind().is_custom());
}
FigmentSourceKind::Code => {
assert!(tag.kind().is_code());
assert!(!tag.kind().is_file());
assert!(!tag.kind().is_custom());
}
FigmentSourceKind::Custom => {
assert!(tag.kind().is_custom());
assert!(!tag.kind().is_file());
assert!(!tag.kind().is_code());
}
}
}
}
#[test]
fn figment_source_kind_all_iterates_in_declaration_order() {
let collected: Vec<FigmentSourceKind> = FigmentSourceKind::ALL.to_vec();
assert_eq!(
collected,
vec![
FigmentSourceKind::File,
FigmentSourceKind::Code,
FigmentSourceKind::Custom,
],
);
}
#[test]
fn figment_source_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(FigmentSourceKind::File.as_str(), "file");
assert_eq!(FigmentSourceKind::Code.as_str(), "code");
assert_eq!(FigmentSourceKind::Custom.as_str(), "custom");
}
#[test]
fn figment_source_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for k in FigmentSourceKind::ALL.iter().copied() {
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str(k.as_str()),
Some(k),
"trait from_canonical_str must round-trip for {k:?}",
);
}
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str("File"),
Some(FigmentSourceKind::File),
);
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str("CODE"),
Some(FigmentSourceKind::Code),
);
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str("Custom"),
Some(FigmentSourceKind::Custom),
);
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str("code "),
None,
);
assert_eq!(
<FigmentSourceKind as ClosedAxisLabel>::from_canonical_str("fil"),
None,
);
}
#[test]
fn figment_source_kind_all_attribution_axis_image_is_metadata_source() {
use crate::AttributionAxis;
use std::collections::HashSet;
let samples = canonical_figment_source_kind_samples();
let observed: HashSet<AttributionAxis> = FigmentSourceKind::ALL
.iter()
.copied()
.map(|kind| {
let (src, _) = samples
.iter()
.find(|(src, _)| FigmentSourceTag::classify(src).unwrap().kind() == kind)
.expect("every kind cell must be witnessed");
FigmentSourceTag::classify(src).unwrap().attribution_axis()
})
.collect();
assert_eq!(
observed,
HashSet::from([AttributionAxis::MetadataSource]),
"every FigmentSourceKind variant projects to AttributionAxis::MetadataSource",
);
}
#[test]
fn figment_source_kind_ord_matches_all_declaration_order() {
use std::cmp::Ordering;
for window in FigmentSourceKind::ALL.windows(2) {
assert!(
window[0] < window[1],
"FigmentSourceKind::ALL must be strictly increasing under Ord, \
but {:?} >= {:?}",
window[0],
window[1],
);
}
for (i, &a) in FigmentSourceKind::ALL.iter().enumerate() {
for (j, &b) in FigmentSourceKind::ALL.iter().enumerate() {
let expected = i.cmp(&j);
assert_eq!(
a.cmp(&b),
expected,
"FigmentSourceKind::cmp must match ALL-index lex for ({a:?}, {b:?})",
);
assert_eq!(
a.partial_cmp(&b),
Some(expected),
"FigmentSourceKind::partial_cmp must agree with cmp for ({a:?}, {b:?})",
);
if i == j {
assert_eq!(a.cmp(&b), Ordering::Equal, "Ord must be reflexive on {a:?}",);
}
}
}
}
#[test]
fn figment_source_kind_btreemap_emits_in_declaration_order() {
use std::collections::BTreeMap;
let mut counts: BTreeMap<FigmentSourceKind, u32> = BTreeMap::new();
counts.insert(FigmentSourceKind::Custom, 3);
counts.insert(FigmentSourceKind::File, 1);
counts.insert(FigmentSourceKind::Code, 2);
let observed: Vec<FigmentSourceKind> = counts.keys().copied().collect();
assert_eq!(
observed,
FigmentSourceKind::ALL.to_vec(),
"BTreeMap<FigmentSourceKind, _> must emit keys in ALL declaration order",
);
}
#[test]
fn figment_source_kind_display_matches_as_str() {
for k in FigmentSourceKind::ALL.iter().copied() {
assert_eq!(
format!("{k}"),
k.as_str(),
"Display must agree with as_str for {k:?}",
);
}
}
#[test]
fn figment_source_kind_from_str_round_trips_over_every_variant() {
for k in FigmentSourceKind::ALL {
let rendered = k.to_string();
let parsed: FigmentSourceKind = rendered
.parse()
.expect("FromStr must round-trip Display output");
assert_eq!(parsed, *k, "FromStr must round-trip {k:?}");
}
}
#[test]
fn figment_source_kind_from_str_is_case_insensitive() {
assert_eq!(
"FILE".parse::<FigmentSourceKind>().unwrap(),
FigmentSourceKind::File,
);
assert_eq!(
"Code".parse::<FigmentSourceKind>().unwrap(),
FigmentSourceKind::Code,
);
assert_eq!(
"CuStOm".parse::<FigmentSourceKind>().unwrap(),
FigmentSourceKind::Custom,
);
}
#[test]
fn figment_source_kind_from_str_unknown_kind_error_carries_label_verbatim() {
for bad in &["files", "raw", "url", "", " code"] {
let err = bad
.parse::<FigmentSourceKind>()
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn figment_source_kind_serde_yaml_round_trips_over_every_variant() {
for k in FigmentSourceKind::ALL {
let yaml = serde_yaml::to_string(k).expect("Serialize must succeed");
let parsed: FigmentSourceKind =
serde_yaml::from_str(&yaml).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_yaml round-trip must preserve {k:?}");
}
}
#[test]
fn figment_source_kind_serde_json_round_trips_over_every_variant() {
for k in FigmentSourceKind::ALL {
let json = serde_json::to_string(k).expect("Serialize must succeed");
let parsed: FigmentSourceKind =
serde_json::from_str(&json).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_json round-trip must preserve {k:?}");
}
}
#[test]
fn figment_source_kind_serde_yaml_is_case_insensitive() {
let cases: &[(&str, FigmentSourceKind)] = &[
("File", FigmentSourceKind::File),
("CODE", FigmentSourceKind::Code),
("Custom", FigmentSourceKind::Custom),
];
for (input, expected) in cases {
let parsed: FigmentSourceKind =
serde_yaml::from_str(input).expect("case-insensitive Deserialize must succeed");
assert_eq!(
parsed, *expected,
"serde_yaml must parse case-insensitively for input {input:?}",
);
}
}
#[test]
fn figment_source_kind_serde_yaml_unknown_kind_error_carries_label_verbatim() {
for bad in &["files", "raw", "url", "configmap"] {
let err = serde_yaml::from_str::<FigmentSourceKind>(bad)
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered serde error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn figment_name_tag_kind_ord_matches_all_declaration_order() {
use std::cmp::Ordering;
for window in FigmentNameTagKind::ALL.windows(2) {
assert!(
window[0] < window[1],
"FigmentNameTagKind::ALL must be strictly increasing under Ord, \
but {:?} >= {:?}",
window[0],
window[1],
);
}
for (i, &a) in FigmentNameTagKind::ALL.iter().enumerate() {
for (j, &b) in FigmentNameTagKind::ALL.iter().enumerate() {
let expected = i.cmp(&j);
assert_eq!(
a.cmp(&b),
expected,
"FigmentNameTagKind::cmp must match ALL-index lex for ({a:?}, {b:?})",
);
assert_eq!(
a.partial_cmp(&b),
Some(expected),
"FigmentNameTagKind::partial_cmp must agree with cmp for ({a:?}, {b:?})",
);
if i == j {
assert_eq!(a.cmp(&b), Ordering::Equal, "Ord must be reflexive on {a:?}",);
}
}
}
}
#[test]
fn figment_name_tag_kind_btreemap_emits_in_declaration_order() {
use std::collections::BTreeMap;
let mut counts: BTreeMap<FigmentNameTagKind, u32> = BTreeMap::new();
counts.insert(FigmentNameTagKind::Env, 2);
counts.insert(FigmentNameTagKind::Format, 1);
let observed: Vec<FigmentNameTagKind> = counts.keys().copied().collect();
assert_eq!(
observed,
FigmentNameTagKind::ALL.to_vec(),
"BTreeMap<FigmentNameTagKind, _> must emit keys in ALL declaration order",
);
}
#[test]
fn figment_name_tag_kind_display_matches_as_str() {
for k in FigmentNameTagKind::ALL.iter().copied() {
assert_eq!(
format!("{k}"),
k.as_str(),
"Display must agree with as_str for {k:?}",
);
}
}
#[test]
fn figment_name_tag_kind_from_str_round_trips_over_every_variant() {
for k in FigmentNameTagKind::ALL {
let rendered = k.to_string();
let parsed: FigmentNameTagKind = rendered
.parse()
.expect("FromStr must round-trip Display output");
assert_eq!(parsed, *k, "FromStr must round-trip {k:?}");
}
}
#[test]
fn figment_name_tag_kind_from_str_is_case_insensitive() {
assert_eq!(
"FORMAT".parse::<FigmentNameTagKind>().unwrap(),
FigmentNameTagKind::Format,
);
assert_eq!(
"Env".parse::<FigmentNameTagKind>().unwrap(),
FigmentNameTagKind::Env,
);
assert_eq!(
"FoRmAt".parse::<FigmentNameTagKind>().unwrap(),
FigmentNameTagKind::Format,
);
}
#[test]
fn figment_name_tag_kind_from_str_unknown_kind_error_carries_label_verbatim() {
for bad in &["formats", "envs", "raw", "", " env"] {
let err = bad
.parse::<FigmentNameTagKind>()
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn figment_name_tag_kind_serde_yaml_round_trips_over_every_variant() {
for k in FigmentNameTagKind::ALL {
let yaml = serde_yaml::to_string(k).expect("Serialize must succeed");
let parsed: FigmentNameTagKind =
serde_yaml::from_str(&yaml).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_yaml round-trip must preserve {k:?}");
}
}
#[test]
fn figment_name_tag_kind_serde_json_round_trips_over_every_variant() {
for k in FigmentNameTagKind::ALL {
let json = serde_json::to_string(k).expect("Serialize must succeed");
let parsed: FigmentNameTagKind =
serde_json::from_str(&json).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_json round-trip must preserve {k:?}");
}
}
#[test]
fn figment_name_tag_kind_serde_yaml_is_case_insensitive() {
let cases: &[(&str, FigmentNameTagKind)] = &[
("Format", FigmentNameTagKind::Format),
("ENV", FigmentNameTagKind::Env),
("FoRmAt", FigmentNameTagKind::Format),
];
for (input, expected) in cases {
let parsed: FigmentNameTagKind =
serde_yaml::from_str(input).expect("case-insensitive Deserialize must succeed");
assert_eq!(
parsed, *expected,
"serde_yaml must parse case-insensitively for input {input:?}",
);
}
}
#[test]
fn figment_name_tag_kind_serde_yaml_unknown_kind_error_carries_label_verbatim() {
for bad in &["formats", "envs", "raw", "configmap"] {
let err = serde_yaml::from_str::<FigmentNameTagKind>(bad)
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered serde error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn env_metadata_tag_kind_ord_matches_all_declaration_order() {
use std::cmp::Ordering;
for window in EnvMetadataTagKind::ALL.windows(2) {
assert!(
window[0] < window[1],
"EnvMetadataTagKind::ALL must be strictly increasing under Ord, \
but {:?} >= {:?}",
window[0],
window[1],
);
}
for (i, &a) in EnvMetadataTagKind::ALL.iter().enumerate() {
for (j, &b) in EnvMetadataTagKind::ALL.iter().enumerate() {
let expected = i.cmp(&j);
assert_eq!(
a.cmp(&b),
expected,
"EnvMetadataTagKind::cmp must match ALL-index lex for ({a:?}, {b:?})",
);
assert_eq!(
a.partial_cmp(&b),
Some(expected),
"EnvMetadataTagKind::partial_cmp must agree with cmp for ({a:?}, {b:?})",
);
if i == j {
assert_eq!(a.cmp(&b), Ordering::Equal, "Ord must be reflexive on {a:?}",);
}
}
}
}
#[test]
fn env_metadata_tag_kind_btreemap_emits_in_declaration_order() {
use std::collections::BTreeMap;
let mut counts: BTreeMap<EnvMetadataTagKind, u32> = BTreeMap::new();
counts.insert(EnvMetadataTagKind::Bare, 2);
counts.insert(EnvMetadataTagKind::Prefixed, 1);
let observed: Vec<EnvMetadataTagKind> = counts.keys().copied().collect();
assert_eq!(
observed,
EnvMetadataTagKind::ALL.to_vec(),
"BTreeMap<EnvMetadataTagKind, _> must emit keys in ALL declaration order",
);
}
#[test]
fn env_metadata_tag_kind_display_matches_as_str() {
for k in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
format!("{k}"),
k.as_str(),
"Display must agree with as_str for {k:?}",
);
}
}
#[test]
fn env_metadata_tag_kind_from_str_round_trips_over_every_variant() {
for k in EnvMetadataTagKind::ALL {
let rendered = k.to_string();
let parsed: EnvMetadataTagKind = rendered
.parse()
.expect("FromStr must round-trip Display output");
assert_eq!(parsed, *k, "FromStr must round-trip {k:?}");
}
}
#[test]
fn env_metadata_tag_kind_from_str_is_case_insensitive() {
assert_eq!(
"PREFIXED".parse::<EnvMetadataTagKind>().unwrap(),
EnvMetadataTagKind::Prefixed,
);
assert_eq!(
"Bare".parse::<EnvMetadataTagKind>().unwrap(),
EnvMetadataTagKind::Bare,
);
assert_eq!(
"PrEfIxEd".parse::<EnvMetadataTagKind>().unwrap(),
EnvMetadataTagKind::Prefixed,
);
assert_eq!(
"bArE".parse::<EnvMetadataTagKind>().unwrap(),
EnvMetadataTagKind::Bare,
);
}
#[test]
fn env_metadata_tag_kind_from_str_unknown_kind_error_carries_label_verbatim() {
for bad in &["pref", "raw", "naked", "", " bare"] {
let err = bad
.parse::<EnvMetadataTagKind>()
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn env_metadata_tag_kind_serde_yaml_round_trips_over_every_variant() {
for k in EnvMetadataTagKind::ALL {
let yaml = serde_yaml::to_string(k).expect("Serialize must succeed");
let parsed: EnvMetadataTagKind =
serde_yaml::from_str(&yaml).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_yaml round-trip must preserve {k:?}");
}
}
#[test]
fn env_metadata_tag_kind_serde_json_round_trips_over_every_variant() {
for k in EnvMetadataTagKind::ALL {
let json = serde_json::to_string(k).expect("Serialize must succeed");
let parsed: EnvMetadataTagKind =
serde_json::from_str(&json).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_json round-trip must preserve {k:?}");
}
}
#[test]
fn env_metadata_tag_kind_serde_yaml_is_case_insensitive() {
let cases: &[(&str, EnvMetadataTagKind)] = &[
("Prefixed", EnvMetadataTagKind::Prefixed),
("BARE", EnvMetadataTagKind::Bare),
("PrEfIxEd", EnvMetadataTagKind::Prefixed),
("bArE", EnvMetadataTagKind::Bare),
];
for (input, expected) in cases {
let parsed: EnvMetadataTagKind =
serde_yaml::from_str(input).expect("case-insensitive Deserialize must succeed");
assert_eq!(
parsed, *expected,
"serde_yaml must parse case-insensitively for input {input:?}",
);
}
}
#[test]
fn env_metadata_tag_kind_serde_yaml_unknown_kind_error_carries_label_verbatim() {
for bad in &["pref", "raw", "naked", "envvar"] {
let err = serde_yaml::from_str::<EnvMetadataTagKind>(bad)
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered serde error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn env_metadata_tag_kind_serde_yaml_emission_is_bare_scalar() {
assert_eq!(
serde_yaml::to_string(&EnvMetadataTagKind::Prefixed).unwrap(),
"prefixed\n",
);
assert_eq!(
serde_yaml::to_string(&EnvMetadataTagKind::Bare).unwrap(),
"bare\n",
);
}
#[test]
fn figment_name_tag_yaml_provider_emission_is_unrecognized() {
use figment::providers::Format as _;
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("c.yaml");
std::fs::write(&file, "k: v\n").unwrap();
let figment = figment::Figment::new().merge(figment::providers::Yaml::file(&file));
let value: figment::value::Value = figment.find_value("k").unwrap();
let md = figment.get_metadata(value.tag()).unwrap();
let name: &str = md.name.as_ref();
assert!(
FigmentNameTag::classify(name).is_none(),
"Yaml provider's path-shaped name `{name}` must NOT classify as a name-axis tag"
);
}
fn canonical_figment_name_tag_kind_samples() -> Vec<(String, FigmentNameTagKind)> {
vec![
(
crate::discovery::Format::Lisp.metadata_name(Path::new("/etc/app/app.lisp")),
FigmentNameTagKind::Format,
),
(
crate::discovery::Format::Nix.metadata_name(Path::new("/etc/app/app.nix")),
FigmentNameTagKind::Format,
),
(
ConfigSource::env_metadata_name("MYAPP_"),
FigmentNameTagKind::Env,
),
(ConfigSource::env_metadata_name(""), FigmentNameTagKind::Env),
]
}
#[test]
fn figment_name_tag_kind_classifies_each_variant() {
let lisp_name = crate::discovery::Format::Lisp.metadata_name(Path::new("/a.lisp"));
let lisp_tag = FigmentNameTag::classify(&lisp_name).unwrap();
assert_eq!(lisp_tag.kind(), FigmentNameTagKind::Format);
let nix_name = crate::discovery::Format::Nix.metadata_name(Path::new("/a.nix"));
let nix_tag = FigmentNameTag::classify(&nix_name).unwrap();
assert_eq!(nix_tag.kind(), FigmentNameTagKind::Format);
let prefixed = ConfigSource::env_metadata_name("APP_");
let prefixed_tag = FigmentNameTag::classify(&prefixed).unwrap();
assert_eq!(prefixed_tag.kind(), FigmentNameTagKind::Env);
let bare = ConfigSource::env_metadata_name("");
let bare_tag = FigmentNameTag::classify(&bare).unwrap();
assert_eq!(bare_tag.kind(), FigmentNameTagKind::Env);
}
#[test]
fn figment_name_tag_kind_is_data_free() {
for (format, path) in [
(crate::discovery::Format::Lisp, "/a.lisp"),
(crate::discovery::Format::Lisp, "/very/long/path/to/x.lisp"),
(crate::discovery::Format::Nix, "rel.nix"),
] {
let name = format.metadata_name(Path::new(path));
let tag = FigmentNameTag::classify(&name).unwrap();
assert_eq!(tag.kind(), FigmentNameTagKind::Format);
}
for prefix in ["MYAPP_", "TOBIRA_", "X_", ""] {
let name = ConfigSource::env_metadata_name(prefix);
let tag = FigmentNameTag::classify(&name).unwrap();
assert_eq!(tag.kind(), FigmentNameTagKind::Env);
}
}
#[test]
fn figment_name_tag_kind_agrees_with_as_predicates_pointwise() {
for (name, _) in canonical_figment_name_tag_kind_samples() {
let tag = FigmentNameTag::classify(&name).unwrap();
assert_eq!(
tag.as_format().is_some(),
tag.kind() == FigmentNameTagKind::Format,
);
assert_eq!(
tag.as_env().is_some(),
tag.kind() == FigmentNameTagKind::Env,
);
assert_eq!(
tag.kind().is_format(),
tag.kind() == FigmentNameTagKind::Format,
);
assert_eq!(tag.kind().is_env(), tag.kind() == FigmentNameTagKind::Env);
}
}
#[test]
fn figment_name_tag_attribution_axis_is_always_metadata_name() {
use crate::AttributionAxis;
for (name, _) in canonical_figment_name_tag_kind_samples() {
let tag = FigmentNameTag::classify(&name).unwrap();
assert_eq!(tag.attribution_axis(), AttributionAxis::MetadataName);
}
}
#[test]
fn figment_name_tag_kind_is_static_and_copy_and_hashable() {
fn assert_static<T: 'static>() {}
use std::collections::HashSet;
let mut set: HashSet<FigmentNameTagKind> =
FigmentNameTagKind::ALL.iter().copied().collect();
set.insert(FigmentNameTagKind::Format); assert_eq!(set.len(), FigmentNameTagKind::ALL.len());
let k = FigmentNameTagKind::Env;
let k2 = k;
let k3 = k;
assert_eq!(k, k2);
assert_eq!(k2, k3);
assert_static::<FigmentNameTagKind>();
}
#[test]
fn figment_name_tag_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<FigmentNameTagKind> = FigmentNameTagKind::ALL.iter().copied().collect();
assert_eq!(
set.len(),
FigmentNameTagKind::ALL.len(),
"FigmentNameTagKind::ALL must contain no duplicates; got: {:?}",
FigmentNameTagKind::ALL,
);
}
#[test]
fn figment_name_tag_kind_all_covers_every_constructible_tag() {
use std::collections::HashSet;
let declared: HashSet<FigmentNameTagKind> =
FigmentNameTagKind::ALL.iter().copied().collect();
let observed: HashSet<FigmentNameTagKind> = canonical_figment_name_tag_kind_samples()
.iter()
.map(|(name, _)| FigmentNameTag::classify(name).unwrap().kind())
.collect();
assert!(
observed.is_subset(&declared),
"FigmentNameTag::kind image must lie in FigmentNameTagKind::ALL; \
observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn figment_name_tag_kind_all_equals_tag_kind_image() {
use std::collections::HashSet;
let declared: HashSet<FigmentNameTagKind> =
FigmentNameTagKind::ALL.iter().copied().collect();
let observed: HashSet<FigmentNameTagKind> = canonical_figment_name_tag_kind_samples()
.iter()
.map(|(name, _)| FigmentNameTag::classify(name).unwrap().kind())
.collect();
assert_eq!(
observed, declared,
"FigmentNameTag::kind image must equal FigmentNameTagKind::ALL",
);
}
#[test]
fn figment_name_tag_kind_all_declaration_order_is_format_env() {
assert_eq!(
FigmentNameTagKind::ALL,
&[FigmentNameTagKind::Format, FigmentNameTagKind::Env],
);
}
#[test]
fn figment_name_tag_kind_all_partition_is_format_xor_env() {
let samples = canonical_figment_name_tag_kind_samples();
for kind in FigmentNameTagKind::ALL.iter().copied() {
let witnessing_name = samples
.iter()
.find(|(name, _)| FigmentNameTag::classify(name).unwrap().kind() == kind)
.map(|(name, _)| name)
.expect("every kind cell must be witnessed by some tag");
let tag = FigmentNameTag::classify(witnessing_name).unwrap();
match kind {
FigmentNameTagKind::Format => {
assert!(tag.kind().is_format());
assert!(!tag.kind().is_env());
}
FigmentNameTagKind::Env => {
assert!(tag.kind().is_env());
assert!(!tag.kind().is_format());
}
}
}
}
#[test]
fn figment_name_tag_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(FigmentNameTagKind::Format.as_str(), "format");
assert_eq!(FigmentNameTagKind::Env.as_str(), "env");
}
#[test]
fn figment_name_tag_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for k in FigmentNameTagKind::ALL.iter().copied() {
assert_eq!(
<FigmentNameTagKind as ClosedAxisLabel>::from_canonical_str(k.as_str()),
Some(k),
"trait from_canonical_str must round-trip for {k:?}",
);
}
assert_eq!(
<FigmentNameTagKind as ClosedAxisLabel>::from_canonical_str("Format"),
Some(FigmentNameTagKind::Format),
);
assert_eq!(
<FigmentNameTagKind as ClosedAxisLabel>::from_canonical_str("ENV"),
Some(FigmentNameTagKind::Env),
);
assert_eq!(
<FigmentNameTagKind as ClosedAxisLabel>::from_canonical_str("env "),
None,
);
assert_eq!(
<FigmentNameTagKind as ClosedAxisLabel>::from_canonical_str("forma"),
None,
);
}
#[test]
fn figment_name_tag_kind_all_attribution_axis_image_is_metadata_name() {
use crate::AttributionAxis;
use std::collections::HashSet;
let samples = canonical_figment_name_tag_kind_samples();
let observed: HashSet<AttributionAxis> = FigmentNameTagKind::ALL
.iter()
.copied()
.map(|kind| {
let (name, _) = samples
.iter()
.find(|(name, _)| FigmentNameTag::classify(name).unwrap().kind() == kind)
.expect("every kind cell must be witnessed");
FigmentNameTag::classify(name).unwrap().attribution_axis()
})
.collect();
assert_eq!(
observed,
HashSet::from([AttributionAxis::MetadataName]),
"every FigmentNameTagKind variant projects to AttributionAxis::MetadataName",
);
}
#[test]
fn figment_name_tag_kind_round_trips_through_figment_env_emission() {
use figment::Provider;
for prefix in ["MYAPP_", "TOBIRA_", "X_"] {
let env = figment::providers::Env::prefixed(prefix);
let md = env.metadata();
let name: &str = md.name.as_ref();
let tag = FigmentNameTag::classify(name).expect("figment Env name must classify");
assert_eq!(tag.kind(), FigmentNameTagKind::Env);
}
}
#[test]
fn figment_name_tag_kind_round_trips_through_format_emission() {
use crate::discovery::Format;
for f in Format::ALL.iter().filter(|f| f.has_shikumi_provider()) {
let name = f.metadata_name(Path::new("/etc/app/app.cfg"));
let tag = FigmentNameTag::classify(&name).expect("format-emitted name classifies");
assert_eq!(tag.kind(), FigmentNameTagKind::Format);
}
}
fn canonical_env_metadata_tag_kind_samples() -> Vec<(String, EnvMetadataTagKind)> {
vec![
(
ConfigSource::env_metadata_name("MYAPP_"),
EnvMetadataTagKind::Prefixed,
),
(
ConfigSource::env_metadata_name("TOBIRA_"),
EnvMetadataTagKind::Prefixed,
),
(
ConfigSource::env_metadata_name(""),
EnvMetadataTagKind::Bare,
),
]
}
#[test]
fn env_metadata_tag_kind_classifies_each_variant() {
let prefixed_name = ConfigSource::env_metadata_name("APP_");
let prefixed = ConfigSource::strip_env_metadata_name(&prefixed_name)
.expect("prefixed env metadata classifies");
assert_eq!(prefixed.kind(), EnvMetadataTagKind::Prefixed);
let bare_name = ConfigSource::env_metadata_name("");
let bare = ConfigSource::strip_env_metadata_name(&bare_name)
.expect("bare env metadata classifies");
assert_eq!(bare.kind(), EnvMetadataTagKind::Bare);
}
#[test]
fn env_metadata_tag_kind_is_data_free() {
for prefix in ["MYAPP_", "TOBIRA_", "X_", "VERY_LONG_PREFIX_"] {
let tag = EnvMetadataTag::Prefixed(prefix);
assert_eq!(tag.kind(), EnvMetadataTagKind::Prefixed);
}
assert_eq!(EnvMetadataTag::Bare.kind(), EnvMetadataTagKind::Bare);
}
#[test]
fn env_metadata_tag_kind_agrees_with_predicates_pointwise() {
for (name, _) in canonical_env_metadata_tag_kind_samples() {
let tag = ConfigSource::strip_env_metadata_name(&name)
.expect("canonical sample must classify as env metadata");
assert_eq!(
tag.kind().is_prefixed(),
tag.kind() == EnvMetadataTagKind::Prefixed,
);
assert_eq!(tag.kind().is_bare(), tag.kind() == EnvMetadataTagKind::Bare,);
}
}
#[test]
fn env_metadata_tag_kind_is_static_and_copy_and_hashable() {
fn assert_static<T: 'static>() {}
use std::collections::HashSet;
let mut set: HashSet<EnvMetadataTagKind> =
EnvMetadataTagKind::ALL.iter().copied().collect();
set.insert(EnvMetadataTagKind::Prefixed); assert_eq!(set.len(), EnvMetadataTagKind::ALL.len());
let k = EnvMetadataTagKind::Bare;
let k2 = k;
let k3 = k;
assert_eq!(k, k2);
assert_eq!(k2, k3);
assert_static::<EnvMetadataTagKind>();
}
#[test]
fn env_metadata_tag_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<EnvMetadataTagKind> = EnvMetadataTagKind::ALL.iter().copied().collect();
assert_eq!(
set.len(),
EnvMetadataTagKind::ALL.len(),
"EnvMetadataTagKind::ALL must contain no duplicates; got: {:?}",
EnvMetadataTagKind::ALL,
);
}
#[test]
fn env_metadata_tag_kind_all_covers_every_constructible_tag() {
use std::collections::HashSet;
let declared: HashSet<EnvMetadataTagKind> =
EnvMetadataTagKind::ALL.iter().copied().collect();
let observed: HashSet<EnvMetadataTagKind> = canonical_env_metadata_tag_kind_samples()
.iter()
.map(|(name, _)| {
ConfigSource::strip_env_metadata_name(name)
.expect("canonical sample must classify as env metadata")
.kind()
})
.collect();
assert!(
observed.is_subset(&declared),
"EnvMetadataTag::kind image must lie in EnvMetadataTagKind::ALL; \
observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn env_metadata_tag_kind_all_equals_tag_kind_image() {
use std::collections::HashSet;
let declared: HashSet<EnvMetadataTagKind> =
EnvMetadataTagKind::ALL.iter().copied().collect();
let observed: HashSet<EnvMetadataTagKind> = canonical_env_metadata_tag_kind_samples()
.iter()
.map(|(name, _)| {
ConfigSource::strip_env_metadata_name(name)
.expect("canonical sample must classify as env metadata")
.kind()
})
.collect();
assert_eq!(
observed, declared,
"EnvMetadataTag::kind image must equal EnvMetadataTagKind::ALL",
);
}
#[test]
fn env_metadata_tag_kind_all_declaration_order_is_prefixed_bare() {
assert_eq!(
EnvMetadataTagKind::ALL,
&[EnvMetadataTagKind::Prefixed, EnvMetadataTagKind::Bare],
);
}
#[test]
fn env_metadata_tag_kind_all_partition_is_prefixed_xor_bare() {
let samples = canonical_env_metadata_tag_kind_samples();
for kind in EnvMetadataTagKind::ALL.iter().copied() {
let witnessing_name = samples
.iter()
.find(|(name, _)| {
ConfigSource::strip_env_metadata_name(name)
.expect("sample must classify")
.kind()
== kind
})
.map(|(name, _)| name)
.expect("every kind cell must be witnessed by some tag");
let tag = ConfigSource::strip_env_metadata_name(witnessing_name)
.expect("witness must classify");
match kind {
EnvMetadataTagKind::Prefixed => {
assert!(tag.kind().is_prefixed());
assert!(!tag.kind().is_bare());
}
EnvMetadataTagKind::Bare => {
assert!(tag.kind().is_bare());
assert!(!tag.kind().is_prefixed());
}
}
}
}
#[test]
fn env_metadata_tag_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(EnvMetadataTagKind::Prefixed.as_str(), "prefixed");
assert_eq!(EnvMetadataTagKind::Bare.as_str(), "bare");
}
#[test]
fn env_metadata_tag_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for k in EnvMetadataTagKind::ALL.iter().copied() {
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str(k.as_str()),
Some(k),
"trait from_canonical_str must round-trip for {k:?}",
);
}
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str("Prefixed"),
Some(EnvMetadataTagKind::Prefixed),
);
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str("BARE"),
Some(EnvMetadataTagKind::Bare),
);
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str("bare "),
None,
);
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str("prefixe"),
None,
);
assert_eq!(
<EnvMetadataTagKind as ClosedAxisLabel>::from_canonical_str(""),
None,
);
}
#[test]
fn env_metadata_tag_kind_pairs_with_figment_name_tag_kind_env() {
for (name, expected_env_kind) in canonical_env_metadata_tag_kind_samples() {
let outer = FigmentNameTag::classify(&name)
.expect("canonical env metadata classifies via FigmentNameTag");
assert_eq!(outer.kind(), FigmentNameTagKind::Env);
let inner = outer
.as_env()
.expect("Env variant must expose inner EnvMetadataTag");
assert_eq!(inner.kind(), expected_env_kind);
}
}
#[test]
fn env_metadata_tag_kind_round_trips_through_figment_env_emission() {
use figment::Provider;
for prefix in ["MYAPP_", "TOBIRA_", "X_"] {
let env = figment::providers::Env::prefixed(prefix);
let md = env.metadata();
let name: &str = md.name.as_ref();
let tag = ConfigSource::strip_env_metadata_name(name)
.expect("figment prefixed Env name must classify as env metadata");
assert_eq!(tag.kind(), EnvMetadataTagKind::Prefixed);
}
}
}