haystack_core/kinds/
ref_.rs1use std::fmt;
4use std::hash::{Hash, Hasher};
5
6#[derive(Debug, Clone)]
13pub struct HRef {
14 pub val: String,
15 pub dis: Option<String>,
16}
17
18impl HRef {
19 pub fn new(val: impl Into<String>, dis: Option<String>) -> Self {
20 Self {
21 val: val.into(),
22 dis,
23 }
24 }
25
26 pub fn from_val(val: impl Into<String>) -> Self {
27 Self {
28 val: val.into(),
29 dis: None,
30 }
31 }
32}
33
34impl HRef {
35 pub fn is_valid(&self) -> bool {
38 !self.val.is_empty()
39 && self
40 .val
41 .chars()
42 .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | ':' | '-' | '.' | '~'))
43 }
44}
45
46impl PartialEq for HRef {
47 fn eq(&self, other: &Self) -> bool {
48 self.val == other.val
49 }
50}
51
52impl Eq for HRef {}
53
54impl Hash for HRef {
55 fn hash<H: Hasher>(&self, state: &mut H) {
56 self.val.hash(state);
57 }
58}
59
60impl fmt::Display for HRef {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "@{}", self.val)?;
63 if let Some(ref dis) = self.dis {
64 write!(f, " '{dis}'")?;
65 }
66 Ok(())
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use std::collections::HashSet;
74
75 #[test]
76 fn ref_display_without_dis() {
77 let r = HRef::from_val("site-1");
78 assert_eq!(r.to_string(), "@site-1");
79 }
80
81 #[test]
82 fn ref_display_with_dis() {
83 let r = HRef::new("site-1", Some("Main Site".into()));
84 assert_eq!(r.to_string(), "@site-1 'Main Site'");
85 }
86
87 #[test]
88 fn ref_equality_ignores_dis() {
89 let a = HRef::new("site-1", Some("Building A".into()));
90 let b = HRef::new("site-1", Some("Different Name".into()));
91 let c = HRef::from_val("site-1");
92 assert_eq!(a, b);
93 assert_eq!(a, c);
94 }
95
96 #[test]
97 fn ref_hash_ignores_dis() {
98 let a = HRef::new("site-1", Some("A".into()));
99 let b = HRef::new("site-1", Some("B".into()));
100 let mut set = HashSet::new();
101 set.insert(a);
102 assert!(set.contains(&b));
103 }
104
105 #[test]
106 fn ref_inequality() {
107 let a = HRef::from_val("site-1");
108 let b = HRef::from_val("site-2");
109 assert_ne!(a, b);
110 }
111
112 #[test]
113 fn ref_from_val_convenience() {
114 let r = HRef::from_val("abc");
115 assert_eq!(r.val, "abc");
116 assert_eq!(r.dis, None);
117 }
118}