use crate::Label;
use once_cell::sync::OnceCell;
use pretty_type_name::pretty_type_name;
use smol_str::SmolStr;
use std::borrow::Cow;
use std::convert::Infallible;
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;
pub trait Labeling {
fn label(&self) -> &str;
fn decorated_label(&self) -> Cow<'_, str> {
Cow::Borrowed(self.label())
}
}
impl dyn Labeling {
pub fn summon<T: Label>() -> <T as Label>::Labeler {
T::labeler()
}
}
#[derive(Clone)]
pub struct MakeLabeling<T: ?Sized> {
label: OnceCell<SmolStr>,
marker: PhantomData<T>,
}
impl<T: ?Sized> MakeLabeling<T> {
pub const fn new() -> Self {
Self {
label: OnceCell::new(),
marker: PhantomData,
}
}
}
impl<T: ?Sized> Default for MakeLabeling<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: ?Sized> Labeling for MakeLabeling<T> {
fn label(&self) -> &str {
self.label
.get_or_init(|| SmolStr::new(pretty_type_name::<T>()))
.as_str()
}
}
impl<T: ?Sized> fmt::Debug for MakeLabeling<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MakeLabeling({})", self.label())
}
}
impl<T: ?Sized> fmt::Display for MakeLabeling<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.label())
}
}
#[derive(Clone)]
pub struct CustomLabeling {
label: SmolStr,
}
impl CustomLabeling {
pub fn new(label: impl AsRef<str>) -> Self {
Self {
label: SmolStr::new(label),
}
}
}
impl Labeling for CustomLabeling {
fn label(&self) -> &str {
self.label.as_str()
}
}
impl fmt::Debug for CustomLabeling {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CustomLabeling({})", self.label())
}
}
impl fmt::Display for CustomLabeling {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.label())
}
}
impl From<&str> for CustomLabeling {
fn from(label: &str) -> Self {
Self {
label: SmolStr::new(label),
}
}
}
impl From<String> for CustomLabeling {
fn from(label: String) -> Self {
Self {
label: label.into(),
}
}
}
impl FromStr for CustomLabeling {
type Err = Infallible;
fn from_str(label: &str) -> Result<Self, Self::Err> {
Ok(label.into())
}
}
#[derive(Debug, Copy, Clone)]
pub struct NoLabeling;
impl Labeling for NoLabeling {
fn label(&self) -> &str {
""
}
}
impl fmt::Display for NoLabeling {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "")
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Foo;
impl Label for Foo {
type Labeler = CustomLabeling;
fn labeler() -> Self::Labeler {
CustomLabeling::new("MyFooferNut")
}
}
#[test]
fn test_make_labeling_caching() {
let labeler = MakeLabeling::<String>::default();
let label1 = labeler.label();
let label2 = labeler.label();
assert_eq!(label1, label2);
assert!(!label1.is_empty());
}
#[test]
fn test_make_labeling_display_and_debug() {
let labeler = MakeLabeling::<u32>::default();
let label = labeler.label();
assert_eq!(format!("{}", labeler), label);
assert!(format!("{:?}", labeler).contains(label));
}
#[test]
fn test_custom_labeling_conversions() {
let from_str = CustomLabeling::from("test");
let from_string = CustomLabeling::from("test".to_string());
let from_parse: CustomLabeling = "test".parse().unwrap();
assert_eq!(from_str.label(), "test");
assert_eq!(from_string.label(), "test");
assert_eq!(from_parse.label(), "test");
}
#[test]
fn test_custom_labeling_display_and_debug() {
let labeler = CustomLabeling::new("MyLabel");
assert_eq!(format!("{}", labeler), "MyLabel");
assert!(format!("{:?}", labeler).contains("MyLabel"));
}
#[test]
fn test_no_labeling_returns_empty() {
let labeler = NoLabeling;
assert_eq!(labeler.label(), "");
}
#[test]
fn test_labeling_summon() {
let labeler = <dyn Labeling>::summon::<Foo>();
assert_eq!(labeler.label(), "MyFooferNut");
}
#[test]
fn test_labeling_decorated_default() {
let labeler = MakeLabeling::<String>::default();
let decorated = labeler.decorated_label();
assert_eq!(decorated.as_ref(), labeler.label());
}
#[test]
fn test_primitive_labels_not_empty() {
assert!(!<u32 as Label>::labeler().label().is_empty());
assert!(!<u64 as Label>::labeler().label().is_empty());
assert!(!<String as Label>::labeler().label().is_empty());
assert!(!<bool as Label>::labeler().label().is_empty());
}
}