Skip to main content

preftool_clap/
context.rs

1#[derive(Clone, Debug)]
2enum PathList<'a> {
3  Nil,
4  Node {
5    path: &'a str,
6    rest: &'a PathList<'a>,
7    total_length: usize,
8    node_count: usize,
9  },
10}
11
12impl<'a> PathList<'a> {
13  pub fn node_count(&self) -> usize {
14    match self {
15      PathList::Nil => 0,
16      PathList::Node {
17        total_length: l, ..
18      } => *l,
19    }
20  }
21
22  pub fn total_len(&self) -> usize {
23    match self {
24      PathList::Nil => 0,
25      PathList::Node { node_count: c, .. } => *c,
26    }
27  }
28
29  fn first(&self) -> &'a str {
30    match self {
31      PathList::Nil => panic!("No nodes in path list"),
32      PathList::Node { path: p, .. } => p,
33    }
34  }
35
36  pub fn iter(&self) -> ::std::vec::IntoIter<&'a str> {
37    let len = self.node_count();
38    let mut vec = Vec::with_capacity(len);
39    let mut node = self;
40
41    while let PathList::Node {
42      path: p, rest: r, ..
43    } = *node
44    {
45      vec.push(p);
46      node = r;
47    }
48
49    vec.reverse();
50    vec.into_iter()
51  }
52}
53
54bitflags! {
55  struct Flags: u32 {
56    const NONE = 0;
57    const LIST = 1 << 0;
58    const OPTIONAL = 1 << 1;
59    const VARIANT = 1 << 2;
60  }
61}
62
63#[derive(Clone, Debug)]
64pub struct Context<'a> {
65  path: PathList<'a>,
66  flags: Flags,
67}
68
69impl<'a> Default for Context<'a> {
70  fn default() -> Self {
71    Self::new()
72  }
73}
74
75impl<'a> Context<'a> {
76  pub fn new() -> Self {
77    Context {
78      path: PathList::Nil,
79      flags: Flags::NONE,
80    }
81  }
82
83  pub fn child<'b>(&'a self, path: &'b str) -> Context<'b>
84  where
85    'a: 'b,
86  {
87    let len = path.len();
88    Context {
89      path: PathList::Node {
90        path,
91        rest: &self.path,
92        total_length: self.path.total_len() + len,
93        node_count: self.path.node_count() + 1,
94      },
95      flags: self.flags,
96    }
97  }
98
99  pub fn list(self) -> Self {
100    Context {
101      path: self.path,
102      flags: self.flags | Flags::LIST,
103    }
104  }
105
106  pub fn optional(self) -> Self {
107    Context {
108      path: self.path,
109      flags: self.flags | Flags::OPTIONAL,
110    }
111  }
112
113  pub fn variant(self) -> Self {
114    Context {
115      path: self.path,
116      flags: self.flags | Flags::VARIANT,
117    }
118  }
119
120  pub fn is_list(&self) -> bool {
121    self.flags.contains(Flags::LIST)
122  }
123
124  pub fn is_optional(&self) -> bool {
125    self.flags.contains(Flags::OPTIONAL)
126  }
127
128  pub fn is_variant(&self) -> bool {
129    self.flags.contains(Flags::VARIANT)
130  }
131
132  pub fn join_path(&self, separator: &str) -> String {
133    let node_count = self.path.node_count();
134    if node_count == 0 {
135      "".to_owned()
136    } else if node_count == 1 {
137      self.path.first().to_owned()
138    } else {
139      let path_str_len = self.path.total_len();
140      let mut ret = String::with_capacity(path_str_len + (node_count - 1) * separator.len());
141      let mut first = true;
142      for chunk in self.path.iter() {
143        if first {
144          first = false;
145        } else {
146          ret.push_str(separator);
147        }
148
149        ret.push_str(chunk);
150      }
151
152      ret
153    }
154  }
155}