Skip to content

Commit

Permalink
work
Browse files Browse the repository at this point in the history
  • Loading branch information
Magicolo committed Feb 28, 2024
1 parent 5fb1a03 commit df37d6e
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 81 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rust-version = "1.70.0"

[dependencies]
termion = "3.0.0"
orn = "0.4.1"
orn = "0.4.2"
regex = "1.10.3"

[dev-dependencies]
Expand Down
23 changes: 17 additions & 6 deletions examples/scalp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ pub enum Command {
Fett { jango: Option<Jango> },
}

#[derive(Debug)]
pub struct Root {
pub commands: Vec<Command>,
pub kroule: String,
}

fn main() -> Result<(), Box<dyn error::Error>> {
let commands = Parser::builder()
let root = Parser::builder()
.pipe(header!())
.usage("Usage: scalp [OPTIONS]")
.group(|group| {
group
.verb(|verb| {
Expand All @@ -41,11 +46,17 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.require_with("command")
})
.line()
.group(|group| group.name("Options:").options(Options::all(true, true)))
.map(|(commands, _)| commands)
.group(|group| {
group
.name("Options:")
.option(|option| option.name("kroule").require())
.options(Options::all(true, true))
})
.map(|(commands, (kroule,))| Root { commands, kroule })
.note("A note.")
.build()?
.parse()?;
println!("{:?}", commands);
.parse_with(["fett", "--help"], [("", "")])?;
// .parse()?;
println!("{:?}", root);
Ok(())
}
4 changes: 2 additions & 2 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,8 @@ impl<S: Scope, P> Builder<S, P> {
P: Parse<Value = Option<T>>,
{
let format = self.convert(format);
self.meta(Meta::Require)
.map_parse(|parse| Require(parse, format))
self.meta(Meta::Require(format))
.map_parse(|parse| Require(parse))
}

pub fn help(self, help: impl Into<Cow<'static, str>>) -> Self {
Expand Down
7 changes: 5 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub enum Error {
License(Option<String>),

MissingOptionValue(Option<Cow<'static, str>>, Vec<Key>),
MissingRequiredValue(Vec<Key>, Option<Key>, Cow<'static, str>),
MissingRequiredValue(Vec<Key>, Option<Key>, Option<Cow<'static, str>>),
DuplicateOption(Vec<Key>),
UnrecognizedArgument(Cow<'static, str>, Vec<(Cow<'static, str>, usize)>),
ExcessArguments(VecDeque<Cow<'static, str>>),
Expand Down Expand Up @@ -108,7 +108,10 @@ impl fmt::Display for Error {
write!(f, ".")?;
}
Error::MissingRequiredValue(path, name, type_name) => {
write!(f, "Missing required value of type '{type_name}'")?;
write!(f, "Missing required value")?;
if let Some(type_name) = type_name {
write!(f, " of type '{type_name}'")?;
}
write_join(f, " for ", "", " ", path.iter().chain(name))?;
write!(f, ".")?;
}
Expand Down
94 changes: 79 additions & 15 deletions src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ use core::{
mem::{replace, take},
slice::from_ref,
};
use std::{borrow::Cow, fs, ops::Deref};
use std::{
borrow::Cow,
fs,
ops::{ControlFlow, Deref},
};

struct Helper<'a, S: Style + ?Sized> {
buffer: &'a mut String,
Expand Down Expand Up @@ -255,24 +259,78 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {

fn usage(
&mut self,
root: &Meta,
metas: &[Meta],
prefix: impl Format,
suffix: impl Format,
) -> Result<usize, fmt::Error> {
self.join(metas, prefix, suffix, " ", |meta| match meta {
let mut width = self.join(metas, &prefix, &suffix, " ", |meta| match meta {
Meta::Usage(value) => Some(Cow::Borrowed(value)),
_ => None,
})
})?;
if width == 0 {
width += self.write(prefix)?;
width += self.write("Usage: ")?;
let mut has = false;
for key in root.key().as_ref().into_iter().chain(self.path) {
if replace(&mut has, true) {
width += self.write(" ")?;
}
width += self.write(key)?;
}

match Meta::descend(
metas,
(),
false,
usize::MAX,
|state, meta| match meta {
Meta::Option(_) => ControlFlow::Break(self.write(" [OPTIONS]")),
_ => ControlFlow::Continue(state),
},
|state, _| ControlFlow::Continue(state),
) {
ControlFlow::Break(result) => width += result?,
ControlFlow::Continue(_) => {}
}

fn control(result: Result<usize, fmt::Error>) -> ControlFlow<fmt::Error, usize> {
result.map_or_else(ControlFlow::Break, ControlFlow::Continue)
}

match Meta::descend(
metas,
(),
false,
1,
|state, _| ControlFlow::Continue(state),
|state, meta| match meta {
Meta::Require(value) => {
width += control(self.write((' ', '<', value, '>')))?;
ControlFlow::Continue(state)
}
_ => ControlFlow::Continue(state),
},
) {
ControlFlow::Break(error) => Err(error),
ControlFlow::Continue(_) => Ok(width + self.write(suffix)?),
}
} else {
Ok(width)
}
}

fn columns(&self, metas: &[Meta], depth: usize) -> Columns {
let (mut short, mut long) = (false, false);
let mut columns = Columns::default();
for meta in Meta::visible(metas) {
match meta {
Meta::Position(_) if depth == 0 => {
Meta::Position(position) if *position < 10 && depth == 0 => {
columns.short += 3 + if replace(&mut short, true) { 2 } else { 0 }
}
Meta::Position(_) if depth == 0 => {
columns.short += 4 + if replace(&mut short, true) { 2 } else { 0 }
}
Meta::Name(Name::Short, value) if depth == 0 => {
columns.short += value.len() + if replace(&mut short, true) { 2 } else { 0 }
}
Expand Down Expand Up @@ -303,7 +361,7 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {

fn tags(&mut self, metas: &[Meta]) -> Result<usize, fmt::Error> {
let mut width = self.join(metas, "", "", ", ", |meta| match meta {
Meta::Require => Some(Cow::Borrowed("require")),
Meta::Require(_) => Some(Cow::Borrowed("require")),
Meta::Swizzle => Some(Cow::Borrowed("swizzle")),
Meta::Many(_) => Some(Cow::Borrowed("many")),
_ => None,
Expand All @@ -322,7 +380,7 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
Ok(width)
}

fn node(&mut self, metas: &[Meta], depth: usize) -> fmt::Result {
fn node(&mut self, root: &Meta, metas: &[Meta], depth: usize) -> fmt::Result {
let columns = self.columns(metas, 1);
let mut helper = self.own();
for meta in Meta::visible(metas) {
Expand Down Expand Up @@ -355,8 +413,8 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
helper.write_line("")?;
}
Meta::Root(metas) => {
helper.write_header(metas)?;
helper.node(metas, depth + 1)?;
helper.write_header(root, metas)?;
helper.node(root, metas, depth + 1)?;
}
Meta::Group(metas) => {
helper.indentation()?;
Expand All @@ -369,15 +427,15 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
)?;
if width > 0 {
helper.write_line("")?;
helper.indent().node(metas, depth + 1)?;
helper.indent().node(root, metas, depth + 1)?;
helper.write_line("")?;
} else {
helper.node(metas, depth + 1)?;
helper.node(root, metas, depth + 1)?;
}
}
Meta::Verb(metas) if depth == 0 => {
helper.write_header(metas)?;
helper.node(metas, depth + 1)?;
helper.write_header(root, metas)?;
helper.node(root, metas, depth + 1)?;
}
Meta::Verb(metas) => {
helper.indentation()?;
Expand Down Expand Up @@ -423,7 +481,7 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
Ok(())
}

fn write_header(&mut self, metas: &[Meta]) -> Result<usize, fmt::Error> {
fn write_header(&mut self, root: &Meta, metas: &[Meta]) -> Result<usize, fmt::Error> {
let mut width = 0;
width += self.write_line(())?;
width += self.indentation()?;
Expand Down Expand Up @@ -516,6 +574,7 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
has |= count > 0;

width += helper.usage(
root,
metas,
(
if has {
Expand Down Expand Up @@ -616,15 +675,20 @@ impl<'a, S: Style + ?Sized + 'a> Helper<'a, S> {
}
}

pub(crate) fn help<S: Style + ?Sized>(meta: &Meta, path: &[Key], style: &S) -> Option<String> {
pub(crate) fn help<S: Style + ?Sized>(
root: &Meta,
meta: &Meta,
path: &[Key],
style: &S,
) -> Option<String> {
let mut buffer = String::new();
let mut writer = Helper {
buffer: &mut buffer,
path,
style,
indent: 0,
};
writer.node(from_ref(meta), 0).ok()?;
writer.node(root, from_ref(meta), 0).ok()?;
Some(buffer)
}

Expand Down
102 changes: 99 additions & 3 deletions src/meta.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::parse::Key;
use core::num::NonZeroUsize;
use std::{borrow::Cow, iter::from_fn};
use std::{borrow::Cow, iter::from_fn, ops::ControlFlow, slice::from_ref};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Name {
Expand All @@ -24,7 +25,7 @@ pub enum Meta {
Note(Cow<'static, str>),
Type(Cow<'static, str>),
Valid(Cow<'static, str>),
Require,
Require(Cow<'static, str>),
Many(Option<NonZeroUsize>),
Default(Cow<'static, str>),
Environment(Cow<'static, str>),
Expand Down Expand Up @@ -98,7 +99,7 @@ impl Meta {
Meta::Usage(value) => Meta::Usage(value.clone()),
Meta::Note(value) => Meta::Note(value.clone()),
Meta::Type(value) => Meta::Type(value.clone()),
Meta::Require => Meta::Require,
Meta::Require(value) => Meta::Require(value.clone()),
Meta::Many(value) => Meta::Many(*value),
Meta::Default(value) => Meta::Default(value.clone()),
Meta::Environment(value) => Meta::Environment(value.clone()),
Expand Down Expand Up @@ -126,6 +127,78 @@ impl Meta {
}
}

pub(crate) fn require(&self) -> Option<Cow<'static, str>> {
let control = Self::descend(
from_ref(self),
None,
false,
1,
|state, meta| {
ControlFlow::<(), _>::Continue(match meta {
Meta::Require(value) => state.or(Some(value.clone())),
_ => state,
})
},
|state, _| ControlFlow::Continue(state),
);
match control {
ControlFlow::Continue(Some(value)) => Some(value.clone()),
_ => None,
}
}

pub(crate) fn key(&self) -> Option<Key> {
let control = Self::descend(
from_ref(self),
(None, None, None, None),
false,
1,
|state, meta| {
ControlFlow::<(), _>::Continue(match meta {
Meta::Name(Name::Plain, value) => {
(state.0.or(Some(value.clone())), state.1, state.2, state.3)
}
Meta::Name(Name::Short, value) => {
(state.0, state.1.or(Some(value.clone())), state.2, state.3)
}
Meta::Name(Name::Long, value) => {
(state.0, state.1, state.2.or(Some(value.clone())), state.3)
}
Meta::Position(value) => (state.0, state.1, state.2, state.3.or(Some(*value))),
_ => state,
})
},
|state, _| ControlFlow::Continue(state),
);
match control {
ControlFlow::Continue((Some(value), _, _, _)) => Some(Key::Name(value)),
ControlFlow::Continue((_, Some(value), _, _)) => Some(Key::Name(value)),
ControlFlow::Continue((_, _, Some(value), _)) => Some(Key::Name(value)),
ControlFlow::Continue((_, _, _, Some(value))) => Some(Key::Index(value)),
_ => None,
}
}

pub(crate) fn descend<T, S>(
metas: &[Meta],
mut state: S,
hidden: bool,
depth: usize,
mut down: impl FnMut(S, &Meta) -> ControlFlow<T, S>,
mut up: impl FnMut(S, &Meta) -> ControlFlow<T, S>,
) -> ControlFlow<T, S> {
if hidden {
for meta in metas {
state = meta.descend_one(state, hidden, depth, &mut down, &mut up)?;
}
} else {
for meta in Meta::visible(metas) {
state = meta.descend_one(state, hidden, depth, &mut down, &mut up)?;
}
}
ControlFlow::Continue(state)
}

pub(crate) fn children(&self) -> &[Meta] {
match self {
Meta::Root(metas) | Meta::Option(metas) | Meta::Verb(metas) | Meta::Group(metas) => {
Expand All @@ -151,4 +224,27 @@ impl Meta {
}
})
}

fn descend_one<T, S>(
&self,
mut state: S,
hidden: bool,
depth: usize,
down: &mut impl FnMut(S, &Self) -> ControlFlow<T, S>,
up: &mut impl FnMut(S, &Self) -> ControlFlow<T, S>,
) -> ControlFlow<T, S> {
state = down(state, self)?;
if depth > 0 {
if hidden {
for child in self.children() {
state = child.descend_one(state, hidden, depth - 1, down, up)?;
}
} else {
for child in Self::visible(self.children()) {
state = child.descend_one(state, hidden, depth - 1, down, up)?;
}
}
}
up(state, self)
}
}

0 comments on commit df37d6e

Please sign in to comment.