Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Jan 13, 2024
1 parent 8551715 commit 83c0969
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 73 deletions.
6 changes: 5 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ impl UsageErr {

#[macro_export]
macro_rules! bail_parse {
($span:expr, $fmt:literal$(,$arg:expr)*) => {{
($span:expr, $fmt:literal) => {{
let msg = format!($fmt);
return std::result::Result::Err(UsageErr::new(msg, $span.span()));
}};
($span:expr, $fmt:literal, $($arg:tt)*) => {{
let msg = format!($fmt, $($arg)*);
return std::result::Result::Err(UsageErr::new(msg, $span.span()));
}};
Expand Down
32 changes: 14 additions & 18 deletions src/parse/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use itertools::Itertools;
use kdl::{KdlEntry, KdlNode};

use crate::error::UsageErr;
use crate::parse::helpers::NodeHelper;

#[derive(Debug, Default)]
pub struct Arg {
Expand Down Expand Up @@ -65,24 +66,19 @@ impl From<&Arg> for KdlNode {
impl TryFrom<&KdlNode> for Arg {
type Error = UsageErr;
fn try_from(node: &KdlNode) -> Result<Self, UsageErr> {
let mut arg: Arg = node
.entries()
.first()
.and_then(|e| e.value().as_string())
.map(|s| s.parse())
.transpose()?
.unwrap_or_default();
for entry in node.entries().iter().skip(1) {
match entry.name().unwrap().to_string().as_str() {
"help" => arg.help = entry.value().as_string().map(|s| s.to_string()),
"long_help" => arg.long_help = entry.value().as_string().map(|s| s.to_string()),
"required" => arg.required = entry.value().as_bool().unwrap(),
"var" => arg.var = entry.value().as_bool().unwrap(),
"hide" => arg.hide = entry.value().as_bool().unwrap(),
"var_min" => arg.var_min = entry.value().as_i64(),
"var_max" => arg.var_max = entry.value().as_i64(),
"default" => arg.default = entry.value().as_string().map(|s| s.to_string()),
_ => Err(UsageErr::new(entry.to_string(), entry.span()))?,
let hnode: NodeHelper = node.into();
let mut arg: Arg = hnode.arg(0)?.ensure_string()?.parse()?;
for (k, v) in hnode.props() {
match k {
"help" => arg.help = Some(v.ensure_string()?),
"long_help" => arg.long_help = Some(v.ensure_string()?),
"required" => arg.required = v.ensure_bool()?,
"var" => arg.var = v.ensure_bool()?,
"hide" => arg.hide = v.ensure_bool()?,
"var_min" => arg.var_min = v.ensure_i64().map(Some)?,
"var_max" => arg.var_max = v.ensure_i64().map(Some)?,
"default" => arg.default = v.ensure_string().map(Some)?,
k => bail_parse!(v.entry, "unsupported key {k}"),
}
}
Ok(arg)
Expand Down
57 changes: 28 additions & 29 deletions src/parse/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::UsageErr;
use crate::parse::helpers::NodeHelper;
use crate::{Arg, Flag};
use indexmap::IndexMap;
use kdl::{KdlDocument, KdlEntry, KdlNode};
Expand Down Expand Up @@ -68,58 +69,56 @@ impl From<&SchemaCmd> for KdlNode {
impl TryFrom<&KdlNode> for SchemaCmd {
type Error = UsageErr;
fn try_from(node: &KdlNode) -> Result<Self, UsageErr> {
let hnode: NodeHelper = node.into();
hnode.ensure_args_count(1, 1)?;
let mut cmd = Self {
name: node
.entries()
.first()
.expect("no name provided")
.value()
.as_string()
.unwrap()
.to_string(),
name: hnode.arg(0)?.ensure_string()?.to_string(),
..Default::default()
};
for entry in node.entries().iter().skip(1) {
match entry.name().unwrap().to_string().as_str() {
"help" => cmd.help = entry.value().as_string().map(|s| s.to_string()),
"long_help" => cmd.long_help = entry.value().as_string().map(|s| s.to_string()),
"before_help" => cmd.before_help = entry.value().as_string().map(|s| s.to_string()),
"before_long_help" => {
cmd.before_long_help = entry.value().as_string().map(|s| s.to_string())
}
"after_help" => cmd.after_help = entry.value().as_string().map(|s| s.to_string()),
for (k, v) in hnode.props() {
match k {
"help" => cmd.help = Some(v.ensure_string()?),
"long_help" => cmd.long_help = Some(v.ensure_string()?),
"before_help" => cmd.before_help = Some(v.ensure_string()?),
"before_long_help" => cmd.before_long_help = Some(v.ensure_string()?),
"after_help" => cmd.after_help = Some(v.ensure_string()?),
"after_long_help" => {
cmd.after_long_help = entry.value().as_string().map(|s| s.to_string())
cmd.after_long_help = Some(v.ensure_string()?);
}
"subcommand_required" => cmd.subcommand_required = entry.value().as_bool().unwrap(),
"hide" => cmd.hide = entry.value().as_bool().unwrap(),
_ => Err(UsageErr::new(entry.to_string(), entry.span()))?,
"subcommand_required" => cmd.subcommand_required = v.ensure_bool()?,
"hide" => cmd.hide = v.ensure_bool()?,
k => bail_parse!(node, "unsupported key {k}"),
}
}
for child in node.children().map(|c| c.nodes()).unwrap_or_default() {
match child.name().to_string().as_str() {
"flag" => cmd.flags.push(child.try_into()?),
"arg" => cmd.args.push(child.try_into()?),
let child: NodeHelper = child.into();
match child.name() {
"flag" => cmd.flags.push(child.node.try_into()?),
"arg" => cmd.args.push(child.node.try_into()?),
"cmd" => {
let node: SchemaCmd = child.try_into()?;
let node: SchemaCmd = child.node.try_into()?;
cmd.subcommands.insert(node.name.to_string(), node);
}
"alias" => {
let alias = child
.node
.entries()
.iter()
.filter_map(|e| e.value().as_string().map(|v| v.to_string()))
.collect::<Vec<_>>();
if child
let hide = child
.props()
.get("hide")
.is_some_and(|n| n.value().as_bool().unwrap())
{
.map(|n| n.ensure_bool())
.transpose()?
.unwrap_or(false);
if hide {
cmd.hidden_aliases.extend(alias);
} else {
cmd.aliases.extend(alias);
}
}
_ => Err(UsageErr::new(child.to_string(), child.span()))?,
k => bail_parse!(child.node, "unsupported key {k}"),
}
}
Ok(cmd)
Expand Down
1 change: 1 addition & 0 deletions src/parse/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl TryFrom<&KdlNode> for SpecConfig {
if let Some(children) = doc.children().map(|doc| doc.nodes()) {
for node in children {
let ph = NodeHelper::new(node);
ph.ensure_args_count(1, 1)?;
match ph.name() {
"prop" => {
let key = ph.arg(0)?;
Expand Down
37 changes: 17 additions & 20 deletions src/parse/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use kdl::{KdlDocument, KdlEntry, KdlNode};

use crate::error::UsageErr;
use crate::error::UsageErr::InvalidFlag;
use crate::parse::helpers::NodeHelper;
use crate::{bail_parse, Arg};

#[derive(Debug, Default)]
Expand Down Expand Up @@ -81,30 +82,26 @@ impl From<&Flag> for KdlNode {
impl TryFrom<&KdlNode> for Flag {
type Error = UsageErr;
fn try_from(node: &KdlNode) -> Result<Self, UsageErr> {
let mut flag: Self = node
.entries()
.first()
.and_then(|e| e.value().as_string())
.map(|s| s.parse())
.transpose()?
.unwrap_or_default();
for entry in node.entries().iter().skip(1) {
match entry.name().unwrap().to_string().as_str() {
"help" => flag.help = entry.value().as_string().map(|s| s.to_string()),
"long_help" => flag.long_help = entry.value().as_string().map(|s| s.to_string()),
"required" => flag.required = entry.value().as_bool().unwrap(),
"var" => flag.var = entry.value().as_bool().unwrap(),
"hide" => flag.hide = entry.value().as_bool().unwrap(),
"global" => flag.global = entry.value().as_bool().unwrap(),
"count" => flag.count = entry.value().as_bool().unwrap(),
k => bail_parse!(entry, "unsupported key {k}"),
let hnode: NodeHelper = node.into();
let mut flag: Self = hnode.arg(0)?.ensure_string()?.parse()?;
for (k, v) in hnode.props() {
match k {
"help" => flag.help = Some(v.ensure_string()?),
"long_help" => flag.long_help = Some(v.ensure_string()?),
"required" => flag.required = v.ensure_bool()?,
"var" => flag.var = v.ensure_bool()?,
"hide" => flag.hide = v.ensure_bool()?,
"global" => flag.global = v.ensure_bool()?,
"count" => flag.count = v.ensure_bool()?,
k => bail_parse!(v.entry, "unsupported key {k}"),
}
}
let children = node.children().map(|c| c.nodes()).unwrap_or_default();
for child in children {
match child.name().to_string().as_str() {
"arg" => flag.arg = Some(child.try_into()?),
k => bail_parse!(child, "unsupported key {k}"),
let child: NodeHelper = child.into();
match child.name() {
"arg" => flag.arg = Some(child.node.try_into()?),
k => bail_parse!(child.node, "unsupported key {k}"),
}
}
Ok(flag)
Expand Down
46 changes: 41 additions & 5 deletions src/parse/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::error::UsageErr;
use indexmap::IndexMap;
use kdl::{KdlEntry, KdlNode, KdlValue};

use crate::error::UsageErr;

#[derive(Debug)]
pub struct NodeHelper<'a> {
pub(crate) node: &'a KdlNode,
Expand All @@ -15,7 +16,24 @@ impl<'a> NodeHelper<'a> {
pub(crate) fn name(&self) -> &str {
self.node.name().value()
}

pub(crate) fn ensure_args_count(&self, min: usize, max: usize) -> Result<(), UsageErr> {
let count = self
.node
.entries()
.into_iter()
.filter(|e| e.name().is_none())
.count();
if count < min || count > max {
bail_parse!(
self.node,
"expected {} to {} arguments, got {}",
min,
max,
count
)
}
Ok(())
}
pub(crate) fn arg(&self, i: usize) -> Result<ParseEntry, UsageErr> {
if let Some(entry) = self.node.entries().get(i) {
if entry.name().is_some() {
Expand All @@ -25,7 +43,6 @@ impl<'a> NodeHelper<'a> {
}
bail_parse!(self.node, "missing argument")
}

pub(crate) fn props(&self) -> IndexMap<&str, ParseEntry> {
self.node
.entries()
Expand Down Expand Up @@ -60,9 +77,28 @@ impl<'a> From<&'a KdlEntry> for ParseEntry<'a> {
}

impl<'a> ParseEntry<'a> {
pub fn ensure_string(&self) -> Result<&str, UsageErr> {
pub fn ensure_i64(&self) -> Result<i64, UsageErr> {
match self.value.as_i64() {
Some(i) => Ok(i),
None => bail_parse!(self.entry, "expected integer"),
}
}
#[allow(dead_code)]
pub fn ensure_f64(&self) -> Result<f64, UsageErr> {
match self.value.as_f64() {
Some(f) => Ok(f),
None => bail_parse!(self.entry, "expected float"),
}
}
pub fn ensure_bool(&self) -> Result<bool, UsageErr> {
match self.value.as_bool() {
Some(b) => Ok(b),
None => bail_parse!(self.entry, "expected bool"),
}
}
pub fn ensure_string(&self) -> Result<String, UsageErr> {
match self.value.as_string() {
Some(s) => Ok(s),
Some(s) => Ok(s.to_string()),
None => bail_parse!(self.entry, "expected string"),
}
}
Expand Down

0 comments on commit 83c0969

Please sign in to comment.