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)]
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}