Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions crates/lexarg-error/examples/hello-error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lexarg_error::ErrorContext;
use lexarg_error::LexError;

struct Args {
thing: String,
Expand All @@ -23,21 +23,21 @@ fn parse_args() -> Result<Args, String> {
match arg {
Short("n") | Long("number") => {
let value = parser.next_flag_value().ok_or_else(|| {
ErrorContext::msg("missing required value")
LexError::msg("missing required value")
.within(arg)
.to_string()
})?;
number = value
.to_str()
.ok_or_else(|| {
ErrorContext::msg("invalid number")
LexError::msg("invalid number")
.unexpected(Value(value))
.within(arg)
.to_string()
})?
.parse()
.map_err(|e| {
ErrorContext::msg(e)
LexError::msg(e)
.unexpected(Value(value))
.within(arg)
.to_string()
Expand All @@ -47,18 +47,17 @@ fn parse_args() -> Result<Args, String> {
shout = true;
}
Value(val) if thing.is_none() => {
thing = Some(val.to_str().ok_or_else(|| {
ErrorContext::msg("invalid string")
.unexpected(arg)
.to_string()
})?);
thing =
Some(val.to_str().ok_or_else(|| {
LexError::msg("invalid string").unexpected(arg).to_string()
})?);
}
Short("h") | Long("help") => {
println!("Usage: hello [-n|--number=NUM] [--shout] THING");
std::process::exit(0);
}
_ => {
return Err(ErrorContext::msg("unexpected argument")
return Err(LexError::msg("unexpected argument")
.unexpected(arg)
.to_string());
}
Expand All @@ -68,7 +67,7 @@ fn parse_args() -> Result<Args, String> {
Ok(Args {
thing: thing
.ok_or_else(|| {
ErrorContext::msg("missing argument THING")
LexError::msg("missing argument THING")
.within(Value(bin_name))
.to_string()
})?
Expand Down
8 changes: 4 additions & 4 deletions crates/lexarg-error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

/// Collect context for creating an error
#[derive(Debug)]
pub struct ErrorContext<'a> {
pub struct LexError<'a> {
msg: String,
within: Option<lexarg_parser::Arg<'a>>,
unexpected: Option<lexarg_parser::Arg<'a>>,
}

impl<'a> ErrorContext<'a> {
impl<'a> LexError<'a> {
/// Create a new error object from a printable error message.
#[cold]
pub fn msg<M>(message: M) -> Self
Expand Down Expand Up @@ -52,7 +52,7 @@ impl<'a> ErrorContext<'a> {
}
}

impl<E> From<E> for ErrorContext<'_>
impl<E> From<E> for LexError<'_>
where
E: std::error::Error + Send + Sync + 'static,
{
Expand All @@ -62,7 +62,7 @@ where
}
}

impl std::fmt::Display for ErrorContext<'_> {
impl std::fmt::Display for LexError<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.msg.fmt(formatter)?;
if let Some(unexpected) = &self.unexpected {
Expand Down
8 changes: 3 additions & 5 deletions crates/lexarg/examples/hello.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lexarg::ErrorContext;
use lexarg::LexError;
use lexarg::Result;

struct Args {
Expand Down Expand Up @@ -40,15 +40,13 @@ fn parse_args() -> Result<Args> {
std::process::exit(0);
}
Unexpected(_) => {
return Err(ErrorContext::msg("unexpected value")
return Err(LexError::msg("unexpected value")
.unexpected(arg)
.within(prev_arg)
.into());
}
_ => {
return Err(ErrorContext::msg("unexpected argument")
.unexpected(arg)
.into());
return Err(LexError::msg("unexpected argument").unexpected(arg).into());
}
}
prev_arg = arg;
Expand Down
74 changes: 37 additions & 37 deletions crates/lexarg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
/// Simplify parsing of arguments
pub mod prelude {
pub use crate::Arg::*;
pub use crate::OptionContextExt as _;
pub use crate::ResultContextExt as _;
pub use crate::OptionLexErrorExt as _;
pub use crate::ResultLexErrorExt as _;
pub use crate::ValueExt as _;
}

pub use lexarg_error::ErrorContext;
pub use lexarg_error::LexError;
pub use lexarg_parser::Arg;
pub use lexarg_parser::Parser;
pub use lexarg_parser::RawArgs;
Expand All @@ -50,9 +50,9 @@ impl Error {
}
}

impl From<ErrorContext<'_>> for Error {
impl From<LexError<'_>> for Error {
#[cold]
fn from(error: ErrorContext<'_>) -> Self {
fn from(error: LexError<'_>) -> Self {
Self::msg(error.to_string())
}
}
Expand All @@ -72,32 +72,32 @@ impl std::fmt::Display for Error {
/// Extensions for parsing [`Arg::Value`]
pub trait ValueExt<'a> {
/// Convert [`Arg::Value`]
fn path(self) -> Result<&'a std::path::Path, ErrorContext<'a>>;
fn path(self) -> Result<&'a std::path::Path, LexError<'a>>;
/// Convert [`Arg::Value`] with a description of the intended format
fn string(self, description: &str) -> Result<&'a str, ErrorContext<'a>>;
fn string(self, description: &str) -> Result<&'a str, LexError<'a>>;
/// Ensure [`Arg::Value`] is from a closed set of values
fn one_of(self, possible: &[&str]) -> Result<&'a str, ErrorContext<'a>>;
fn one_of(self, possible: &[&str]) -> Result<&'a str, LexError<'a>>;
/// Parse [`Arg::Value`]
fn parse<T: std::str::FromStr>(self) -> Result<T, ErrorContext<'a>>
fn parse<T: std::str::FromStr>(self) -> Result<T, LexError<'a>>
where
T::Err: std::fmt::Display;
/// Custom conversion for [`Arg::Value`]
fn try_map<F, T, E>(self, op: F) -> Result<T, ErrorContext<'a>>
fn try_map<F, T, E>(self, op: F) -> Result<T, LexError<'a>>
where
F: FnOnce(&'a std::ffi::OsStr) -> Result<T, E>,
E: std::fmt::Display;
}

impl<'a> ValueExt<'a> for &'a std::ffi::OsStr {
fn path(self) -> Result<&'a std::path::Path, ErrorContext<'a>> {
fn path(self) -> Result<&'a std::path::Path, LexError<'a>> {
Ok(std::path::Path::new(self))
}
fn string(self, description: &str) -> Result<&'a str, ErrorContext<'a>> {
fn string(self, description: &str) -> Result<&'a str, LexError<'a>> {
self.to_str().ok_or_else(|| {
ErrorContext::msg(format_args!("invalid {description}")).unexpected(Arg::Value(self))
LexError::msg(format_args!("invalid {description}")).unexpected(Arg::Value(self))
})
}
fn one_of(self, possible: &[&str]) -> Result<&'a str, ErrorContext<'a>> {
fn one_of(self, possible: &[&str]) -> Result<&'a str, LexError<'a>> {
self.to_str()
.filter(|v| possible.contains(v))
.ok_or_else(|| {
Expand All @@ -108,43 +108,43 @@ impl<'a> ValueExt<'a> for &'a std::ffi::OsStr {
use std::fmt::Write as _;
let _ = write!(&mut error, ", `{possible}`");
}
ErrorContext::msg(error)
LexError::msg(error)
})
}
fn parse<T: std::str::FromStr>(self) -> Result<T, ErrorContext<'a>>
fn parse<T: std::str::FromStr>(self) -> Result<T, LexError<'a>>
where
T::Err: std::fmt::Display,
{
self.string(std::any::type_name::<T>())?
.parse::<T>()
.map_err(|err| ErrorContext::msg(err).unexpected(Arg::Value(self)))
.map_err(|err| LexError::msg(err).unexpected(Arg::Value(self)))
}
fn try_map<F, T, E>(self, op: F) -> Result<T, ErrorContext<'a>>
fn try_map<F, T, E>(self, op: F) -> Result<T, LexError<'a>>
where
F: FnOnce(&'a std::ffi::OsStr) -> Result<T, E>,
E: std::fmt::Display,
{
op(self).map_err(|err| ErrorContext::msg(err).unexpected(Arg::Value(self)))
op(self).map_err(|err| LexError::msg(err).unexpected(Arg::Value(self)))
}
}

impl<'a> ValueExt<'a> for Result<&'a std::ffi::OsStr, ErrorContext<'a>> {
fn path(self) -> Result<&'a std::path::Path, ErrorContext<'a>> {
impl<'a> ValueExt<'a> for Result<&'a std::ffi::OsStr, LexError<'a>> {
fn path(self) -> Result<&'a std::path::Path, LexError<'a>> {
self.and_then(|os| os.path())
}
fn string(self, description: &str) -> Result<&'a str, ErrorContext<'a>> {
fn string(self, description: &str) -> Result<&'a str, LexError<'a>> {
self.and_then(|os| os.string(description))
}
fn one_of(self, possible: &[&str]) -> Result<&'a str, ErrorContext<'a>> {
fn one_of(self, possible: &[&str]) -> Result<&'a str, LexError<'a>> {
self.and_then(|os| os.one_of(possible))
}
fn parse<T: std::str::FromStr>(self) -> Result<T, ErrorContext<'a>>
fn parse<T: std::str::FromStr>(self) -> Result<T, LexError<'a>>
where
T::Err: std::fmt::Display,
{
self.and_then(|os| os.parse())
}
fn try_map<F, T, E>(self, op: F) -> Result<T, ErrorContext<'a>>
fn try_map<F, T, E>(self, op: F) -> Result<T, LexError<'a>>
where
F: FnOnce(&'a std::ffi::OsStr) -> Result<T, E>,
E: std::fmt::Display,
Expand All @@ -153,33 +153,33 @@ impl<'a> ValueExt<'a> for Result<&'a std::ffi::OsStr, ErrorContext<'a>> {
}
}

/// Extensions for extending [`ErrorContext`]
pub trait ResultContextExt<'a> {
/// Extensions for extending [`LexError`]
pub trait ResultLexErrorExt<'a> {
/// [`Arg`] the error occurred within
fn within(self, within: Arg<'a>) -> Self;
}

impl<'a, T> ResultContextExt<'a> for Result<T, ErrorContext<'a>> {
impl<'a, T> ResultLexErrorExt<'a> for Result<T, LexError<'a>> {
fn within(self, within: Arg<'a>) -> Self {
self.map_err(|err| err.within(within))
}
}

/// Extensions for creating an [`ErrorContext`]
pub trait OptionContextExt<T> {
/// Extensions for creating an [`LexError`]
pub trait OptionLexErrorExt<T> {
/// [`Arg`] that was expected
///
/// For [`Arg::Value`], the contents are assumed to be a placeholder
fn ok_or_missing(self, expected: Arg<'static>) -> Result<T, ErrorContext<'static>>;
fn ok_or_missing(self, expected: Arg<'static>) -> Result<T, LexError<'static>>;
}

impl<T> OptionContextExt<T> for Option<T> {
fn ok_or_missing(self, expected: Arg<'static>) -> Result<T, ErrorContext<'static>> {
impl<T> OptionLexErrorExt<T> for Option<T> {
fn ok_or_missing(self, expected: Arg<'static>) -> Result<T, LexError<'static>> {
self.ok_or_else(|| match expected {
Arg::Short(short) => ErrorContext::msg(format_args!("missing required `-{short}`")),
Arg::Long(long) => ErrorContext::msg(format_args!("missing required `--{long}`")),
Arg::Escape(escape) => ErrorContext::msg(format_args!("missing required `{escape}`")),
Arg::Value(value) | Arg::Unexpected(value) => ErrorContext::msg(format_args!(
Arg::Short(short) => LexError::msg(format_args!("missing required `-{short}`")),
Arg::Long(long) => LexError::msg(format_args!("missing required `--{long}`")),
Arg::Escape(escape) => LexError::msg(format_args!("missing required `{escape}`")),
Arg::Value(value) | Arg::Unexpected(value) => LexError::msg(format_args!(
"missing required `{}`",
value.to_string_lossy()
)),
Expand Down
4 changes: 2 additions & 2 deletions crates/libtest-lexarg/examples/libtest-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn main() -> lexarg::Result<()> {
continue;
}
Unexpected(_) => {
return Err(lexarg::ErrorContext::msg("unexpected value")
return Err(lexarg::LexError::msg("unexpected value")
.unexpected(arg)
.within(prev_arg)
.into());
Expand All @@ -43,7 +43,7 @@ fn main() -> lexarg::Result<()> {
let arg = test_opts.parse_next(&mut parser, arg)?;

if let Some(arg) = arg {
return Err(lexarg::ErrorContext::msg("unexpected argument")
return Err(lexarg::LexError::msg("unexpected argument")
.unexpected(arg)
.into());
}
Expand Down
14 changes: 6 additions & 8 deletions crates/libtest-lexarg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#![warn(missing_debug_implementations, elided_lifetimes_in_paths)]

use lexarg::Arg;
use lexarg_error::ErrorContext;
use lexarg_error::LexError;

/// Parsed command-line options
///
Expand Down Expand Up @@ -174,7 +174,7 @@ impl TestOptsBuilder {
&mut self,
parser: &mut lexarg::Parser<'a>,
arg: Arg<'a>,
) -> Result<Option<Arg<'a>>, ErrorContext<'a>> {
) -> Result<Option<Arg<'a>>, LexError<'a>> {
use lexarg::prelude::*;

match arg {
Expand Down Expand Up @@ -257,7 +257,7 @@ impl TestOptsBuilder {
.string("FEATURE")
.within(arg)?;
if !is_nightly() {
return Err(ErrorContext::msg("expected nightly compiler").unexpected(arg));
return Err(LexError::msg("expected nightly compiler").unexpected(arg));
}
// Don't validate `feature` as other parsers might provide values
self.opts.allowed_unstable.push(feature.to_owned());
Expand All @@ -274,17 +274,15 @@ impl TestOptsBuilder {
}

/// Finish parsing, resolving to [`TestOpts`]
pub fn finish(mut self) -> Result<TestOpts, ErrorContext<'static>> {
pub fn finish(mut self) -> Result<TestOpts, LexError<'static>> {
let allow_unstable_options = self
.opts
.allowed_unstable
.iter()
.any(|f| f == UNSTABLE_OPTIONS);

if self.format.is_some() && !allow_unstable_options {
return Err(ErrorContext::msg(
"`--format` requires `-Zunstable-options`",
));
return Err(LexError::msg("`--format` requires `-Zunstable-options`"));
}
if let Some(format) = self.format {
self.opts.format = format;
Expand All @@ -296,7 +294,7 @@ impl TestOptsBuilder {

self.opts.run_ignored = match (self.include_ignored, self.ignored) {
(true, true) => {
return Err(ErrorContext::msg(
return Err(LexError::msg(
"`--include-ignored` and `--ignored` are mutually exclusive",
))
}
Expand Down
4 changes: 2 additions & 2 deletions crates/libtest2-harness/src/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub trait Case: Send + Sync + 'static {
fn kind(&self) -> TestKind;
fn source(&self) -> Option<&Source>;
/// This case cannot run in parallel to other cases within this binary
fn exclusive(&self, state: &State) -> bool;
fn exclusive(&self, state: &TestContext) -> bool;

fn run(&self, state: &State) -> Result<(), RunError>;
fn run(&self, state: &TestContext) -> Result<(), RunError>;
}

/// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
Expand Down
Loading
Loading