From ee98d6803f660cd2e7521870693d047d673ec53a Mon Sep 17 00:00:00 2001 From: PatriotRossii Date: Sat, 6 Mar 2021 03:12:51 +0600 Subject: [PATCH 1/3] Replace `String` to `&str` --- src/types/destination.rs | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/types/destination.rs b/src/types/destination.rs index ab5ce4a..e6bb4b2 100644 --- a/src/types/destination.rs +++ b/src/types/destination.rs @@ -81,25 +81,24 @@ impl Destination { } } - /// __TODO__: Replace `String` to `&str` - pub fn pick_param(&self) -> String { + pub fn pick_param(&self) -> &'static str { match self { - Album => "file1".to_owned(), - Wall => "photo".to_owned(), - OwnerPhoto => "photo".to_owned(), - Message => "photo".to_owned(), - ChatPhoto => "file".to_owned(), - MarketPhoto => "file".to_owned(), - MarketAlbum => "file".to_owned(), - Audio => "file".to_owned(), - Video => "video_file".to_owned(), - Document => "file".to_owned(), - DocumentWall => "file".to_owned(), - DocumentMessage => "file".to_owned(), - Cover => "photo".to_owned(), - AudioMessage => "file".to_owned(), - StoryPhoto => "file".to_owned(), - StoryVideo => "video_file".to_owned(), + Album => "file1", + Wall => "photo", + OwnerPhoto => "photo", + Message => "photo", + ChatPhoto => "file", + MarketPhoto => "file", + MarketAlbum => "file", + Audio => "file", + Video => "video_file", + Document => "file", + DocumentWall => "file", + DocumentMessage => "file", + Cover => "photo", + AudioMessage => "file", + StoryPhoto => "file", + StoryVideo => "video_file", } } } From 833e5f2b4f8fbf02387024fb1a3759d513a84cee Mon Sep 17 00:00:00 2001 From: PatriotRossii Date: Sun, 7 Mar 2021 03:59:57 +0600 Subject: [PATCH 2/3] Implement Keyboard type --- Cargo.toml | 1 + src/types/keyboard.rs | 407 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 407 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 73dfda4..4c1b4ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ reqwest = { version = "0.10.4", features = ["blocking", "stream", "json"] } tokio = { version = "0.2", features = ["full"] } serde = { version = "1.0.110", features = ["derive"] } serde_json = "1.0.53" +urlencoding = "1" futures = "0.3" anyhow = "1.0.31" tokio-util = "0.3.1" diff --git a/src/types/keyboard.rs b/src/types/keyboard.rs index 1b40f2b..9f308f3 100644 --- a/src/types/keyboard.rs +++ b/src/types/keyboard.rs @@ -1 +1,406 @@ -// TODO: Add macro to make Keyboard +use urlencoding::encode; +use serde::{Serialize}; +use std::fmt; + +#[derive(Debug, Serialize)] +#[serde(rename_all="lowercase")] +pub enum ButtonColor { + Primary, + Secondary, + Negative, + Positive +} + +#[derive(Debug, Serialize)] +pub struct TextButton { + r#type: String, + label: String, + payload: String, +} + +impl TextButton { + pub fn new(label: S1, payload: S2) -> Self + where + S1: Into, + S2: Into, + { + Self { + r#type: "text".to_owned(), + label: label.into(), + payload: payload.into(), + } + } + pub fn label(mut self, label: S) -> Self + where + S: Into + { + self.label = label.into(); + self + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } +} + +impl From for Action { + fn from(val: TextButton) -> Self { + Self::Text(val) + } +} + +#[derive(Debug, Serialize)] +pub struct OpenLinkButton { + r#type: String, + link: String, + label: String, + payload: String, +} + +impl OpenLinkButton { + pub fn new(link: S1, label: S2, payload: S3) -> Self + where + S1: Into, + S2: Into, + S3: Into + { + Self { + r#type: "open_link".to_owned(), + link: link.into(), + label: label.into(), + payload: payload.into(), + } + } + pub fn link(mut self, link: S) -> Self + where + S: Into + { + self.link = link.into(); + self + } + pub fn label(mut self, label: S) -> Self + where + S: Into + { + self.label = label.into(); + self + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } +} + +impl From for Action { + fn from(val: OpenLinkButton) -> Self { + Self::OpenLink(val) + } +} + +#[derive(Debug, Serialize)] +pub struct LocationButton { + r#type: String, + payload: String, +} + +impl LocationButton { + pub fn new(payload: S) -> Self + where + S: Into + { + Self { + r#type: "location".to_owned(), + payload: payload.into(), + } + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } +} + +impl From for Action { + fn from(val: LocationButton) -> Self { + Self::Location(val) + } +} + +#[derive(Debug, Serialize)] +pub struct VKPayButton { + r#type: String, + payload: String, + hash: String, +} + +impl VKPayButton { + pub fn new(payload: S1, hash: S2) -> Self + where + S1: Into, + S2: Into + { + Self { + r#type: "vkpay".to_owned(), + payload: payload.into(), + hash: hash.into(), + } + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } + pub fn hash(mut self, hash: S) -> Self + where + S: Into + { + self.hash = hash.into(); + self + } +} + +impl From for Action { + fn from(val: VKPayButton) -> Self { + Self::VKPay(val) + } +} + +#[derive(Debug, Serialize)] +pub struct VKAppsButton { + r#type: String, + + app_id: i64, + owner_id: i64, + + payload: String, + label: String, + hash: String, +} + +impl VKAppsButton { + pub fn new(app_id: i64, owner_id: i64, payload: S1, label: S2, hash: S3) -> Self + where + S1: Into, + S2: Into, + S3: Into + { + Self { + r#type: "open_app".to_owned(), + + app_id, + owner_id, + + payload: payload.into(), + label: label.into(), + hash: hash.into(), + } + } + pub fn app_id(mut self, app_id: i64) -> Self { + self.app_id = app_id; + self + } + pub fn owner_id(mut self, owner_id: i64) -> Self { + self.owner_id = owner_id; + self + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } + pub fn label(mut self, label: S) -> Self + where + S: Into + { + self.label = label.into(); + self + } + pub fn hash(mut self, hash: S) -> Self + where + S: Into + { + self.hash = hash.into(); + self + } +} + +impl From for Action { + fn from(val: VKAppsButton) -> Self { + Self::VKApps(val) + } +} + +#[derive(Debug, Serialize)] +pub struct CallbackButton { + r#type: String, + label: String, + payload: String, +} + +impl CallbackButton { + pub fn new(label: S1, payload: S2) -> Self + where + S1: Into, + S2: Into + { + Self { + r#type: "text".to_owned(), + label: label.into(), + payload: payload.into(), + } + } + pub fn payload(mut self, payload: S) -> Self + where + S: Into + { + self.payload = payload.into(); + self + } + pub fn label(mut self, label: S) -> Self + where + S: Into + { + self.label = label.into(); + self + } +} + +impl From for Action { + fn from(val: CallbackButton) -> Self { + Action::Callback(val) + } +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum Action { + Text(TextButton), + OpenLink(OpenLinkButton), + Location(LocationButton), + VKPay(VKPayButton), + VKApps(VKAppsButton), + Callback(CallbackButton), +} + +#[derive(Debug, Serialize)] +pub struct Button { + action: Action, + #[serde(skip_serializing_if = "Option::is_none")] + color: Option, +} + +impl Button { + fn validate(action: &Action, color: &Option) -> bool { + match *color { + None => true, + Some(_) => { + matches!(*action, Action::Text(_) | Action::Callback(_)) + } + } + } + pub fn new(action: T, color: Option) -> Result + where T: + Into + { + let action = action.into(); + if Self::validate(&action, &color) { + Ok(Self { action, color }) + } else { + Err("Color can be used only with text or callback button") + } + } + pub fn action(mut self, action: T) -> Self + where T: + Into + { + let action = action.into(); + if Self::validate(&action, &self.color) { + self.action = action; + } + self + } + pub fn color(mut self, color: Option) -> Self { + if Self::validate(&self.action, &color) { + self.color = color; + } + self + } +} + +#[derive(Debug, Serialize)] +pub struct Keyboard { + #[serde(skip_serializing_if = "Option::is_none")] + one_time: Option, + buttons: Vec>, + inline: bool, +} + +impl Keyboard { + fn validate(inline: bool, one_time: &Option) -> bool { + !(matches!(one_time, &Option::Some(_)) && inline) + } + pub fn new(buttons: I1, inline: bool, one_time: Option) -> Result + where + I1: Into>, + I2: Into> + { + if Self::validate(inline, &one_time) { + Ok(Self { + one_time, + buttons: buttons.into().into_iter().map(Into::into).collect(), + inline, + }) + } else { + Err("One_time field is not available for inline keyboard") + } + } + pub fn one_time(mut self, one_time: Option) -> Self { + if Self::validate(self.inline, &one_time) { + self.one_time = one_time; + } + self + } + pub fn buttons(mut self, buttons: I1) -> Self + where + I1: Into>, + I2: Into> + { + self.buttons = buttons.into().into_iter().map(Into::into).collect(); + self + } + pub fn append_row(mut self, buttons: Vec