code_moniker_core/core/moniker/
mod.rs1mod builder;
2pub mod encoding;
3pub mod query;
4mod view;
5
6pub use builder::MonikerBuilder;
7pub use encoding::EncodingError;
8pub use view::{MonikerView, Segment, SegmentIter};
9
10#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
11pub struct Moniker {
12 bytes: Box<[u8]>,
13}
14
15impl Moniker {
16 pub fn from_encoded(bytes: Vec<u8>) -> Result<Self, EncodingError> {
17 MonikerView::from_encoded(&bytes)?;
18 Ok(Self {
19 bytes: bytes.into_boxed_slice(),
20 })
21 }
22
23 pub(crate) fn from_encoded_unchecked(bytes: Vec<u8>) -> Self {
24 Self {
25 bytes: bytes.into_boxed_slice(),
26 }
27 }
28
29 pub fn as_view(&self) -> MonikerView<'_> {
30 unsafe { MonikerView::from_encoded_unchecked(&self.bytes) }
31 }
32
33 pub fn as_encoded(&self) -> &[u8] {
34 &self.bytes
35 }
36
37 pub fn into_encoded(self) -> Vec<u8> {
38 self.bytes.into_vec()
39 }
40}
41
42#[cfg(feature = "serde")]
43impl serde::Serialize for Moniker {
44 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
45 where
46 S: serde::Serializer,
47 {
48 use serde::ser::SerializeStruct;
49
50 let mut state = serializer.serialize_struct("Moniker", 1)?;
51 state.serialize_field("bytes", &self.bytes)?;
52 state.end()
53 }
54}
55
56#[cfg(feature = "serde")]
57impl<'de> serde::Deserialize<'de> for Moniker {
58 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59 where
60 D: serde::Deserializer<'de>,
61 {
62 #[derive(serde::Deserialize)]
63 struct Repr {
64 bytes: Vec<u8>,
65 }
66
67 let repr = Repr::deserialize(deserializer)?;
68 Self::from_encoded(repr.bytes).map_err(serde::de::Error::custom)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn builder_rebuilds_same_encoding_from_view() {
78 let m1 = MonikerBuilder::new()
79 .project(b"my-app")
80 .segment(b"module", b"main")
81 .segment(b"class", b"Foo")
82 .segment(b"method", b"bar(2)")
83 .build();
84
85 let v = m1.as_view();
86 let mut b2 = MonikerBuilder::new();
87 b2.project(v.project());
88 for seg in v.segments() {
89 b2.segment(seg.kind, seg.name);
90 }
91 let m2 = b2.build();
92
93 assert_eq!(m1.as_encoded(), m2.as_encoded());
94 assert_eq!(m1, m2);
95 }
96
97 #[test]
98 fn eq_via_bytes() {
99 let a = MonikerBuilder::new()
100 .project(b"x")
101 .segment(b"path", b"a")
102 .build();
103 let b = MonikerBuilder::new()
104 .project(b"x")
105 .segment(b"path", b"a")
106 .build();
107 let c = MonikerBuilder::new()
108 .project(b"x")
109 .segment(b"path", b"b")
110 .build();
111 assert_eq!(a, b);
112 assert_ne!(a, c);
113 }
114
115 #[test]
116 fn ord_places_parent_before_child() {
117 let parent = MonikerBuilder::new()
118 .project(b"app")
119 .segment(b"module", b"main")
120 .build();
121 let child = MonikerBuilder::new()
122 .project(b"app")
123 .segment(b"module", b"main")
124 .segment(b"class", b"Foo")
125 .build();
126 assert!(parent < child);
127 }
128
129 #[test]
130 fn ord_separates_distinct_projects() {
131 let a = MonikerBuilder::new().project(b"app1").build();
132 let b = MonikerBuilder::new().project(b"app2").build();
133 assert!(a < b);
134 }
135
136 #[test]
137 fn from_encoded_roundtrip() {
138 let m = MonikerBuilder::new()
139 .project(b"pj")
140 .segment(b"path", b"foo")
141 .build();
142 let bytes = m.clone().into_encoded();
143 let m2 = Moniker::from_encoded(bytes).unwrap();
144 assert_eq!(m, m2);
145 assert!(Moniker::from_encoded(vec![99u8; 5]).is_err());
146 }
147
148 #[cfg(feature = "serde")]
149 #[test]
150 fn serde_rejects_invalid_encoded_moniker() {
151 let err = serde_json::from_str::<Moniker>(r#"{"bytes":[2,0,0]}"#).unwrap_err();
152 assert!(err.to_string().contains("project must not be empty"));
153 }
154
155 #[cfg(feature = "serde")]
156 #[test]
157 fn serde_roundtrips_valid_moniker() {
158 let m = MonikerBuilder::new()
159 .project(b"pj")
160 .segment(b"path", b"foo")
161 .build();
162 let json = serde_json::to_string(&m).unwrap();
163 let decoded: Moniker = serde_json::from_str(&json).unwrap();
164 assert_eq!(decoded, m);
165 }
166
167 use proptest::prelude::*;
168
169 proptest! {
170 #![proptest_config(ProptestConfig {
171 cases: 256,
172 ..ProptestConfig::default()
173 })]
174
175 #[test]
176 fn moniker_from_encoded_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..4096)) {
177 if let Ok(m) = Moniker::from_encoded(bytes.clone()) {
178 prop_assert_eq!(m.as_encoded(), bytes.as_slice());
179 let m2 = Moniker::from_encoded(m.as_encoded().to_vec())
180 .expect("validated encoding must re-parse");
181 prop_assert_eq!(m, m2);
182 }
183 }
184
185 #[test]
186 fn moniker_view_and_owned_agree(bytes in proptest::collection::vec(any::<u8>(), 0..4096)) {
187 let owned = Moniker::from_encoded(bytes.clone()).is_ok();
188 let view = MonikerView::from_encoded(&bytes).is_ok();
189 prop_assert_eq!(owned, view);
190 }
191 }
192}