code_moniker_core/core/moniker/
view.rs1use super::encoding::{EncodingError, HEADER_FIXED_LEN, VERSION, read_u16};
2
3#[derive(Copy, Clone, Eq, PartialEq, Debug)]
4pub struct Segment<'a> {
5 pub kind: &'a [u8],
6 pub name: &'a [u8],
7}
8
9#[derive(Copy, Clone, Debug)]
10pub struct MonikerView<'a> {
11 bytes: &'a [u8],
12 project_off: usize,
13 project_len: usize,
14 segs_off: usize,
15}
16
17impl<'a> MonikerView<'a> {
18 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, EncodingError> {
19 if bytes.len() < HEADER_FIXED_LEN {
20 return Err(EncodingError::Truncated);
21 }
22 let version = bytes[0];
23 if version != VERSION {
24 return Err(EncodingError::UnknownVersion(version));
25 }
26 let project_len = read_u16(bytes, 1) as usize;
27 let project_off = 3;
28 let segs_off = project_off + project_len;
29 if bytes.len() < segs_off {
30 return Err(EncodingError::ProjectOverflow);
31 }
32 let mut cursor = segs_off;
33 while cursor < bytes.len() {
34 if bytes.len() < cursor + 2 {
35 return Err(EncodingError::SegmentOverflow);
36 }
37 let kind_len = read_u16(bytes, cursor) as usize;
38 cursor += 2 + kind_len;
39 if bytes.len() < cursor + 2 {
40 return Err(EncodingError::SegmentOverflow);
41 }
42 let name_len = read_u16(bytes, cursor) as usize;
43 cursor += 2 + name_len;
44 if bytes.len() < cursor {
45 return Err(EncodingError::SegmentOverflow);
46 }
47 }
48 Ok(Self {
49 bytes,
50 project_off,
51 project_len,
52 segs_off,
53 })
54 }
55
56 #[allow(clippy::missing_safety_doc)]
57 pub unsafe fn from_canonical_bytes(bytes: &'a [u8]) -> Self {
58 debug_assert!(bytes.len() >= HEADER_FIXED_LEN && bytes[0] == VERSION);
59 let project_len = read_u16(bytes, 1) as usize;
60 let project_off = 3;
61 let segs_off = project_off + project_len;
62 Self {
63 bytes,
64 project_off,
65 project_len,
66 segs_off,
67 }
68 }
69
70 pub fn project(&self) -> &'a [u8] {
71 &self.bytes[self.project_off..self.project_off + self.project_len]
72 }
73
74 pub fn segment_count(&self) -> u16 {
75 self.segments().count() as u16
76 }
77
78 pub fn segments(&self) -> SegmentIter<'a> {
79 SegmentIter {
80 bytes: self.bytes,
81 cursor: self.segs_off,
82 }
83 }
84
85 pub fn as_bytes(&self) -> &'a [u8] {
86 self.bytes
87 }
88
89 pub fn is_ancestor_of(&self, other: &MonikerView<'_>) -> bool {
90 if self.project() != other.project() {
91 return false;
92 }
93 other.bytes.starts_with(self.bytes)
94 }
95}
96
97#[derive(Clone, Debug)]
98pub struct SegmentIter<'a> {
99 bytes: &'a [u8],
100 cursor: usize,
101}
102
103impl<'a> Iterator for SegmentIter<'a> {
104 type Item = Segment<'a>;
105
106 fn next(&mut self) -> Option<Self::Item> {
107 if self.cursor >= self.bytes.len() {
108 return None;
109 }
110 let kind_len = read_u16(self.bytes, self.cursor) as usize;
111 let kind_start = self.cursor + 2;
112 let kind = &self.bytes[kind_start..kind_start + kind_len];
113 let name_len_off = kind_start + kind_len;
114 let name_len = read_u16(self.bytes, name_len_off) as usize;
115 let name_start = name_len_off + 2;
116 let name = &self.bytes[name_start..name_start + name_len];
117 self.cursor = name_start + name_len;
118 Some(Segment { kind, name })
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn view_rejects_truncated_buffer() {
128 assert_eq!(
129 MonikerView::from_bytes(&[2, 0]).unwrap_err(),
130 EncodingError::Truncated
131 );
132 }
133
134 #[test]
135 fn view_rejects_unknown_version() {
136 let buf = [99, 0, 0];
137 assert_eq!(
138 MonikerView::from_bytes(&buf).unwrap_err(),
139 EncodingError::UnknownVersion(99)
140 );
141 }
142
143 #[test]
144 fn view_rejects_project_overflow() {
145 let buf: Vec<u8> = vec![2, 10, 0, 0];
146 assert_eq!(
147 MonikerView::from_bytes(&buf).unwrap_err(),
148 EncodingError::ProjectOverflow
149 );
150 }
151
152 #[test]
153 fn view_rejects_segment_overflow() {
154 let buf: Vec<u8> = vec![2, 0, 0, 5, 0];
155 assert_eq!(
156 MonikerView::from_bytes(&buf).unwrap_err(),
157 EncodingError::SegmentOverflow
158 );
159 }
160
161 #[test]
162 fn view_accepts_project_only() {
163 let buf: Vec<u8> = vec![2, 3, 0, b'a', b'p', b'p'];
164 let v = MonikerView::from_bytes(&buf).unwrap();
165 assert_eq!(v.project(), b"app");
166 assert_eq!(v.segment_count(), 0);
167 }
168}