From 2455caa1cfce2aa693716ce5ff9255104757c829 Mon Sep 17 00:00:00 2001
From: p0lunin <dmytro.polunin@gmail.com>
Date: Sat, 30 May 2020 21:23:49 +0300
Subject: [PATCH] added tests for typed enums

---
 src/dispatching/dispatcher_handler_rx_ext.rs |   2 +-
 src/utils/command.rs                         | 100 +++++++++++++++----
 2 files changed, 81 insertions(+), 21 deletions(-)

diff --git a/src/dispatching/dispatcher_handler_rx_ext.rs b/src/dispatching/dispatcher_handler_rx_ext.rs
index 23a9c029..81af87a3 100644
--- a/src/dispatching/dispatcher_handler_rx_ext.rs
+++ b/src/dispatching/dispatcher_handler_rx_ext.rs
@@ -61,7 +61,7 @@ where
                         cx,
                         command,
                     )
-                })
+                }).ok()
             }
         }))
     }
diff --git a/src/utils/command.rs b/src/utils/command.rs
index 24618001..8e733482 100644
--- a/src/utils/command.rs
+++ b/src/utils/command.rs
@@ -57,7 +57,6 @@
 //! [examples/admin_bot]: https://github.com/teloxide/teloxide/blob/master/examples/miltiple_handlers_bot/
 
 pub use teloxide_macros::BotCommand;
-use std::str::FromStr;
 
 /// An enumeration of bot's commands.
 ///
@@ -102,25 +101,27 @@ use std::str::FromStr;
 /// All variant attributes overlap the `enum` attributes.
 pub trait BotCommand: Sized {
     fn descriptions() -> String;
-    fn parse<N>(s: &str, bot_name: N) -> Option<Self>
+    fn parse<N>(s: &str, bot_name: N) -> Result<Self, ParseError>
     where
         N: Into<String>;
 }
 
-pub trait CommandArgument {
-    fn parse(args: &mut String) -> Option<Self> where Self: Sized;
-}
-
-impl<T: FromStr> CommandArgument for T {
-    fn parse(args: &mut String) -> Option<Self> {
-        match T::from_str(&args) {
-            Ok(res) => {
-                args.clear();
-                Some(res)
-            }
-            Err(_) => None
-        }
-    }
+#[derive(Debug)]
+pub enum ParseError {
+    LessArguments {
+        expected: u8,
+        found: u8,
+        message: String,
+    },
+    ManyArguments {
+        expected: u8,
+        found: u8,
+        message: String,
+    },
+    UncorrectFormat,
+    UncorrectCommand(String),
+    WrongBotName(String),
+    Custom(String)
 }
 
 /// Parses a string into a command with args.
@@ -212,8 +213,8 @@ mod tests {
         }
 
         let data = "/start arg1 arg2";
-        let expected = Some(DefaultCommands::Start("arg1 arg2".to_string()));
-        let actual = DefaultCommands::parse(data, "");
+        let expected = DefaultCommands::Start("arg1 arg2".to_string());
+        let actual = DefaultCommands::parse(data, "").unwrap();
         assert_eq!(actual, expected)
     }
 
@@ -228,8 +229,8 @@ mod tests {
         }
 
         let data = "!start arg1 arg2";
-        let expected = Some(DefaultCommands::Start("arg1 arg2".to_string()));
-        let actual = DefaultCommands::parse(data, "");
+        let expected = DefaultCommands::Start("arg1 arg2".to_string());
+        let actual = DefaultCommands::parse(data, "").unwrap();
         assert_eq!(actual, expected)
     }
 
@@ -293,4 +294,63 @@ mod tests {
             DefaultCommands::parse("/start@MyNameBot", "MyNameBot").unwrap()
         );
     }
+
+    #[test]
+    fn parse_with_split() {
+        #[command(rename = "lowercase")]
+        #[command(parse_with = "split")]
+        #[derive(BotCommand, Debug, PartialEq)]
+        enum DefaultCommands {
+            Start(u8, String),
+            Help,
+        }
+
+        assert_eq!(
+            DefaultCommands::Start(10, "hello".to_string()),
+            DefaultCommands::parse("/start 10 hello", "").unwrap()
+        );
+    }
+
+    #[test]
+    fn parse_with_split2() {
+        #[command(rename = "lowercase")]
+        #[command(parse_with = "split", separator = "|")]
+        #[derive(BotCommand, Debug, PartialEq)]
+        enum DefaultCommands {
+            Start(u8, String),
+            Help,
+        }
+
+        assert_eq!(
+            DefaultCommands::Start(10, "hello".to_string()),
+            DefaultCommands::parse("/start 10|hello", "").unwrap()
+        );
+    }
+
+    #[test]
+    fn parse_custom_parser() {
+        fn custom_parse_function(s: String) -> Result<(u8, String), ParseError> {
+            let vec = s.split_whitespace().collect::<Vec<_>>();
+            let (left, right) = match vec.as_slice() {
+                [l, r] => (l, r),
+                _ => return Err(ParseError::UncorrectFormat)
+            };
+            left.parse::<u8>()
+                .map(|res| (res, right.to_string()))
+                .map_err(|_| ParseError::Custom("First argument must be a integer!".to_owned()))
+        }
+
+        #[command(rename = "lowercase")]
+        #[derive(BotCommand, Debug, PartialEq)]
+        enum DefaultCommands {
+            #[command(parse_with="custom_parse_function")]
+            Start(u8, String),
+            Help,
+        }
+
+        assert_eq!(
+            DefaultCommands::Start(10, "hello".to_string()),
+            DefaultCommands::parse("/start 10 hello", "").unwrap()
+        );
+    }
 }