Skip to content

Commit

Permalink
fix(cli): make file inputs work, add varint parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
nothendev committed Sep 29, 2023
1 parent bc62152 commit 422a65f
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 98 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ thiserror.version = "1"
bytes = { version = "1", features = [ "serde" ] }
bevy = { version = "0.11.2", features = ["multi-threaded"], default-features = false }
rayon = "1"
itertools = "0"
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
oxcr_protocol.workspace = true
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
itertools.workspace = true
76 changes: 34 additions & 42 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
//! Spanned-clap, lol

use itertools::Itertools;
use std::{marker::PhantomData, path::PathBuf, str::FromStr};

use aott::prelude::*;
use oxcr_protocol::{
aott::{
self, pfn_type,
text::{ascii::ident, inline_whitespace, int, whitespace},
text::{ascii::ident, digits, inline_whitespace, int, whitespace},
},
bytes::{BufMut, Bytes, BytesMut},
tracing::{level_filters::LevelFilter, Level},
};

use crate::error::{Expectation, ParseError, ParseErrorKind};
use crate::error::{Expectation, ParseError};

pub struct Extra<C = ()>(PhantomData<C>);
impl<'a, C> ParserExtras<&'a str> for Extra<C> {
Expand All @@ -30,6 +31,7 @@ pub enum ByteInput {
pub enum CliCommand {
Decode(ByteInput),
Help,
VarInt(ByteInput),
}

#[derive(Debug, Clone)]
Expand All @@ -43,6 +45,7 @@ pub enum Flag {
Level(Level),
Help,
Decode(ByteInput),
VarInt(ByteInput),
}

#[derive(Debug)]
Expand All @@ -54,50 +57,35 @@ enum FlagName<'a> {
fn numbah<'a>(radix: u32, cha: char) -> pfn_type!(&'a str, Bytes, Extra) {
move |input| {
just(['0', cha])
.ignore_then(text::int(radix))
.try_map_with_span(|x: &str, span| {
.ignore_then(text::digits(radix).slice())
.map(|x: &str| {
x.chars()
.map(|c| {
c.to_digit(radix)
.ok_or_else(|| ParseError {
span: span.clone().into(),
kind: ParseErrorKind::Expected {
expected: Expectation::Digit(8),
found: c,
},
})
.map(|x| x.try_into().expect("not a u8"))
.batching(|it| {
let mut s = it.next()?.to_string();
it.next().map(|c| s.push(c));
Some(u8::from_str_radix(&s, radix).expect("a u8..."))
})
.try_collect()
.collect()
})
.parse_with(input)
}
}

#[parser(extras = Extra)]
fn path_buf(input: &str) -> PathBuf {
Ok(PathBuf::from_str(input.input.slice_from(input.offset..)).unwrap())
}

#[parser(extras = Extra)]
fn byte_input(input: &str) -> ByteInput {
choice((
(choice((
numbah(16, 'x'),
numbah(2, 'b'),
numbah(8, 'o'),
numbah(10, 'd'),
))
.or(text::int(16).try_map_with_span(|x: &str, span| {
x.chars()
.map(|c| {
c.to_digit(16)
.ok_or_else(|| ParseError {
span: span.clone().into(),
kind: ParseErrorKind::Expected {
expected: Expectation::Digit(8),
found: c,
},
})
.map(|x| x.try_into().expect("not a u8"))
})
.try_collect()
}))
.map(ByteInput::Data)
.map(ByteInput::Data))
.or(path_buf.map(ByteInput::File))
.parse_with(input)
}

Expand Down Expand Up @@ -131,12 +119,11 @@ fn flags(input: &str) -> Vec<Flag> {
one_of(" =")(input)?;
let before_ = input.offset;
Flag::Level(
Level::from_str(ident.or(slice(int(10))).parse_with(input)?).map_err(
|error| ParseError {
span: (before_, input.offset).into(),
kind: error.into(),
},
)?,
Level::from_str(ident.or(slice(digits(10))).parse_with(input)?)
.map_err(|actual| ParseError::ParseLevel {
at: (before_, input.offset).into(),
actual,
})?,
)
}
FlagName::Short("D") | FlagName::Long("debug") => Flag::Level(Level::DEBUG),
Expand All @@ -147,10 +134,15 @@ fn flags(input: &str) -> Vec<Flag> {

Flag::Decode(byte_input(input)?)
}
FlagName::Short("V") | FlagName::Long("varint") => {
one_of(" =")(input)?;

Flag::VarInt(byte_input(input)?)
}
FlagName::Short("h") | FlagName::Long("help") => Flag::Help,
FlagName::Short(flag) | FlagName::Long(flag) => Err(ParseError {
span: input.span_since(before).into(),
kind: ParseErrorKind::UnknownFlag(flag.to_owned()),
FlagName::Short(flag) | FlagName::Long(flag) => Err(ParseError::UnknownFlag {
flag: flag.to_owned(),
at: input.span_since(before).into(),
})?,
}
}
Expand All @@ -167,9 +159,9 @@ fn flags_handle<'a>(
Flag::Level(level) => cli.level = LevelFilter::from(level),
Flag::Help => cli.command = CliCommand::Help,
Flag::Decode(input) => cli.command = CliCommand::Decode(input),
Flag::VarInt(input) => cli.command = CliCommand::VarInt(input),
}
}

try { flags(input)?.into_iter().for_each(|flag| handle(cli, flag)) }
}

Expand Down
94 changes: 50 additions & 44 deletions cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,41 @@ use oxcr_protocol::{
};

#[derive(miette::Diagnostic, thiserror::Error, Debug)]
pub enum ParseErrorKind {
pub enum ParseError {
#[error("expected {expected}, found {found}")]
Expected { expected: Expectation, found: char },
#[error("unexpected end of input")]
UnexpectedEof,
#[error("parsing a Level failed: {_0}")]
ParseLevel(#[from] ParseLevelError),
#[error("unknown flag encountered: {_0}")]
UnknownFlag(String),
#[diagnostic(code(cli::expected))]
Expected {
expected: Expectation,
found: char,
#[label = "here"]
at: SourceSpan,
},
#[error("unexpected end of input{}", .expected.as_ref().map(|expectation| format!(", expected {expectation}")).unwrap_or_else(String::new))]
#[diagnostic(
code(cli::unexpected_eof),
help("try giving it more input next time, I guess?")
)]
UnexpectedEof {
#[label = "here"]
at: SourceSpan,
#[label = "last data here"]
last_data_at: Option<SourceSpan>,
expected: Option<Expectation>,
},
#[error("parsing a Level failed: {actual}")]
#[diagnostic(code(cli::parse_level_error))]
ParseLevel {
actual: ParseLevelError,
#[label = "here"]
at: SourceSpan,
},
#[error("unknown flag encountered: {flag}")]
#[diagnostic(code(cli::unknown_flag))]
UnknownFlag {
flag: String,
#[label = "here"]
at: SourceSpan,
},
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -30,57 +56,37 @@ pub enum Expectation {
Digit(u32),
}

#[derive(miette::Diagnostic, thiserror::Error, Debug)]
#[error("{kind}")]
pub struct ParseError {
#[label = "here"]
pub span: SourceSpan,
#[diagnostic(transparent)]
#[diagnostic_source]
#[source]
pub kind: ParseErrorKind,
}

impl ParseError {
pub fn new(span: Range<usize>, kind: ParseErrorKind) -> Self {
Self {
span: span.into(),
kind,
}
}
}

impl<'a> aott::error::Error<&'a str> for ParseError {
type Span = Range<usize>;

fn unexpected_eof(
span: Self::Span,
_expected: Option<Vec<<&'a str as aott::prelude::InputType>::Token>>,
expected: Option<Vec<<&'a str as aott::prelude::InputType>::Token>>,
) -> Self {
Self::new(span, ParseErrorKind::UnexpectedEof)
Self::UnexpectedEof {
at: span.into(),
last_data_at: None,
expected: expected.map(Expectation::AnyOf),
}
}

fn expected_token_found(
span: Self::Span,
expected: Vec<char>,
found: aott::MaybeRef<'_, char>,
) -> Self {
Self::new(
span,
ParseErrorKind::Expected {
expected: Expectation::AnyOf(expected),
found: found.into_clone(),
},
)
Self::Expected {
expected: Expectation::AnyOf(expected),
found: found.into_clone(),
at: span.into(),
}
}

fn expected_eof_found(span: Self::Span, found: aott::MaybeRef<'_, char>) -> Self {
Self::new(
span,
ParseErrorKind::Expected {
expected: Expectation::EndOfInput,
found: found.into_clone(),
},
)
Self::Expected {
expected: Expectation::EndOfInput,
found: found.into_clone(),
at: span.into(),
}
}
}
Loading

0 comments on commit 422a65f

Please sign in to comment.