added parse_command, TelegramCommandEnum trait, parse_command_into_enum

This commit is contained in:
p0lunin 2020-01-18 15:14:31 +02:00
parent 6a6e49d60a
commit db5145911b
6 changed files with 145 additions and 1 deletions

View file

@ -23,4 +23,5 @@ futures = "0.3.1"
pin-project = "0.4.6"
serde_with_macros = "1.0.1"
either = "1.5.3"
mime = "0.3.16"
mime = "0.3.16"
teloxide-macros = { path = "teloxide-macros" }

View file

@ -15,3 +15,5 @@ pub mod dispatching;
pub mod requests;
pub mod types;
pub mod utils;
extern crate teloxide_macros;

View file

@ -1,2 +1,5 @@
pub mod html;
pub mod markdown;
pub use parsers::*;
mod parsers;

87
src/utils/parsers.rs Normal file
View file

@ -0,0 +1,87 @@
pub use teloxide_macros::TelegramCommandEnum;
/// Enum for telegram commands
///
/// Example:
/// ```
/// use teloxide::utils::TelegramCommandEnum;
/// use teloxide::utils::parse_command_into_enum;
/// #[derive(TelegramCommandEnum, PartialEq, Debug)]
/// enum TelegramCommand {
/// Start,
/// Help,
/// }
/// let (command, args) = parse_command_into_enum::<TelegramCommand>("/", "/start arg1 arg2").unwrap();
/// assert_eq!(command, TelegramCommand::Start);
/// assert_eq!(args, vec!["arg1", "arg2"]);
/// ```
pub trait TelegramCommandEnum: Sized {
fn try_from(s: &str) -> Option<Self>;
}
pub fn parse_command_into_enum<'a, T>(
prefix: &str,
text: &'a str,
) -> Option<(T, Vec<&'a str>)>
where
T: TelegramCommandEnum,
{
let (command, args) = parse_command(prefix,text)?;
return match T::try_from(command) {
Some(command) => Some((command, args)),
_ => None,
};
}
/// Function which parse string and return command with args
///
/// Example:
/// ```
/// use teloxide::utils::parse_command;
/// let text = "/ban 5 hours";
/// let (command, args) = parse_command("/", text).unwrap();
/// assert_eq!(command, "ban");
/// assert_eq!(args, vec!["5", "hours"]);
/// ```
pub fn parse_command<'a>(prefix: &str, text: &'a str) -> Option<(&'a str, Vec<&'a str>)> {
if !text.starts_with(prefix) {
return None;
}
let mut words = text.split_whitespace();
let command = &words.next()?[1..];
Some((command, words.collect()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_command_with_args_() {
let data = "/command arg1 arg2";
let expected = Some(("command", vec!["arg1", "arg2"]));
let actual = parse_command("/", data);
assert_eq!(actual, expected)
}
#[test]
fn parse_command_with_args_without_args() {
let data = "/command";
let expected = Some(("command", vec![]));
let actual = parse_command("/", data);
assert_eq!(actual, expected)
}
#[test]
fn parse_command__with_args() {
#[derive(TelegramCommandEnum, Debug, PartialEq)]
enum DefaultCommands {
Start,
Help,
}
let data = "/start arg1 arg2";
let expected = Some((DefaultCommands::Start, vec!["arg1", "arg2"]));
let actual = parse_command_into_enum::<DefaultCommands>("/", data);
assert_eq!(actual, expected)
}
}

View file

@ -0,0 +1,14 @@
[package]
name = "teloxide-macros"
version = "0.1.0"
authors = ["p0lunin <dmytro.polunin@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quote = "1.0.2"
syn = "1.0.13"
[lib]
proc-macro = true

View file

@ -0,0 +1,37 @@
extern crate proc_macro;
extern crate syn;
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, parse_macro_input};
#[proc_macro_derive(TelegramCommandEnum)]
pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as DeriveInput);
let (variant, variant_str) = match &input.data {
syn::Data::Enum(data) => {
(data.variants.iter(),
data.variants.iter().map(|variant| {
variant.ident.to_string().to_lowercase()
}))
}
_ => panic!("TelegramCommandEnum allowed only for enums")
};
let ident = input.ident;
let expanded = quote! {
impl TelegramCommandEnum for #ident {
fn try_from(value: &str) -> Option<Self> {
match value {
#(
#variant_str => Some(Self::#variant),
)*
_ => None
}
}
}
};
let tokens = TokenStream::from(expanded);
tokens
}