summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2021-12-05 12:21:19 -0300
committerSoniEx2 <endermoneymod@gmail.com>2021-12-05 12:21:19 -0300
commitea604e09b8338b9fdb66c3921ea3dc955168e79b (patch)
treea839b77eb3832385fa6e404b0e0a6f6fbdeb8617
parent23712f84e2f72bc8ac516882888d011e13f8c49a (diff)
Add StringArgumentType
-rw-r--r--src/args.rs93
-rw-r--r--src/strcursor.rs15
-rw-r--r--tests/arguments.rs45
3 files changed, 136 insertions, 17 deletions
diff --git a/src/args.rs b/src/args.rs
index ca35bfb..b3bd7ca 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -134,7 +134,6 @@ impl<T: ArgumentType<S, E> + Send + Sync, 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;
 
@@ -324,3 +323,95 @@ where
         Cow::Borrowed(&["0", "1.2", ".5", "-1", "-.5", "-1234.56"])
     }
 }
+
+/// A string argument.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
+pub struct StringArgumentType(StringMode);
+
+/// Creates a string argument that accepts simple words.
+///
+/// # Examples
+///
+/// ```rust
+/// use ::iosonism::args::word;
+///
+/// let argtype = word();
+/// ```
+pub fn word() -> StringArgumentType {
+    StringArgumentType(StringMode::SingleWord)
+}
+
+/// Creates a string argument that accepts simple words and quoted strings.
+///
+/// # Examples
+///
+/// ```rust
+/// use ::iosonism::args::string;
+///
+/// let argtype = string();
+/// ```
+pub fn string() -> StringArgumentType {
+    StringArgumentType(StringMode::QuotablePhrase)
+}
+
+/// Creates a string argument that accepts simple text until the end of the
+/// input.
+///
+/// # Examples
+///
+/// ```rust
+/// use ::iosonism::args::greedy_string;
+///
+/// let argtype = greedy_string();
+/// ```
+pub fn greedy_string() -> StringArgumentType {
+    StringArgumentType(StringMode::GreedyPhrase)
+}
+
+/// The "mode" of parsing a string.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
+enum StringMode {
+    SingleWord,
+    QuotablePhrase,
+    GreedyPhrase,
+}
+
+/// An `ArgumentType` for strings.
+impl<S, E> ArgumentType<S, E> for StringArgumentType
+where for<'i> E: ReadError<'i, Cursor<&'i str>> {
+    /// A `StringArgumentType` parses a string.
+    type Result = String;
+
+    /// Attempts to parse a string from the `reader`.
+    fn parse<'i>(
+        &self,
+        reader: &mut Cursor<&'i str>,
+    ) -> Result<String, E> where E: 'i {
+        match self {
+            Self(StringMode::SingleWord) => {
+                Ok(reader.read_unquoted_str().into())
+            },
+            Self(StringMode::QuotablePhrase) => reader.read_string(),
+            Self(StringMode::GreedyPhrase) => {
+                let text = reader.get_remaining().into();
+                reader.set_position(reader.total_len() as u64);
+                Ok(text)
+            },
+        }
+    }
+
+    /// Returns examples
+    fn get_examples(&self) -> Cow<'static, [&str]> {
+        match self {
+            Self(StringMode::SingleWord) => {
+                Cow::Borrowed(&["word", "words_With_underscores"])
+            },
+            Self(StringMode::QuotablePhrase) => {
+                Cow::Borrowed(&["\"quoted phrase\"", "word", "\"\""])
+            },
+            Self(StringMode::GreedyPhrase) => {
+                Cow::Borrowed(&["word", "with spaces", "text \"and symbols\""])
+            },
+        }
+    }
+}
diff --git a/src/strcursor.rs b/src/strcursor.rs
index d733ade..b7c0e5f 100644
--- a/src/strcursor.rs
+++ b/src/strcursor.rs
@@ -18,6 +18,9 @@ use crate::error::ReadError;
 /// `getRemainingLength` (use `get_remaining().len()` or
 /// `remaining_slice().len()`) and `getTotalLength` (use `get_ref().len()`).
 pub trait StringReader<'a>: Sized {
+    /// Returns the total length of the string.
+    //#[inline]
+    fn total_len(&self) -> usize;
     /// Returns the part of the string that has been read so far.
     ///
     /// # Panics
@@ -147,6 +150,10 @@ pub trait StringReader<'a>: Sized {
 
 impl<'a> StringReader<'a> for Cursor<&'a str> {
     #[inline]
