cirru_edn/
tag.rs

1//! Tag abstractions for EDN values.
2//!
3//! Tags (previously called "keywords") are named constants that can be used
4//! as map keys, enum values, or identifiers. This module provides efficient
5//! string reuse through Arc<str> to minimize memory allocation.
6
7use std::{
8  cmp::Eq,
9  cmp::Ordering,
10  fmt,
11  hash::{Hash, Hasher},
12  sync::Arc,
13};
14
15/// Tags across whole program with strings reused for efficiency.
16///
17/// A tag is similar to a keyword in other Lisp dialects - it's a
18/// self-evaluating named constant. Tags are commonly used as:
19/// - Map keys (like `:name`, `:age`)
20/// - Enum-like values (like `:success`, `:error`)
21/// - Type identifiers in records
22///
23/// # Examples
24///
25/// ```
26/// use cirru_edn::EdnTag;
27///
28/// let tag1 = EdnTag::new("status");
29/// let tag2 = EdnTag::from("active");
30/// ```
31#[derive(fmt::Debug, Clone)]
32pub struct EdnTag(
33  /// The tag string - there will be a practical limit on the count of all tags
34  pub Arc<str>,
35);
36
37impl fmt::Display for EdnTag {
38  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39    f.write_str(&self.0)
40  }
41}
42
43impl Hash for EdnTag {
44  fn hash<H>(&self, _state: &mut H)
45  where
46    H: Hasher,
47  {
48    "EdnTag:".hash(_state);
49    self.0.hash(_state);
50  }
51}
52
53impl From<&str> for EdnTag {
54  fn from(s: &str) -> Self {
55    Self(Arc::from(s))
56  }
57}
58
59impl EdnTag {
60  /// Create a new tag from a string.
61  ///
62  /// # Examples
63  ///
64  /// ```
65  /// use cirru_edn::EdnTag;
66  ///
67  /// let tag = EdnTag::new("my-tag");
68  /// assert_eq!(tag.ref_str(), "my-tag");
69  /// ```
70  pub fn new<T: Into<Arc<str>>>(s: T) -> Self {
71    EdnTag(s.into())
72  }
73
74  /// Get the inner Arc<str> reference.
75  ///
76  /// This provides access to the underlying string data without cloning.
77  pub fn arc_str(&self) -> Arc<str> {
78    (*self.0).into()
79  }
80
81  /// Get a string slice reference for comparison.
82  ///
83  /// This is the most efficient way to compare tag content.
84  pub fn ref_str(&self) -> &str {
85    &self.0
86  }
87
88  /// Check if the tag matches a string slice.
89  ///
90  /// This is more efficient than converting the tag to a string.
91  ///
92  /// # Examples
93  ///
94  /// ```
95  /// use cirru_edn::EdnTag;
96  ///
97  /// let tag = EdnTag::new("status");
98  /// assert!(tag.matches("status"));
99  /// assert!(!tag.matches("other"));
100  /// ```
101  pub fn matches(&self, s: &str) -> bool {
102    self.0.as_ref() == s
103  }
104
105  /// Get the length of the tag string.
106  pub fn len(&self) -> usize {
107    self.0.len()
108  }
109
110  /// Check if the tag is empty.
111  pub fn is_empty(&self) -> bool {
112    self.0.is_empty()
113  }
114}
115
116impl Ord for EdnTag {
117  fn cmp(&self, other: &Self) -> Ordering {
118    self.0.cmp(&other.0)
119  }
120}
121
122impl PartialOrd for EdnTag {
123  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
124    Some(self.cmp(other))
125  }
126}
127
128impl Eq for EdnTag {}
129
130impl PartialEq for EdnTag {
131  fn eq(&self, other: &Self) -> bool {
132    self.0 == other.0
133  }
134}