Skip to main content

code_moniker_core/core/moniker/
view.rs

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