+    fn total_len(&self) -> usize {
+        self.get_ref().len()
+    }
+    #[inline]
     fn get_read(&self) -> &'a str {
         &self.get_ref()[..(self.position() as usize)]
     }
@@ -157,7 +164,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> {
     #[inline]
     fn can_read_n(&self, len: usize) -> bool {
         // NOTE: NOT overflow-aware!
-        self.position() as usize + len <= self.get_ref().len()
+        self.position() as usize + len <= self.total_len()
     }
     #[inline]
     fn peek_n(&self, offset: usize) -> char {
@@ -183,7 +190,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> {
     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();
+        let total = self.total_len();
         let end = total - {
             self.get_remaining().trim_start_matches(number_chars).len()
         };
@@ -204,7 +211,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> {
     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();
+        let total = self.total_len();
         let end = total - {
             self.get_remaining().trim_start_matches(number_chars).len()
         };
@@ -240,7 +247,7 @@ impl<'a> StringReader<'a> for Cursor<&'a str> {
         // there's no easy way to grab start matches, so we have to do something
         // a bit more involved.
         let start = self.position() as usize;
-        let total = self.get_ref().len();
+        let total = self.total_len();
         let end = total - {
             self.get_remaining().trim_start_matches(unquoted_chars).len()
         };
diff --git a/tests/arguments.rs b/tests/arguments.rs
index 55b4a4b..0029998 100644
--- a/tests/arguments.rs
+++ b/tests/arguments.rs
@@ -13,7 +13,10 @@ use ::iosonism::args::BoolArgumentType;
 use ::iosonism::args::bounded_float;
 use ::iosonism::args::bounded_integer;
 use ::iosonism::args::float;
+use ::iosonism::args::greedy_string;
 use ::iosonism::args::integer;
+use ::iosonism::args::string;
+use ::iosonism::args::word;
 use ::iosonism::strcursor::StringReader;
 
 mod common;
@@ -45,10 +48,7 @@ fn test_bool__parse() {
 fn test_i32__parse() {
     let mut reader = Cursor::new("15");
     assert_eq!(
-        ArgumentType::<(), ErrorPanic>::parse(
-            &integer::<i32>(),
-            &mut reader,
-        ),
+        ArgumentType::<(), ErrorPanic>::parse(&integer::<i32>(), &mut reader),
         Ok(15),
     );
     assert!(!reader.can_read());
@@ -74,10 +74,7 @@ fn test_i32__parse__range() {
 fn test_i64__parse() {
     let mut reader = Cursor::new("15");
     assert_eq!(
-        ArgumentType::<(), ErrorPanic>::parse(
-            &integer::<i64>(),
-            &mut reader,
-        ),
+        ArgumentType::<(), ErrorPanic>::parse(&integer::<i64>(), &mut reader),
         Ok(15),
     );
     assert!(!reader.can_read());
@@ -103,10 +100,7 @@ fn test_i64__parse__range() {
 fn test_f32__parse() {
     let mut reader = Cursor::new("15");
     assert_eq!(
-        ArgumentType::<(), ErrorPanic>::parse(
-            &float::<f32>(),
-            &mut reader,
-        ),
+        ArgumentType::<(), ErrorPanic>::parse(&float::<f32>(), &mut reader),
         Ok(15.0),
     );
     assert!(!reader.can_read());
@@ -157,3 +151,30 @@ fn test_f64__parse__range() {
     }
 }
 
+#[test]
+fn test_string__parse__word() {
+    let mut reader = Cursor::new("hello");
+    assert_eq!(
+        ArgumentType::<(), ErrorPanic>::parse(&word(), &mut reader),
+        Ok("hello".into()),
+    );
+}
+
+#[test]
+fn test_string__parse__string() {
+    let mut reader = Cursor::new("\"hello world\"");
+    assert_eq!(
+        ArgumentType::<(), ErrorPanic>::parse(&string(), &mut reader),
+        Ok("hello world".into()),
+    );
+}
+
+#[test]
+fn test_string__parse__greedy_string() {
+    let mut reader = Cursor::new("Hello world! This is a test.");
+    assert_eq!(
+        ArgumentType::<(), ErrorPanic>::parse(&greedy_string(), &mut reader),
+        Ok("Hello world! This is a test.".into()),
+    );
+    assert!(!reader.can_read());
+}