Skip to main content

code_moniker_core/core/moniker/
builder.rs

1use super::encoding::{HEADER_FIXED_LEN, VERSION, write_u16};
2use super::{Moniker, MonikerView};
3
4const MAX_COMPONENT_LEN: usize = u16::MAX as usize;
5
6#[derive(Default, Debug)]
7pub struct MonikerBuilder {
8	project: Vec<u8>,
9	segments: Vec<(Vec<u8>, Vec<u8>)>,
10}
11
12impl MonikerBuilder {
13	pub fn new() -> Self {
14		Self::default()
15	}
16
17	pub fn from_view(view: MonikerView<'_>) -> Self {
18		let mut b = Self::new();
19		b.project(view.project());
20		for seg in view.segments() {
21			b.segment(seg.kind, seg.name);
22		}
23		b
24	}
25
26	pub fn truncate(&mut self, len: usize) {
27		self.segments.truncate(len);
28	}
29
30	pub fn project(&mut self, project: &[u8]) -> &mut Self {
31		self.project.clear();
32		self.project.extend_from_slice(project);
33		self
34	}
35
36	pub fn segment(&mut self, kind: &[u8], name: &[u8]) -> &mut Self {
37		self.segments.push((kind.to_vec(), name.to_vec()));
38		self
39	}
40
41	pub fn build(&self) -> Moniker {
42		assert!(
43			self.project.len() <= MAX_COMPONENT_LEN,
44			"moniker project longer than u16::MAX bytes ({})",
45			self.project.len()
46		);
47		let mut buf = Vec::with_capacity(self.estimated_size());
48		buf.push(VERSION);
49		write_u16(&mut buf, self.project.len() as u16);
50		buf.extend_from_slice(&self.project);
51		for (kind, name) in &self.segments {
52			assert!(
53				kind.len() <= MAX_COMPONENT_LEN,
54				"moniker segment kind longer than u16::MAX bytes ({})",
55				kind.len()
56			);
57			assert!(
58				name.len() <= MAX_COMPONENT_LEN,
59				"moniker segment name longer than u16::MAX bytes ({})",
60				name.len()
61			);
62			write_u16(&mut buf, kind.len() as u16);
63			buf.extend_from_slice(kind);
64			write_u16(&mut buf, name.len() as u16);
65			buf.extend_from_slice(name);
66		}
67		Moniker::from_canonical_bytes(buf)
68	}
69
70	fn estimated_size(&self) -> usize {
71		HEADER_FIXED_LEN
72			+ self.project.len()
73			+ self
74				.segments
75				.iter()
76				.map(|(k, n)| 2 + k.len() + 2 + n.len())
77				.sum::<usize>()
78	}
79}
80
81#[cfg(test)]
82mod tests {
83	use super::super::Segment;
84	use super::*;
85
86	#[test]
87	fn builder_empty() {
88		let m = MonikerBuilder::new().build();
89		let v = m.as_view();
90		assert_eq!(v.project(), b"");
91		assert_eq!(v.segment_count(), 0);
92		assert_eq!(v.segments().count(), 0);
93	}
94
95	#[test]
96	fn builder_with_project_no_segments() {
97		let m = MonikerBuilder::new().project(b"my-app").build();
98		let v = m.as_view();
99		assert_eq!(v.project(), b"my-app");
100		assert_eq!(v.segment_count(), 0);
101	}
102
103	#[test]
104	fn builder_with_segments() {
105		let m = MonikerBuilder::new()
106			.project(b"my-app")
107			.segment(b"module", b"main")
108			.segment(b"package", b"com")
109			.segment(b"package", b"acme")
110			.segment(b"class", b"Foo")
111			.build();
112		let v = m.as_view();
113		assert_eq!(v.segment_count(), 4);
114		let segs: Vec<_> = v.segments().collect();
115		assert_eq!(
116			segs[0],
117			Segment {
118				kind: b"module",
119				name: b"main"
120			}
121		);
122		assert_eq!(
123			segs[3],
124			Segment {
125				kind: b"class",
126				name: b"Foo"
127			}
128		);
129	}
130
131	#[test]
132	fn builder_method_with_arity_in_name() {
133		let m = MonikerBuilder::new()
134			.project(b"app")
135			.segment(b"class", b"Foo")
136			.segment(b"method", b"bar()")
137			.segment(b"method", b"bar(2)")
138			.build();
139		let segs: Vec<_> = m.as_view().segments().collect();
140		assert_eq!(segs[1].name, b"bar()");
141		assert_eq!(segs[2].name, b"bar(2)");
142	}
143
144	#[test]
145	fn builder_accepts_max_length_component() {
146		let big = vec![b'a'; MAX_COMPONENT_LEN];
147		let m = MonikerBuilder::new()
148			.project(&big)
149			.segment(b"path", &big)
150			.build();
151		let v = m.as_view();
152		assert_eq!(v.project().len(), MAX_COMPONENT_LEN);
153		let seg = v.segments().next().unwrap();
154		assert_eq!(seg.name.len(), MAX_COMPONENT_LEN);
155	}
156
157	#[test]
158	#[should_panic(expected = "moniker project longer than u16::MAX bytes")]
159	fn builder_panics_on_oversized_project() {
160		let oversize = vec![b'a'; MAX_COMPONENT_LEN + 1];
161		MonikerBuilder::new().project(&oversize).build();
162	}
163
164	#[test]
165	#[should_panic(expected = "moniker segment kind longer than u16::MAX bytes")]
166	fn builder_panics_on_oversized_segment_kind() {
167		let oversize = vec![b'a'; MAX_COMPONENT_LEN + 1];
168		MonikerBuilder::new()
169			.project(b"app")
170			.segment(&oversize, b"x")
171			.build();
172	}
173
174	#[test]
175	#[should_panic(expected = "moniker segment name longer than u16::MAX bytes")]
176	fn builder_panics_on_oversized_segment_name() {
177		let oversize = vec![b'a'; MAX_COMPONENT_LEN + 1];
178		MonikerBuilder::new()
179			.project(b"app")
180			.segment(b"path", &oversize)
181			.build();
182	}
183}