Skip to content

Commit

Permalink
Add 'crates/aargvark/' from commit '3b586b71e8f3f748463cab60f20e7fcc8…
Browse files Browse the repository at this point in the history
…0792547'

git-subtree-dir: crates/aargvark
git-subtree-mainline: 81738fc
git-subtree-split: 3b586b7
  • Loading branch information
andrew committed Dec 1, 2024
2 parents 81738fc + 3b586b7 commit b04128a
Show file tree
Hide file tree
Showing 16 changed files with 2,386 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/aargvark/.github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

---

I agree that when the request is merged I assign the copyright of the request to the repository owner.
18 changes: 18 additions & 0 deletions crates/aargvark/.github/workflows/copyright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
on:
pull_request:
types: [opened, edited, synchronize]

jobs:
confirm_agreement:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BODY: ${{ github.event.pull_request.body }}
PR_ID: ${{ github.event.pull_request.number }}
run: |
set -xeu
if ! grep -F "$(tail -n 1 .github/pull_request_template.md)" <(echo "$BODY"); then
gh pr close --comment "All changes must include the provided agreement to the copyright assignment." --delete-branch "$PR_ID"
fi
3 changes: 3 additions & 0 deletions crates/aargvark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
/.vscode
11 changes: 11 additions & 0 deletions crates/aargvark/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[workspace]
resolver = "2"
members = ["crates/*"]

[workspace.package]
edition = "2021"
license = "ISC"
description = "Self-similar argument parsing"
homepage = "https://github.com/andrewbaxter/aargvark"
repository = "https://github.com/andrewbaxter/aargvark"
readme = "readme.md"
31 changes: 31 additions & 0 deletions crates/aargvark/crates/aargvark/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "aargvark"
description = "Self-similar argument parsing"
version = "0.6.5"
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
readme.workspace = true

[package.metadata.docs.rs]
all-features = true

[features]
default = []
serde_json = ["dep:serde_json", "dep:serde"]
serde_yaml = ["dep:serde_yaml", "dep:serde"]
http_types = ["dep:http"]

[dependencies]
aargvark_proc_macros = { path = "../aargvark_proc_macros", version = "=3.2.5" }
serde_json = { version = "1", optional = true }
serde_yaml = { version = "0", optional = true }
convert_case = "0.6"
comfy-table = { version = "7", features = ["custom_styling"] }
url = { version = "2", optional = true }
http = { version = "1", optional = true }
serde = { version = "1", optional = true }
console = "0.15"
textwrap = { version = "0.16", features = ["terminal_size"] }
unicode-width = "0.1"
1 change: 1 addition & 0 deletions crates/aargvark/crates/aargvark/readme.md
179 changes: 179 additions & 0 deletions crates/aargvark/crates/aargvark/src/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use {
crate::help::{
HelpPartialProduction,
HelpState,
},
unicode_width::UnicodeWidthStr,
};

pub struct VarkFailure {
pub arg_offset: usize,
pub error: String,
}

/// Return type enum (like `Result`) during parsing.
pub enum R<T> {
/// Ran out of arguments before parsing successfully completed.
EOF,
/// Parsing failed due to incorrect arguments.
Err,
/// Encountered `-h` or `--help` and aborted.
Help(Box<dyn FnOnce(&mut HelpState) -> HelpPartialProduction>),
/// Successfully parsed value.
Ok(T),
}

/// Possible results of peeking the output.
pub enum PeekR<'a> {
/// There's another argument
Ok(&'a str),
/// No more arguments remain
None,
/// The next argument is `-h` or `--help` - caller should return `R::Help`.
Help,
}

#[doc(hidden)]
pub struct VarkState {
pub(crate) command: Option<String>,
pub(crate) args: Vec<String>,
pub(crate) i: usize,
pub(crate) errors: Vec<VarkFailure>,
}

impl VarkState {
pub fn new(command: Option<String>, args: Vec<String>) -> Self {
return Self {
command: command,
args: args,
i: 0,
errors: vec![],
};
}
}

impl VarkState {
/// Return the next argument without consuming it.
pub fn peek<'a>(&'a self) -> PeekR<'a> {
if self.i >= self.args.len() {
return PeekR::None;
}
let v = &self.args[self.i];
if v == "-h" || v == "--help" {
return PeekR::Help;
}
return PeekR::Ok(v);
}

/// The argument the current argument pointer is pointing to.
pub fn position(&self) -> usize {
return self.i;
}

/// Reset the agument pointer to an earlier argument (i.e. after consuming N
/// arguments but finding the required final argument missing).
pub fn rewind(&mut self, i: usize) {
self.i = i;
}

/// Move the argument pointer to the next argument (ex: after inspecting it using
/// `peek`).
pub fn consume(&mut self) {
self.i += 1;
}

/// Produce a "parse successful" return value.
pub fn r_ok<T>(&self, v: T) -> R<T> {
return R::Ok(v);
}

/// Produce a "parse failed" return value (includes which argument was being
/// inspected when the failure occured).
pub fn r_err<T>(&mut self, text: String) -> R<T> {
self.errors.push(VarkFailure {
arg_offset: self.i,
error: text,
});
return R::Err;
}
}

pub enum ErrorDetail {
/// The parser needed more command line arguments.
TooLittle,
/// Fully parsed command but additional unconsumed arguments follow (offset of
/// first unrecognized argument)
TooMuch(usize),
/// Aargvark considers multiple possible parses. This is a list of considered
/// parses, in order of when they were ruled out.
Incorrect(Vec<VarkFailure>),
}

/// Returned by `vark_explicit`. `command` is whatever is passed as `command` to
/// `vark_explicit`, the first of argv if using `vark`. `args` is the remaining
/// arguments.
pub struct Error {
pub command: Option<String>,
pub args: Vec<String>,
pub detail: ErrorDetail,
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.detail {
ErrorDetail::TooLittle => {
return "Missing arguments, use --help for more info".fmt(f);
},
ErrorDetail::TooMuch(first) => {
return format_args!(
"Error parsing command line arguments: final arguments are unrecognized\n{:?}",
&self.args[*first..]
).fmt(f);
},
ErrorDetail::Incorrect(failures) => {
let mut display_args = vec![];
let mut offset_offset = 0;
if let Some(c) = &self.command {
display_args.push(c.clone());
offset_offset = 1;
}
display_args.extend(self.args.iter().map(|a| format!("{:?}", a)));
let mut display_arg_offsets = vec![];
{
let mut offset = 0;
for d in &display_args {
display_arg_offsets.push(offset);
offset += d.width() + 1;
}
display_arg_offsets.push(offset);
}
let mut display_args = display_args.join(" ");
display_args.push_str(" <END>");
let mut text = "Error parsing arguments.\n".to_string();
for e in failures.iter().rev() {
text.push_str("\n");
text.push_str(&format!(" * {}\n", e.error));
text.push_str(" ");
text.push_str(&display_args);
text.push_str("\n");
text.push_str(" ");
text.push_str(
&" ".repeat(
display_arg_offsets.get(e.arg_offset + offset_offset).cloned().unwrap_or(0usize),
),
);
text.push_str("^\n");
}
return text.fmt(f);
},
}
}
}

impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return std::fmt::Display::fmt(self, f);
}
}

impl std::error::Error for Error { }
Loading

0 comments on commit b04128a

Please sign in to comment.