preftool-clap 0.2.0

Configuration library for CLI tools/servers.
Documentation
#[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
    }
  }
}