use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Provenance {
#[default]
Derived,
Authored,
Inferred,
ExternalDerived,
ExternalAuthored,
ExternalInferred,
}
impl Provenance {
#[must_use]
pub fn from_token(s: &str) -> Option<Self> {
match s {
"derived" => Some(Self::Derived),
"authored" => Some(Self::Authored),
"inferred" => Some(Self::Inferred),
"external-derived" => Some(Self::ExternalDerived),
"external-authored" => Some(Self::ExternalAuthored),
"external-inferred" => Some(Self::ExternalInferred),
_ => None,
}
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Derived => "derived",
Self::Authored => "authored",
Self::Inferred => "inferred",
Self::ExternalDerived => "external-derived",
Self::ExternalAuthored => "external-authored",
Self::ExternalInferred => "external-inferred",
}
}
#[must_use]
pub fn tokens() -> &'static [&'static str] {
&[
"derived",
"authored",
"inferred",
"external-derived",
"external-authored",
"external-inferred",
]
}
#[must_use]
pub fn is_external(self) -> bool {
matches!(
self,
Self::ExternalDerived | Self::ExternalAuthored | Self::ExternalInferred
)
}
#[must_use]
pub fn tier(self) -> Self {
match self {
Self::Derived | Self::ExternalDerived => Self::Derived,
Self::Authored | Self::ExternalAuthored => Self::Authored,
Self::Inferred | Self::ExternalInferred => Self::Inferred,
}
}
#[must_use]
pub fn externalise(self) -> Self {
match self {
Self::Derived | Self::ExternalDerived => Self::ExternalDerived,
Self::Authored | Self::ExternalAuthored => Self::ExternalAuthored,
Self::Inferred | Self::ExternalInferred => Self::ExternalInferred,
}
}
}
#[cfg(test)]
mod tests {
use super::Provenance;
const ALL: [Provenance; 6] = [
Provenance::Derived,
Provenance::Authored,
Provenance::Inferred,
Provenance::ExternalDerived,
Provenance::ExternalAuthored,
Provenance::ExternalInferred,
];
#[test]
fn stable_string_forms() {
assert_eq!(Provenance::Derived.as_str(), "derived");
assert_eq!(Provenance::Authored.as_str(), "authored");
assert_eq!(Provenance::Inferred.as_str(), "inferred");
assert_eq!(Provenance::ExternalDerived.as_str(), "external-derived");
assert_eq!(Provenance::ExternalAuthored.as_str(), "external-authored");
assert_eq!(Provenance::ExternalInferred.as_str(), "external-inferred");
}
#[test]
fn from_token_round_trips_and_rejects_unknown() {
for p in ALL {
assert_eq!(Provenance::from_token(p.as_str()), Some(p));
}
assert_eq!(Provenance::from_token("bogus"), None);
assert_eq!(Provenance::from_token("external"), None);
assert_eq!(Provenance::from_token("external_authored"), None);
assert_eq!(Provenance::from_token("External-Authored"), None);
}
#[test]
fn the_token_list_is_the_accepted_set() {
let from_enum: Vec<&str> = ALL.iter().map(|p| p.as_str()).collect();
assert_eq!(Provenance::tokens(), from_enum.as_slice());
for token in Provenance::tokens() {
assert!(
Provenance::from_token(token).is_some(),
"`{token}` is listed but not parseable"
);
}
}
#[test]
fn serde_and_the_store_token_agree() {
for p in ALL {
let json = serde_json::to_string(&p).expect("serialize");
assert_eq!(json, format!("\"{}\"", p.as_str()));
let back: Provenance = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back, p);
}
}
#[test]
fn externalise_carries_the_tier_and_flattens() {
assert_eq!(
Provenance::Derived.externalise(),
Provenance::ExternalDerived
);
assert_eq!(
Provenance::Authored.externalise(),
Provenance::ExternalAuthored
);
assert_eq!(
Provenance::Inferred.externalise(),
Provenance::ExternalInferred
);
for p in ALL {
assert_eq!(
p.externalise().externalise(),
p.externalise(),
"externalise must be idempotent for {p:?}"
);
assert!(p.externalise().is_external());
}
}
#[test]
fn tier_strips_externality_and_is_the_inverse_of_externalise() {
for p in ALL {
assert!(!p.tier().is_external());
assert_eq!(p.tier().externalise(), p.externalise());
assert_eq!(p.externalise().tier(), p.tier());
}
assert_eq!(Provenance::ExternalAuthored.tier(), Provenance::Authored);
assert_eq!(Provenance::Authored.tier(), Provenance::Authored);
}
#[test]
fn only_the_external_three_are_external() {
assert!(!Provenance::Derived.is_external());
assert!(!Provenance::Authored.is_external());
assert!(!Provenance::Inferred.is_external());
assert!(Provenance::ExternalDerived.is_external());
assert!(Provenance::ExternalAuthored.is_external());
assert!(Provenance::ExternalInferred.is_external());
}
}