use super::Id;
use crate::{DELIMITER, Label, LabelPolicy, Labeling};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LabelMode {
#[default]
None,
Short,
Full,
}
impl From<LabelPolicy> for LabelMode {
fn from(policy: LabelPolicy) -> Self {
match policy {
LabelPolicy::Opaque | LabelPolicy::OpaqueByDefault => LabelMode::None,
LabelPolicy::EntityNameDefault => LabelMode::Short,
LabelPolicy::ExternalKeyDefault => LabelMode::Full,
}
}
}
pub struct Labeled<'a, T: ?Sized, ID> {
pub(crate) id: &'a Id<T, ID>,
pub(crate) mode: LabelMode,
}
impl<'a, T: ?Sized, ID> Labeled<'a, T, ID> {
pub fn new(id: &'a Id<T, ID>, mode: LabelMode) -> Self {
Self { id, mode }
}
pub fn mode(mut self, mode: LabelMode) -> Self {
self.mode = mode;
self
}
}
impl<T: ?Sized + Label, ID: fmt::Display> fmt::Display for Labeled<'_, T, ID> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.mode {
LabelMode::None => write!(f, "{}", self.id.id),
LabelMode::Short => {
if self.id.label.is_empty() {
write!(f, "{}", self.id.id)
} else {
write!(f, "{}{DELIMITER}{}", self.id.label, self.id.id)
}
}
LabelMode::Full => {
let labeler = T::labeler();
let decorated = labeler.decorated_label();
if decorated.is_empty() {
write!(f, "{}", self.id.id)
} else {
write!(f, "{decorated}{DELIMITER}{}", self.id.id)
}
}
}
}
}
impl<T: ?Sized + Label, ID: fmt::Debug> fmt::Debug for Labeled<'_, T, ID> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.mode {
LabelMode::None => write!(f, "{:?}", self.id.id),
LabelMode::Short => {
if self.id.label.is_empty() {
write!(f, "{:?}", self.id.id)
} else {
write!(f, "{}{DELIMITER}{:?}", self.id.label, self.id.id)
}
}
LabelMode::Full => {
let labeler = T::labeler();
let decorated = labeler.decorated_label();
if decorated.is_empty() {
write!(f, "{:?}", self.id.id)
} else {
write!(f, "{decorated}{DELIMITER}{:?}", self.id.id)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CustomLabeling;
use crate::NoLabeling;
struct Foo;
impl Label for Foo {
type Labeler = CustomLabeling;
fn labeler() -> Self::Labeler {
CustomLabeling::new("MyFooferNut")
}
}
struct NoLabelZed;
impl Label for NoLabelZed {
type Labeler = NoLabeling;
fn labeler() -> Self::Labeler {
NoLabeling
}
}
#[test]
fn test_label_policy_to_mode_conversion() {
assert_eq!(LabelMode::from(LabelPolicy::Opaque), LabelMode::None);
assert_eq!(
LabelMode::from(LabelPolicy::OpaqueByDefault),
LabelMode::None
);
assert_eq!(
LabelMode::from(LabelPolicy::EntityNameDefault),
LabelMode::Short
);
assert_eq!(
LabelMode::from(LabelPolicy::ExternalKeyDefault),
LabelMode::Full
);
}
#[test]
fn test_labeled_none_mode() {
let id: Id<Foo, String> = Id::direct("Label", "value".into());
assert_eq!(id.labeled().mode(LabelMode::None).to_string(), "value");
}
#[test]
fn test_labeled_short_mode() {
let id: Id<Foo, String> = Id::direct("Label", "value".into());
assert_eq!(
id.labeled().mode(LabelMode::Short).to_string(),
"Label::value"
);
}
#[test]
fn test_labeled_full_mode() {
let id: Id<Foo, String> = Id::direct("_", "value".into());
let full = id.labeled().mode(LabelMode::Full).to_string();
assert!(full.contains("value"));
assert!(full.contains("MyFooferNut")); }
#[test]
fn test_labeled_full_ignores_stored_label() {
let id: Id<Foo, String> = Id::direct("WRONG", "value".into());
let short = id.labeled().mode(LabelMode::Short).to_string();
assert!(short.contains("WRONG"));
let full = id.labeled().mode(LabelMode::Full).to_string();
assert!(!full.contains("WRONG")); assert!(full.contains("MyFooferNut")); }
#[test]
fn test_labeled_empty_label_short_mode() {
let id: Id<NoLabelZed, String> = Id::direct("", "value".into());
let short = id.labeled().mode(LabelMode::Short).to_string();
assert_eq!(short, "value");
}
#[test]
fn test_labeled_debug_modes() {
let id: Id<Foo, u64> = Id::direct("Label", 42u64);
assert_eq!(id.labeled().mode(LabelMode::None).to_string(), "42");
assert_eq!(id.labeled().mode(LabelMode::Short).to_string(), "Label::42");
}
#[test]
fn test_labeled_builder_pattern() {
let id: Id<Foo, String> = Id::direct("L", "v".into());
let labeled_none = id.labeled().mode(LabelMode::None);
assert_eq!(labeled_none.to_string(), "v");
let labeled_short = id.labeled().mode(LabelMode::Short);
assert_eq!(labeled_short.to_string(), "L::v");
}
}