#![allow(dead_code)]
use std::marker::PhantomData;
pub trait Provenance: 'static + Send + Sync + Default + Clone {
const NAME: &'static str;
const SLUG: &'static str;
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::Opaque;
type Descriptor: Default + Clone + Send + Sync + 'static;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WithProvenance<E: ?Sized, P: Provenance, ID> {
pub id: crate::Id<crate::id::sourced::Sourced<E, P>, ID>,
pub descriptor: P::Descriptor,
}
impl<E: ?Sized, P: Provenance, ID> WithProvenance<E, P, ID> {
pub fn new(
id: crate::Id<crate::id::sourced::Sourced<E, P>, ID>,
descriptor: P::Descriptor,
) -> Self {
Self { id, descriptor }
}
}
pub use crate::LabelPolicy;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct External<Provider = ()>(PhantomData<Provider>);
impl Provenance for External<()> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Stripe> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("stripe");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Github> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("github");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Spark> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("spark");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Okta> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("okta");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::AwsS3> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("aws-s3");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::GoogleCloud> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("google-cloud");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Iceberg> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("iceberg");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
impl Provenance for External<providers::Nessie> {
const NAME: &'static str = "external";
const SLUG: &'static str = "ext";
const VENDOR: Option<&'static str> = Some("nessie");
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Generated<Strategy = ()>(PhantomData<Strategy>);
impl<S> Provenance for Generated<S>
where
S: 'static + Send + Sync + Default + Clone,
{
const NAME: &'static str = "generated";
const SLUG: &'static str = "gen";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::EntityNameDefault;
type Descriptor = ();
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Imported<From = ()>(PhantomData<From>);
impl<F> Provenance for Imported<F>
where
F: 'static + Send + Sync + Default + Clone,
{
const NAME: &'static str = "imported";
const SLUG: &'static str = "imp";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::ExternalKeyDefault;
type Descriptor = ();
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Derived<Method = ()>(PhantomData<Method>);
impl<M> Provenance for Derived<M>
where
M: 'static + Send + Sync + Default + Clone,
{
const NAME: &'static str = "derived";
const SLUG: &'static str = "der";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::EntityNameDefault;
type Descriptor = ();
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Scoped<Scope = (), Inner = ()>(PhantomData<(Scope, Inner)>);
impl<Scope, Inner> Provenance for Scoped<Scope, Inner>
where
Scope: 'static + Send + Sync + Default + Clone,
Inner: Provenance,
{
const NAME: &'static str = "scoped";
const SLUG: &'static str = "sco";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = Inner::LABEL_POLICY;
type Descriptor = ();
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Temporary;
impl Provenance for Temporary {
const NAME: &'static str = "temporary";
const SLUG: &'static str = "tmp";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::OpaqueByDefault;
type Descriptor = ();
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct ClientProvided;
impl Provenance for ClientProvided {
const NAME: &'static str = "client-provided";
const SLUG: &'static str = "cli";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::OpaqueByDefault;
type Descriptor = ();
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct AliasOf<Canonical = ()>(PhantomData<Canonical>);
impl<C> Provenance for AliasOf<C>
where
C: 'static + Send + Sync + Default + Clone,
{
const NAME: &'static str = "alias";
const SLUG: &'static str = "als";
const VENDOR: Option<&'static str> = None;
const LABEL_POLICY: LabelPolicy = LabelPolicy::EntityNameDefault;
type Descriptor = ();
}
pub trait Vendor {
const VENDOR: &'static str;
}
pub mod providers {
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Stripe;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Github;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Spark;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Okta;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct AwsS3;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct GoogleCloud;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Iceberg;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Nessie;
impl super::Vendor for Stripe {
const VENDOR: &'static str = "stripe";
}
impl super::Vendor for Github {
const VENDOR: &'static str = "github";
}
impl super::Vendor for Spark {
const VENDOR: &'static str = "spark";
}
impl super::Vendor for Okta {
const VENDOR: &'static str = "okta";
}
impl super::Vendor for AwsS3 {
const VENDOR: &'static str = "aws-s3";
}
impl super::Vendor for GoogleCloud {
const VENDOR: &'static str = "google-cloud";
}
impl super::Vendor for Iceberg {
const VENDOR: &'static str = "iceberg";
}
impl super::Vendor for Nessie {
const VENDOR: &'static str = "nessie";
}
}
pub mod strategies {
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct UuidV4;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct UuidV7;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Cuid;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Cuid2;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Snowflake;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Nanoid;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Hashids;
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct Ulid;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::id::sourced::Sourced;
use crate::labeling::Labeling;
use crate::{Entity, Id, IdGenerator, Label, MakeLabeling};
use pretty_assertions::assert_eq;
struct MockGenerator;
impl IdGenerator for MockGenerator {
type IdType = String;
fn next_id_rep() -> Self::IdType {
"mock-id".to_string()
}
}
#[derive(Debug, PartialEq)]
struct User;
impl Label for User {
type Labeler = MakeLabeling<Self>;
fn labeler() -> Self::Labeler {
MakeLabeling::default()
}
}
impl Entity for User {
type IdGen = MockGenerator;
}
struct Customer;
impl Label for Customer {
type Labeler = MakeLabeling<Self>;
fn labeler() -> Self::Labeler {
MakeLabeling::default()
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
struct MetaProv;
impl Provenance for MetaProv {
const NAME: &'static str = "meta";
const SLUG: &'static str = "met";
type Descriptor = u32;
}
type UserId = Id<Sourced<User, Generated<strategies::UuidV7>>, String>;
type StripeId = Id<Sourced<Customer, External<providers::Stripe>>, String>;
type TempId = Id<Sourced<User, Temporary>, String>;
#[test]
fn test_provenance_trait_implemented() {
fn assert_provenance<P: Provenance>() {}
assert_provenance::<External>();
assert_provenance::<Generated>();
assert_provenance::<Imported>();
assert_provenance::<Derived>();
assert_provenance::<Scoped<(), Generated<()>>>();
assert_provenance::<Temporary>();
assert_provenance::<ClientProvided>();
assert_provenance::<AliasOf>();
}
#[test]
fn test_provenance_names() {
assert_eq!(External::<()>::NAME, "external");
assert_eq!(Generated::<()>::NAME, "generated");
assert_eq!(Imported::<()>::NAME, "imported");
assert_eq!(Derived::<()>::NAME, "derived");
assert_eq!(Scoped::<(), Generated<()>>::NAME, "scoped");
assert_eq!(Temporary::NAME, "temporary");
assert_eq!(ClientProvided::NAME, "client-provided");
assert_eq!(AliasOf::<()>::NAME, "alias");
}
#[test]
fn test_all_core_provenance_slugs() {
assert_eq!(External::<()>::SLUG, "ext");
assert_eq!(Generated::<()>::SLUG, "gen");
assert_eq!(Imported::<()>::SLUG, "imp");
assert_eq!(Derived::<()>::SLUG, "der");
assert_eq!(Scoped::<(), Generated<()>>::SLUG, "sco");
assert_eq!(Temporary::SLUG, "tmp");
assert_eq!(ClientProvided::SLUG, "cli");
assert_eq!(AliasOf::<()>::SLUG, "als");
}
#[test]
fn test_vendor_providers_expose_constants() {
assert_eq!(providers::Stripe::VENDOR, "stripe");
assert_eq!(providers::Github::VENDOR, "github");
assert_eq!(providers::Spark::VENDOR, "spark");
assert_eq!(providers::Okta::VENDOR, "okta");
}
#[test]
fn test_with_provenance_stores_both_fields() {
let labeler = User::labeler();
let id =
Id::<Sourced<User, MetaProv>, String>::direct(labeler.label(), "test-id".to_string());
let desc = 42u32;
let with_prov = WithProvenance::new(id.clone(), desc);
assert_eq!(with_prov.id, id);
assert_eq!(with_prov.descriptor, desc);
}
#[test]
fn test_with_provenance_equality() {
let labeler = User::labeler();
let id = Id::<Sourced<User, MetaProv>, String>::direct(labeler.label(), "id".to_string());
let a = WithProvenance::new(id.clone(), 1u32);
let b = WithProvenance::new(id.clone(), 1u32);
let c = WithProvenance::new(id, 2u32);
assert_eq!(a, b);
assert_ne!(a, c); }
#[test]
fn test_label_policies() {
assert_eq!(
External::<()>::LABEL_POLICY,
LabelPolicy::ExternalKeyDefault
);
assert_eq!(
Generated::<()>::LABEL_POLICY,
LabelPolicy::EntityNameDefault
);
assert_eq!(
Imported::<()>::LABEL_POLICY,
LabelPolicy::ExternalKeyDefault
);
assert_eq!(Derived::<()>::LABEL_POLICY, LabelPolicy::EntityNameDefault);
assert_eq!(Temporary::LABEL_POLICY, LabelPolicy::OpaqueByDefault);
assert_eq!(ClientProvided::LABEL_POLICY, LabelPolicy::OpaqueByDefault);
}
#[test]
fn test_canonical_id_is_opaque() {
let id_str = "cus_L3H8Z6K9j2";
let id = StripeId::from_canonical(id_str.to_string());
assert_eq!(id.to_string(), id_str);
assert_eq!(id.as_str(), id_str);
assert_eq!(format!("{}", id), id_str);
}
#[test]
fn test_labeling_output_by_policy() {
let stripe_id = StripeId::from_canonical("cus_123".to_string());
assert_eq!(
stripe_id.labeled().to_string(),
"Customer@ext/stripe::cus_123"
);
let user_id = UserId::direct("User", "user-123".to_string());
assert_eq!(user_id.labeled().to_string(), "User::user-123");
let temp_id = TempId::from_canonical("temp-123".to_string());
assert_eq!(temp_id.labeled().to_string(), "temp-123");
}
#[test]
fn test_explicit_labeling_override() {
use crate::id::labeled::LabelMode;
let stripe_id = StripeId::from_canonical("cus_123".to_string());
assert_eq!(
stripe_id.labeled().mode(LabelMode::None).to_string(),
"cus_123"
);
assert_eq!(
stripe_id.labeled().mode(LabelMode::Short).to_string(),
"Customer::cus_123"
);
}
#[test]
fn test_serialization_is_canonical() {
let id = StripeId::from_canonical("cus_123".to_string());
let json = serde_json::to_string(&id).unwrap();
assert_eq!(json, "\"cus_123\"");
let deserialized: StripeId = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.to_string(), "cus_123");
}
#[test]
fn test_sourced_entity_impl() {
fn assert_entity<E: Entity>() {}
assert_entity::<Sourced<User, Generated<strategies::UuidV7>>>();
}
#[test]
fn test_providers_are_cloneable() {
let stripe = providers::Stripe;
let _stripe_clone = stripe;
let github = providers::Github;
let _github_clone = github;
let spark = providers::Spark;
let _spark_clone = spark;
}
#[test]
fn test_strategies_are_cloneable() {
let uuid_v7 = strategies::UuidV7;
let _uuid_v7_clone = uuid_v7;
let snowflake = strategies::Snowflake;
let _snowflake_clone = snowflake;
}
}