#[derive(Clone, Debug)]
enum PathList<'a> {
Nil,
Node {
path: &'a str,
rest: &'a PathList<'a>,
total_length: usize,
node_count: usize,
},
}
impl<'a> PathList<'a> {
pub fn node_count(&self) -> usize {
match self {
PathList::Nil => 0,
PathList::Node {
total_length: l, ..
} => *l,
}
}
pub fn total_len(&self) -> usize {
match self {
PathList::Nil => 0,
PathList::Node { node_count: c, .. } => *c,
}
}
fn first(&self) -> &'a str {
match self {
PathList::Nil => panic!("No nodes in path list"),
PathList::Node { path: p, .. } => p,
}
}
pub fn iter(&self) -> ::std::vec::IntoIter<&'a str> {
let len = self.node_count();
let mut vec = Vec::with_capacity(len);
let mut node = self;
while let PathList::Node {
path: p, rest: r, ..
} = *node
{
vec.push(p);
node = r;
}
vec.reverse();
vec.into_iter()
}
}
bitflags! {
struct Flags: u32 {
const NONE = 0;
const LIST = 1 << 0;
const OPTIONAL = 1 << 1;
const VARIANT = 1 << 2;
}
}
#[derive(Clone, Debug)]
pub struct Context<'a> {
path: PathList<'a>,
flags: Flags,
}
impl<'a> Default for Context<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Context<'a> {
pub fn new() -> Self {
Context {
path: PathList::Nil,
flags: Flags::NONE,
}
}
pub fn child<'b>(&'a self, path: &'b str) -> Context<'b>
where
'a: 'b,
{
let len = path.len();
Context {
path: PathList::Node {
path,
rest: &self.path,
total_length: self.path.total_len() + len,
node_count: self.path.node_count() + 1,
},
flags: self.flags,
}
}
pub fn list(self) -> Self {
Context {
path: self.path,
flags: self.flags | Flags::LIST,
}
}
pub fn optional(self) -> Self {
Context {
path: self.path,
flags: self.flags | Flags::OPTIONAL,
}
}
pub fn variant(self) -> Self {
Context {
path: self.path,
flags: self.flags | Flags::VARIANT,
}
}
pub fn is_list(&self) -> bool {
self.flags.contains(Flags::LIST)
}
pub fn is_optional(&self) -> bool {
self.flags.contains(Flags::OPTIONAL)
}
pub fn is_variant(&self) -> bool {
self.flags.contains(Flags::VARIANT)
}
pub fn join_path(&self, separator: &str) -> String {
let node_count = self.path.node_count();
if node_count == 0 {
"".to_owned()
} else if node_count == 1 {
self.path.first().to_owned()
} else {
let path_str_len = self.path.total_len();
let mut ret = String::with_capacity(path_str_len + (node_count - 1) * separator.len());
let mut first = true;
for chunk in self.path.iter() {
if first {
first = false;
} else {
ret.push_str(separator);
}
ret.push_str(chunk);
}
ret
}
}
}