use std::borrow::Cow;
use std::marker::PhantomData;
use crate::{Entity, Label, Labeling};
use super::provenance::{Generated, Provenance};
#[derive(Debug, Clone, Copy)]
pub struct Sourced<E: ?Sized, S: Provenance> {
_entity: PhantomData<E>,
_source: PhantomData<S>,
}
impl<E: ?Sized, S: Provenance> Sourced<E, S> {
#[inline]
pub const fn new() -> Self {
Self {
_entity: PhantomData,
_source: PhantomData,
}
}
}
impl<E: ?Sized, S: Provenance> Default for Sourced<E, S> {
fn default() -> Self {
Self::new()
}
}
pub struct SourceLabeler<L: Labeling, S: Provenance> {
entity_labeler: L,
_provenance: PhantomData<S>,
}
impl<L: Labeling, S: Provenance> SourceLabeler<L, S> {
pub fn new(entity_labeler: L) -> Self {
Self {
entity_labeler,
_provenance: PhantomData,
}
}
pub fn provenance_label(&self) -> String {
provenance_display_name::<S>()
}
pub fn decorated_label(&self) -> String {
format!(
"{}@{}",
self.entity_labeler.label(),
self.provenance_label()
)
}
}
impl<L: Labeling, S: Provenance> Labeling for SourceLabeler<L, S> {
fn label(&self) -> &str {
self.entity_labeler.label()
}
fn decorated_label(&self) -> Cow<'_, str> {
Cow::Owned(self.decorated_label())
}
}
impl<E: ?Sized + Label, S: Provenance> Label for Sourced<E, S> {
type Labeler = SourceLabeler<E::Labeler, S>;
const POLICY: crate::LabelPolicy = S::LABEL_POLICY;
fn labeler() -> Self::Labeler {
SourceLabeler::new(E::labeler())
}
}
impl<E: ?Sized + Entity, S> Entity for Sourced<E, Generated<S>>
where
S: 'static + Send + Sync + Default + Clone,
{
type IdGen = E::IdGen;
}
fn provenance_display_name<S: Provenance>() -> String {
match S::VENDOR {
Some(vendor) => format!("{}/{}", S::SLUG, vendor),
None => S::SLUG.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
struct TestEntity;
impl Label for TestEntity {
type Labeler = crate::MakeLabeling<Self>;
fn labeler() -> Self::Labeler {
crate::MakeLabeling::default()
}
}
#[test]
fn test_sourced_is_zero_sized() {
use std::mem::size_of;
assert_eq!(
size_of::<Sourced<TestEntity, super::super::provenance::Generated<()>>>(),
0
);
}
#[test]
fn test_sourced_label_delegation() {
use super::super::provenance::Generated;
let labeler = Sourced::<TestEntity, Generated<()>>::labeler();
assert_eq!(labeler.label(), "TestEntity");
}
#[test]
fn test_provenance_display_name() {
use super::super::provenance::{External, Temporary};
assert_eq!(provenance_display_name::<External<()>>(), "ext");
assert_eq!(provenance_display_name::<Generated<()>>(), "gen");
assert_eq!(provenance_display_name::<Temporary>(), "tmp");
}
#[test]
fn test_slug_format_matches_constants() {
use super::super::provenance::{External, Generated, Imported, Temporary};
assert_eq!(
provenance_display_name::<External<()>>(),
External::<()>::SLUG
);
assert_eq!(
provenance_display_name::<Generated<()>>(),
Generated::<()>::SLUG
);
assert_eq!(
provenance_display_name::<Imported<()>>(),
Imported::<()>::SLUG
);
assert_eq!(provenance_display_name::<Temporary>(), Temporary::SLUG);
}
#[test]
fn test_external_with_vendor_display() {
use super::super::provenance::{External, providers};
assert_eq!(
provenance_display_name::<External<providers::Stripe>>(),
"ext/stripe"
);
assert_eq!(
provenance_display_name::<External<providers::Spark>>(),
"ext/spark"
);
assert_eq!(
provenance_display_name::<External<providers::Github>>(),
"ext/github"
);
assert_eq!(provenance_display_name::<External<()>>(), "ext");
}
}