Skip to main content

code_moniker_core/core/moniker/
mod.rs

1mod 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}