1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::{collections::BTreeMap, error::Error};
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub enum AnnotationKeyError {
10 Empty,
12}
13
14impl fmt::Display for AnnotationKeyError {
15 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 Self::Empty => formatter.write_str("annotation key cannot be empty"),
18 }
19 }
20}
21
22impl Error for AnnotationKeyError {}
23
24#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct AnnotationKey(String);
27
28impl AnnotationKey {
29 pub fn new(value: impl AsRef<str>) -> Result<Self, AnnotationKeyError> {
35 let trimmed = value.as_ref().trim();
36
37 if trimmed.is_empty() {
38 Err(AnnotationKeyError::Empty)
39 } else {
40 Ok(Self(trimmed.to_string()))
41 }
42 }
43
44 #[must_use]
46 pub fn as_str(&self) -> &str {
47 &self.0
48 }
49
50 #[must_use]
52 pub fn into_string(self) -> String {
53 self.0
54 }
55}
56
57impl AsRef<str> for AnnotationKey {
58 fn as_ref(&self) -> &str {
59 self.as_str()
60 }
61}
62
63impl fmt::Display for AnnotationKey {
64 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
65 formatter.write_str(self.as_str())
66 }
67}
68
69impl FromStr for AnnotationKey {
70 type Err = AnnotationKeyError;
71
72 fn from_str(value: &str) -> Result<Self, Self::Err> {
73 Self::new(value)
74 }
75}
76
77#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
79pub struct AnnotationValue(String);
80
81impl AnnotationValue {
82 #[must_use]
84 pub fn new(value: impl Into<String>) -> Self {
85 Self(value.into())
86 }
87
88 #[must_use]
90 pub fn as_str(&self) -> &str {
91 &self.0
92 }
93
94 #[must_use]
96 pub fn into_string(self) -> String {
97 self.0
98 }
99}
100
101impl AsRef<str> for AnnotationValue {
102 fn as_ref(&self) -> &str {
103 self.as_str()
104 }
105}
106
107impl fmt::Display for AnnotationValue {
108 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109 formatter.write_str(self.as_str())
110 }
111}
112
113impl From<&str> for AnnotationValue {
114 fn from(value: &str) -> Self {
115 Self::new(value)
116 }
117}
118
119impl From<String> for AnnotationValue {
120 fn from(value: String) -> Self {
121 Self::new(value)
122 }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq)]
127pub struct Annotation {
128 key: AnnotationKey,
129 value: AnnotationValue,
130}
131
132impl Annotation {
133 #[must_use]
135 pub const fn new(key: AnnotationKey, value: AnnotationValue) -> Self {
136 Self { key, value }
137 }
138
139 #[must_use]
141 pub const fn key(&self) -> &AnnotationKey {
142 &self.key
143 }
144
145 #[must_use]
147 pub const fn value(&self) -> &AnnotationValue {
148 &self.value
149 }
150
151 #[must_use]
153 pub fn into_parts(self) -> (AnnotationKey, AnnotationValue) {
154 (self.key, self.value)
155 }
156}
157
158#[derive(Clone, Debug, Default, Eq, PartialEq)]
160pub struct AnnotationSet {
161 values: BTreeMap<AnnotationKey, AnnotationValue>,
162}
163
164impl AnnotationSet {
165 #[must_use]
167 pub const fn new() -> Self {
168 Self {
169 values: BTreeMap::new(),
170 }
171 }
172
173 pub fn insert(&mut self, annotation: Annotation) -> Option<AnnotationValue> {
175 let (key, value) = annotation.into_parts();
176 self.values.insert(key, value)
177 }
178
179 pub fn insert_pair(
185 &mut self,
186 key: impl AsRef<str>,
187 value: impl Into<AnnotationValue>,
188 ) -> Result<Option<AnnotationValue>, AnnotationKeyError> {
189 Ok(self.values.insert(AnnotationKey::new(key)?, value.into()))
190 }
191
192 #[must_use]
194 pub fn get(&self, key: impl AsRef<str>) -> Option<&AnnotationValue> {
195 let key = AnnotationKey::new(key).ok()?;
196 self.values.get(&key)
197 }
198
199 pub fn remove(&mut self, key: impl AsRef<str>) -> Option<AnnotationValue> {
201 let key = AnnotationKey::new(key).ok()?;
202 self.values.remove(&key)
203 }
204
205 pub fn iter(&self) -> impl Iterator<Item = (&AnnotationKey, &AnnotationValue)> {
207 self.values.iter()
208 }
209
210 #[must_use]
212 pub fn len(&self) -> usize {
213 self.values.len()
214 }
215
216 #[must_use]
218 pub fn is_empty(&self) -> bool {
219 self.values.is_empty()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::{Annotation, AnnotationKey, AnnotationKeyError, AnnotationSet, AnnotationValue};
226
227 #[test]
228 fn creates_valid_annotation_key() {
229 let key = AnnotationKey::new("source").expect("valid key");
230
231 assert_eq!(key.as_str(), "source");
232 }
233
234 #[test]
235 fn rejects_empty_annotation_key() {
236 assert_eq!(AnnotationKey::new(" "), Err(AnnotationKeyError::Empty));
237 }
238
239 #[test]
240 fn constructs_annotation_value() {
241 let value = AnnotationValue::new("manual");
242
243 assert_eq!(value.as_str(), "manual");
244 }
245
246 #[test]
247 fn annotation_ordering_is_deterministic() {
248 let mut annotations = AnnotationSet::new();
249 annotations.insert_pair("zeta", "last").expect("valid key");
250 annotations
251 .insert_pair("alpha", "first")
252 .expect("valid key");
253
254 let keys = annotations
255 .iter()
256 .map(|(key, _)| key.as_str())
257 .collect::<Vec<_>>();
258 assert_eq!(keys, vec!["alpha", "zeta"]);
259 }
260
261 #[test]
262 fn insert_get_remove_behavior() {
263 let mut annotations = AnnotationSet::new();
264 let annotation = Annotation::new(
265 AnnotationKey::new("source").expect("valid key"),
266 AnnotationValue::new("manual"),
267 );
268
269 assert_eq!(annotations.insert(annotation), None);
270 assert_eq!(
271 annotations.get("source").expect("stored value").as_str(),
272 "manual"
273 );
274 assert_eq!(
275 annotations.remove("source"),
276 Some(AnnotationValue::new("manual"))
277 );
278 assert!(annotations.is_empty());
279 }
280}