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)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Moniker {
13	bytes: Vec<u8>,
14}
15
16impl Moniker {
17	pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, EncodingError> {
18		MonikerView::from_bytes(&bytes)?;
19		Ok(Self { bytes })
20	}
21
22	pub fn from_canonical_bytes(bytes: Vec<u8>) -> Self {
23		Self { bytes }
24	}
25
26	pub fn as_view(&self) -> MonikerView<'_> {
27		unsafe { MonikerView::from_canonical_bytes(&self.bytes) }
28	}
29
30	pub fn as_bytes(&self) -> &[u8] {
31		&self.bytes
32	}
33
34	pub fn into_bytes(self) -> Vec<u8> {
35		self.bytes
36	}
37}
38
39#[cfg(test)]
40mod tests {
41	use super::*;
42
43	#[test]
44	fn roundtrip_canonicality() {
45		let m1 = MonikerBuilder::new()
46			.project(b"my-app")
47			.segment(b"module", b"main")
48			.segment(b"class", b"Foo")
49			.segment(b"method", b"bar(2)")
50			.build();
51
52		let v = m1.as_view();
53		let mut b2 = MonikerBuilder::new();
54		b2.project(v.project());
55		for seg in v.segments() {
56			b2.segment(seg.kind, seg.name);
57		}
58		let m2 = b2.build();
59
60		assert_eq!(m1.as_bytes(), m2.as_bytes());
61		assert_eq!(m1, m2);
62	}
63
64	#[test]
65	fn eq_via_bytes() {
66		let a = MonikerBuilder::new()
67			.project(b"x")
68			.segment(b"path", b"a")
69			.build();
70		let b = MonikerBuilder::new()
71			.project(b"x")
72			.segment(b"path", b"a")
73			.build();
74		let c = MonikerBuilder::new()
75			.project(b"x")
76			.segment(b"path", b"b")
77			.build();
78		assert_eq!(a, b);
79		assert_ne!(a, c);
80	}
81
82	#[test]
83	fn ord_places_parent_before_child() {
84		let parent = MonikerBuilder::new()
85			.project(b"app")
86			.segment(b"module", b"main")
87			.build();
88		let child = MonikerBuilder::new()
89			.project(b"app")
90			.segment(b"module", b"main")
91			.segment(b"class", b"Foo")
92			.build();
93		assert!(parent < child);
94	}
95
96	#[test]
97	fn ord_separates_distinct_projects() {
98		let a = MonikerBuilder::new().project(b"app1").build();
99		let b = MonikerBuilder::new().project(b"app2").build();
100		assert!(a < b);
101	}
102
103	#[test]
104	fn from_bytes_roundtrip() {
105		let m = MonikerBuilder::new()
106			.project(b"pj")
107			.segment(b"path", b"foo")
108			.build();
109		let bytes = m.clone().into_bytes();
110		let m2 = Moniker::from_bytes(bytes).unwrap();
111		assert_eq!(m, m2);
112		assert!(Moniker::from_bytes(vec![99u8; 5]).is_err());
113	}
114
115	use proptest::prelude::*;
116
117	proptest! {
118		#![proptest_config(ProptestConfig {
119			cases: 256,
120			..ProptestConfig::default()
121		})]
122
123		#[test]
124		fn moniker_from_bytes_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..4096)) {
125			if let Ok(m) = Moniker::from_bytes(bytes.clone()) {
126				prop_assert_eq!(m.as_bytes(), bytes.as_slice());
127				let m2 = Moniker::from_bytes(m.as_bytes().to_vec())
128					.expect("validated bytes must re-parse");
129				prop_assert_eq!(m, m2);
130			}
131		}
132
133		#[test]
134		fn moniker_view_and_owned_agree(bytes in proptest::collection::vec(any::<u8>(), 0..4096)) {
135			let owned = Moniker::from_bytes(bytes.clone()).is_ok();
136			let view = MonikerView::from_bytes(&bytes).is_ok();
137			prop_assert_eq!(owned, view);
138		}
139	}
140}