1pub type FsString = bstr::BString;
6pub type FsStr = bstr::BStr;
7
8pub const SEPARATOR: u8 = b'/';
9
10pub struct PathBuilder {
12 data: FsString,
14 pos: usize,
15}
16
17impl PathBuilder {
18 const INITIAL_CAPACITY: usize = 32;
19
20 pub fn new() -> Self {
21 Self { data: Default::default(), pos: 0 }
22 }
23
24 pub fn prepend_element(&mut self, element: &FsStr) {
25 self.ensure_capacity(element.len() + 1);
26 let old_pos = self.pos;
27 self.pos -= element.len() + 1;
28 self.data[self.pos + 1..old_pos].copy_from_slice(element);
29 self.data[self.pos] = b'/';
30 }
31
32 pub fn build_absolute(mut self) -> FsString {
34 if self.pos == self.data.len() {
35 return "/".into();
36 }
37 self.data.drain(..self.pos);
38 self.data
39 }
40
41 pub fn build_relative(self) -> FsString {
43 let mut absolute = self.build_absolute();
44 absolute.remove(0);
46 absolute
47 }
48
49 fn ensure_capacity(&mut self, capacity_needed: usize) {
50 if capacity_needed > self.pos {
51 let current_size = self.data.len();
52 let len = current_size - self.pos;
53 let min_size = len + capacity_needed;
54 let mut new_size = std::cmp::max(current_size * 2, Self::INITIAL_CAPACITY);
55 while new_size < min_size {
56 new_size *= 2;
57 }
58 self.data.reserve(new_size - current_size);
59 self.data.resize(new_size - len, 0);
60 self.data.extend_from_within(self.pos..(self.pos + len));
61 self.pos = new_size - len;
62 }
63 }
64}
65
66#[cfg(test)]
67mod test {
68 use super::*;
69
70 #[::fuchsia::test]
71 fn test_path_builder() {
72 let p = PathBuilder::new();
73 assert_eq!(p.build_absolute(), "/");
74
75 let p = PathBuilder::new();
76 assert_eq!(p.build_relative(), "");
77
78 let mut p = PathBuilder::new();
79 p.prepend_element("foo".into());
80 assert_eq!(p.build_absolute(), "/foo");
81
82 let mut p = PathBuilder::new();
83 p.prepend_element("foo".into());
84 assert_eq!(p.build_relative(), "foo");
85
86 let mut p = PathBuilder::new();
87 p.prepend_element("foo".into());
88 p.prepend_element("bar".into());
89 assert_eq!(p.build_absolute(), "/bar/foo");
90
91 let mut p = PathBuilder::new();
92 p.prepend_element("foo".into());
93 p.prepend_element("1234567890123456789012345678901234567890".into());
94 p.prepend_element("bar".into());
95 assert_eq!(p.build_absolute(), "/bar/1234567890123456789012345678901234567890/foo");
96 }
97}