diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/args.rs | 95 | ||||
-rw-r--r-- | src/error.rs | 42 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/strcursor.rs | 32 | ||||
-rw-r--r-- | src/tree.rs | 1 |
5 files changed, 143 insertions, 30 deletions
diff --git a/src/args.rs b/src/args.rs index 178203d..2a9bc28 100644 --- a/src/args.rs +++ b/src/args.rs @@ -9,9 +9,16 @@ use ::std::any::Any; use ::std::borrow::Cow; use ::std::future::Future; use ::std::io::Cursor; +use ::std::marker::PhantomData; +use ::std::num::ParseFloatError; +use ::std::num::ParseIntError; +use ::std::ops::RangeBounds; use ::std::pin::Pin; +use ::std::str::FromStr; -use crate::strcursor::{StringReader, ReadError}; +use crate::error::RangeError; +use crate::error::ReadError; +use crate::strcursor::StringReader; use crate::suggestion::Suggestions; use crate::suggestion::SuggestionsBuilder; @@ -34,7 +41,8 @@ pub struct CommandContext<'i, S, E>(::std::marker::PhantomData<(&'i str, S, E)>) /// use ::std::io::Cursor; /// /// use ::iosonism::args::ArgumentType; -/// use ::iosonism::strcursor::{ReadError, StringReader}; +/// use ::iosonism::error::ReadError; +/// use ::iosonism::strcursor::StringReader; /// /// struct BoolArgumentType; /// @@ -120,6 +128,7 @@ impl<T: ArgumentType<S, E>, S, E> ArgumentTypeAny<S, E> for T { /// A boolean argument. // FIXME add examples/expand docs // FIXME add tests +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)] pub struct BoolArgumentType; /// An `ArgumentType` for `bool`. @@ -158,3 +167,85 @@ where for<'i> E: ReadError<'i, Cursor<&'i str>> Cow::Borrowed(&["true", "false"]) } } + +/// An integer argument. +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)] +pub struct IntegerArgumentType<T, R: RangeBounds<T>> { + /// The valid range for this argument. + pub range: R, + /// PhantomData for the type. + pub _ty: PhantomData<T>, +} + +/// An `ArgumentType` for integer types. +impl<S, E, T, R> ArgumentType<S, E> for IntegerArgumentType<T, R> +where + for<'i> E: ReadError<'i, Cursor<&'i str>>, + for<'i> E: RangeError<'i, Cursor<&'i str>, T, R>, + R: RangeBounds<T>, + T: PartialOrd<T> + FromStr<Err=ParseIntError> + Any, +{ + /// An `IntegerArgumentType` parses an integer type. + type Result = T; + + /// Attempts to parse an integer from the `reader`. + fn parse<'i>( + &self, + reader: &mut Cursor<&'i str>, + ) -> Result<T, E> where E: 'i { + let start = reader.position(); + let value = reader.read_integer()?; + if self.range.contains(&value) { + Ok(value) + } else { + reader.set_position(start); + Err(E::value_not_in_range(reader, &value, &self.range)) + } + } + + /// Returns examples + fn get_examples(&self) -> Cow<'static, [&str]> { + Cow::Borrowed(&["0", "123", "-123"]) + } +} + +/// A float argument. +#[derive(Copy, Clone, PartialEq, Debug, PartialOrd, Default)] +pub struct FloatArgumentType<T, R: RangeBounds<T>> { + /// The valid range for this argument. + pub range: R, + /// PhantomData for the type. + pub _ty: PhantomData<T>, +} + +/// An `ArgumentType` for float types. +impl<S, E, T, R> ArgumentType<S, E> for FloatArgumentType<T, R> +where + for<'i> E: ReadError<'i, Cursor<&'i str>>, + for<'i> E: RangeError<'i, Cursor<&'i str>, T, R>, + R: RangeBounds<T>, + T: PartialOrd<T> + FromStr<Err=ParseFloatError> + Any, +{ + /// A `FloatArgumentType` parses a float type. + type Result = T; + + /// Attempts to parse a float from the `reader`. + fn parse<'i>( + &self, + reader: &mut Cursor<&'i str>, + ) -> Result<T, E> where E: 'i { + let start = reader.position(); + let value = reader.read_float()?; + if self.range.contains(&value) { + Ok(value) + } else { + reader.set_position(start); + Err(E::value_not_in_range(reader, &value, &self.range)) + } + } + + /// Returns examples + fn get_examples(&self) -> Cow<'static, [&str]> { + Cow::Borrowed(&["0", "1.2", ".5", "-1", "-.5", "-1234.56"]) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..74ba2fd --- /dev/null +++ b/src/error.rs @@ -0,0 +1,42 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +// Documentation and comments licensed under CC BY-SA 4.0. + +//! Built-in error traits. +//! +//! Iosonism uses a trait-based approach because that's just far more flexible. + +use ::std::error::Error as StdError; + +use crate::strcursor::StringReader; + +/// Built-in `StringReader` errors. +pub trait ReadError<'a, C: StringReader<'a>>: Sized + StdError { + /// Creates an error that indicates an invalid integer was found. + fn invalid_integer(context: &C, from: &str) -> Self; + /// Creates an error that indicates an integer was expected. + fn expected_integer(context: &C) -> Self; + /// Creates an error that indicates an invalid float was found. + fn invalid_float(context: &C, from: &str) -> Self; + /// Creates an error that indicates a float was expected. + fn expected_float(context: &C) -> Self; + /// Creates an error that indicates an invalid bool was found. + fn invalid_bool(context: &C, from: &str) -> Self; + /// Creates an error that indicates a bool was expected. + fn expected_bool(context: &C) -> Self; + /// Creates an error that indicates the start of a quote was expected. + fn expected_start_of_quote(context: &C) -> Self; + /// Creates an error that indicates the end of a quote was expected. + fn expected_end_of_quote(context: &C) -> Self; + /// Creates an error that indicates an invalid escape was found. + fn invalid_escape(context: &C, from: &str) -> Self; + /// Creates an error that indicates a symbol was expected. + fn expected_symbol(context: &C, from: &str) -> Self; +} + +/// Built-in errors for `IntegerArgumentType` and `FloatArgumentType`. +pub trait RangeError<'a, C: StringReader<'a>, T, R>: Sized + StdError { + /// Creates an error that indicates a value was outside the required bounds. + fn value_not_in_range(context: &C, from: &T, range: &R) -> Self; +} diff --git a/src/lib.rs b/src/lib.rs index 6921bbb..1895244 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,10 @@ // - brigadier.suggestion.Suggestions -> iosonism::suggestion::Suggestions; // - brigadier.suggestion.SuggestionsBuilder -> iosonism::suggestion::SuggestionsBuilder; +pub mod args; +pub mod error; pub mod strcursor; pub mod suggestion; -pub mod args; pub mod tree; use crate::args::CommandContext; diff --git a/src/strcursor.rs b/src/strcursor.rs index b9bf626..d733ade 100644 --- a/src/strcursor.rs +++ b/src/strcursor.rs @@ -8,29 +8,7 @@ use ::std::io::Cursor; use ::std::str::FromStr; -/// Built-in `StringReader` errors. -pub trait ReadError<'a, C: StringReader<'a>>: Sized + std::error::Error { - /// Creates an error that indicates an invalid integer was found. - fn invalid_integer(context: &C, from: &str) -> Self; - /// Creates an error that indicates an integer was expected. - fn expected_integer(context: &C) -> Self; - /// Creates an error that indicates an invalid float was found. - fn invalid_float(context: &C, from: &str) -> Self; - /// Creates an error that indicates a float was expected. - fn expected_float(context: &C) -> Self; - /// Creates an error that indicates an invalid bool was found. - fn invalid_bool(context: &C, from: &str) -> Self; - /// Creates an error that indicates a bool was expected. - fn expected_bool(context: &C) -> Self; - /// Creates an error that indicates the start of a quote was expected. - fn expected_start_of_quote(context: &C) -> Self; - /// Creates an error that indicates the end of a quote was expected. - fn expected_end_of_quote(context: &C) -> Self; - /// Creates an error that indicates an invalid escape was found. - fn invalid_escape(context: &C, from: &str) -> Self; - /// Creates an error that indicates a symbol was expected. - fn expected_symbol(context: &C, from: &str) -> Self; -} +use crate::error::ReadError; /// Extension trait on [`Cursor`]s to help with command parsing. /// @@ -130,14 +108,14 @@ pub trait StringReader<'a>: Sized { /// /// Panics if this cursor is not on an UTF-8 character boundary. fn read_integer<T, E: ReadError<'a, Self>>(&mut self) -> Result<T, E> - where T: FromStr<Err=std::num::ParseIntError>; + where T: FromStr<Err=::std::num::ParseIntError>; /// Reads a float. /// /// # Panics /// /// Panics if this cursor is not on an UTF-8 character boundary. fn read_float<T, E: ReadError<'a, Self>>(&mut self) -> Result<T, E> - where T: FromStr<Err=std::num::ParseFloatError>; + where T: FromStr<Err=::std::num::ParseFloatError>; /// Reads a bool. /// /// # Panics @@ -202,7 +180,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> { } fn read_integer<T, E: ReadError<'a, Self>>(&mut self) -> Result<T, E> - where T: FromStr<Err=std::num::ParseIntError> { + where T: FromStr<Err=::std::num::ParseIntError> { // see read_unquoted_str for rationale let start = self.position() as usize; let total = self.get_ref().len(); @@ -223,7 +201,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> { } } fn read_float<T, E: ReadError<'a, Self>>(&mut self) -> Result<T, E> - where T: FromStr<Err=std::num::ParseFloatError> { + where T: FromStr<Err=::std::num::ParseFloatError> { // see read_unquoted_str for rationale let start = self.position() as usize; let total = self.get_ref().len(); diff --git a/src/tree.rs b/src/tree.rs index b534754..daac8d3 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // Documentation and comments licensed under CC BY-SA 4.0. +#![allow(dead_code, unused_imports)] // for now //! Command syntax tree. use ::std::borrow::Cow; |