From 380a4aed2c6fd232bff690af4a37de60ea2f7780 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Sun, 23 Feb 2025 19:16:58 +0100 Subject: [PATCH] commands: deduplicate logic in macros, add more macros --- src/commands.rs | 186 ++++++++++++------ src/commands/client_to_client/channels.rs | 11 +- src/commands/client_to_client/readmessages.rs | 22 +-- src/commands/music_database/list.rs | 11 +- .../partition_commands/listpartitions.rs | 14 +- src/commands/queue/addid.rs | 16 +- src/commands/stickers/sticker_get.rs | 24 +-- src/commands/stickers/stickernames.rs | 11 +- src/commands/stickers/stickertypes.rs | 11 +- 9 files changed, 154 insertions(+), 152 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 96ad1f4..aa8b8d3 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -200,10 +200,10 @@ impl<'a> From)>> for ResponseAttributes<' /* Parsing Helpers */ /*******************/ -macro_rules! get_property { - ($parts:expr, $name:literal, $variant:ident) => { - match $parts.get($name) { - Some(crate::commands::GenericResponseValue::$variant(value)) => *value, +macro_rules! _expect_property_type { + ($property:expr, $name:expr, $variant:ident) => { + match $property { + Some(crate::commands::GenericResponseValue::$variant(value)) => Some(value), Some(value) => { let actual_type = match value { crate::commands::GenericResponseValue::Text(_) => "Text", @@ -216,82 +216,150 @@ macro_rules! get_property { ), ); } + None => None, + } + }; +} + +macro_rules! _parse_optional_property_type { + ($name:expr, $property:expr) => { + $property + .map(|value| { + value.parse().map_err(|_| { + crate::commands::ResponseParserError::InvalidProperty($name, value) + }) + }) + .transpose()? + }; +} + +macro_rules! _unwrap_optional_property_type { + ($name:expr, $property:expr) => { + match $property { + Some(value) => value, None => return Err(crate::commands::ResponseParserError::MissingProperty($name)), } }; } +macro_rules! expect_optional_property_type { + ($property:expr, $name:expr, $variant:ident) => { + crate::commands::_expect_property_type!($property, $name, $variant) + }; +} + +macro_rules! expect_property_type { + ($property:expr, $name:expr, $variant:ident) => {{ + let prop = crate::commands::_expect_property_type!($property, $name, $variant); + crate::commands::_unwrap_optional_property_type!($name, prop) + }}; +} + macro_rules! get_optional_property { ($parts:expr, $name:literal, $variant:ident) => { - match $parts.get($name) { - Some(crate::commands::GenericResponseValue::$variant(value)) => Some(*value), - Some(value) => { - let actual_type = match value { - crate::commands::GenericResponseValue::Text(_) => "Text", - crate::commands::GenericResponseValue::Binary(_) => "Binary", - }; - return Err( - crate::commands::ResponseParserError::UnexpectedPropertyType( - $name, - actual_type, - ), - ); - } - None => None, - } + crate::commands::_expect_property_type!({ $parts.get($name).map(|v| *v) }, $name, $variant) }; } -macro_rules! get_and_parse_property { - ($parts:ident, $name:literal, $variant:ident) => { - match $parts.get($name) { - Some(crate::commands::GenericResponseValue::$variant(value)) => (*value) - .parse() - .map_err(|_| crate::commands::ResponseParserError::InvalidProperty($name, value))?, - Some(value) => { - let actual_type = match value { - crate::commands::GenericResponseValue::Text(_) => "Text", - crate::commands::GenericResponseValue::Binary(_) => "Binary", - }; - return Err( - crate::commands::ResponseParserError::UnexpectedPropertyType( - $name, - actual_type, - ), - ); - } - None => return Err(crate::commands::ResponseParserError::MissingProperty($name)), - } - }; +macro_rules! get_property { + ($parts:expr, $name:literal, $variant:ident) => {{ + let prop = crate::commands::_expect_property_type!( + { $parts.get($name).map(|v| *v) }, + $name, + $variant + ); + crate::commands::_unwrap_optional_property_type!($name, prop) + }}; } macro_rules! get_and_parse_optional_property { - ($parts:ident, $name:literal, $variant:ident) => { - match $parts.get($name) { - Some(crate::commands::GenericResponseValue::$variant(value)) => { - Some((*value).parse().map_err(|_| { - crate::commands::ResponseParserError::InvalidProperty($name, value) - })?) - } - Some(value) => { - let actual_type = match value { - crate::commands::GenericResponseValue::Text(_) => "Text", - crate::commands::GenericResponseValue::Binary(_) => "Binary", - }; - return Err( - crate::commands::ResponseParserError::UnexpectedPropertyType( - $name, - actual_type, - ), - ); + ($parts:ident, $name:literal, $variant:ident) => {{ + let prop = crate::commands::_expect_property_type!( + { $parts.get($name).map(|v| *v) }, + $name, + $variant + ); + crate::commands::_parse_optional_property_type!($name, prop) + }}; +} + +macro_rules! get_and_parse_property { + ($parts:ident, $name:literal, $variant:ident) => {{ + let prop = crate::commands::_expect_property_type!( + { $parts.get($name).map(|v| *v) }, + $name, + $variant + ); + let prop = crate::commands::_parse_optional_property_type!($name, prop); + crate::commands::_unwrap_optional_property_type!($name, prop) + }}; +} + +macro_rules! get_next_optional_property { + ($parts:ident, $variant:ident) => { + match $parts.next() { + Some((name, value)) => { + crate::commands::_expect_property_type!({ Some(value) }, name, $variant) + .map(|value| (name, value)) } None => None, } }; } +macro_rules! get_next_property { + ($parts:ident, $variant:ident) => { + match $parts.next() { + Some((name, value)) => ( + name, + crate::commands::_expect_property_type!({ Some(value) }, name, $variant).unwrap(), + ), + None => return Err(crate::commands::ResponseParserError::UnexpectedEOF), + } + }; +} + +macro_rules! get_next_and_parse_optional_property { + ($parts:ident, $variant:ident) => { + match $parts.next() { + Some((name, value)) => { + let prop = crate::commands::_expect_property_type!({ Some(value) }, name, $variant); + prop.map(|value| { + ( + name, + crate::commands::_parse_optional_property_type!(name, value), + ) + }) + } + None => None, + } + }; +} + +macro_rules! get_next_and_parse_property { + ($parts:ident, $variant:ident) => { + match $parts.next() { + Some((name, value)) => { + let prop = crate::commands::_expect_property_type!({ Some(value) }, name, $variant); + let prop = crate::commands::_parse_optional_property_type!(name, prop); + ( + name, + crate::commands::_unwrap_optional_property_type!(name, prop), + ) + } + None => return Err(crate::commands::ResponseParserError::UnexpectedEOF), + } + }; +} + +pub(crate) use _expect_property_type; +pub(crate) use _parse_optional_property_type; +pub(crate) use _unwrap_optional_property_type; +pub(crate) use expect_property_type; pub(crate) use get_and_parse_optional_property; pub(crate) use get_and_parse_property; +pub(crate) use get_next_and_parse_property; +pub(crate) use get_next_property; pub(crate) use get_optional_property; pub(crate) use get_property; diff --git a/src/commands/client_to_client/channels.rs b/src/commands/client_to_client/channels.rs index fee6cd4..f88f5f5 100644 --- a/src/commands/client_to_client/channels.rs +++ b/src/commands/client_to_client/channels.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, + expect_property_type, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -29,14 +29,7 @@ impl Command for Channels { let mut channel_names = Vec::with_capacity(parts.len()); for (key, value) in parts { debug_assert!(key == "channels"); - let channel_name = match value { - GenericResponseValue::Text(s) => s, - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType( - "channels", "Binary", - )); - } - }; + let channel_name = expect_property_type!(Some(value), "channels", Text); channel_names.push(channel_name.to_string()); } diff --git a/src/commands/client_to_client/readmessages.rs b/src/commands/client_to_client/readmessages.rs index 3c3a37c..2861a50 100644 --- a/src/commands/client_to_client/readmessages.rs +++ b/src/commands/client_to_client/readmessages.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, + expect_property_type, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -33,26 +33,12 @@ impl Command for ReadMessages { for channel_message_pair in parts.chunks_exact(2) { let (ckey, cvalue) = channel_message_pair[0]; let (mkey, mvalue) = channel_message_pair[1]; + debug_assert!(ckey == "channel"); debug_assert!(mkey == "message"); - let channel = match cvalue { - GenericResponseValue::Text(s) => s.to_string(), - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType( - "channel", "Binary", - )) - } - }; - - let message = match mvalue { - GenericResponseValue::Text(s) => s.to_string(), - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType( - "message", "Binary", - )) - } - }; + let channel = expect_property_type!(Some(cvalue), "channel", Text).to_string(); + let message = expect_property_type!(Some(mvalue), "message", Text).to_string(); messages.push((channel, message)); } diff --git a/src/commands/music_database/list.rs b/src/commands/music_database/list.rs index 0ca8ef5..00092e2 100644 --- a/src/commands/music_database/list.rs +++ b/src/commands/music_database/list.rs @@ -1,6 +1,6 @@ use crate::{ commands::{ - Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, + expect_property_type, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, filter::parse_filter, @@ -49,13 +49,8 @@ impl Command for List { let list = parts .0 - .iter() - .map(|(k, v)| match v { - GenericResponseValue::Text(value) => Ok(value.to_string()), - GenericResponseValue::Binary(_) => { - Err(ResponseParserError::UnexpectedPropertyType(k, "Binary")) - } - }) + .into_iter() + .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string())) .collect::, ResponseParserError>>()?; Ok(list) diff --git a/src/commands/partition_commands/listpartitions.rs b/src/commands/partition_commands/listpartitions.rs index 1fa9e5d..0af883c 100644 --- a/src/commands/partition_commands/listpartitions.rs +++ b/src/commands/partition_commands/listpartitions.rs @@ -1,5 +1,5 @@ use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, + expect_property_type, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -24,17 +24,7 @@ impl Command for ListPartitions { let mut partitions = Vec::with_capacity(parts.len()); for (key, value) in parts.into_iter() { debug_assert_eq!(key, "partition"); - - let partition = match value { - GenericResponseValue::Text(name) => name.to_string(), - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType( - "partition", - "Binary", - )) - } - }; - + let partition = expect_property_type!(Some(value), "partition", Text).to_string(); partitions.push(partition); } diff --git a/src/commands/queue/addid.rs b/src/commands/queue/addid.rs index 42fc089..bb3ecd5 100644 --- a/src/commands/queue/addid.rs +++ b/src/commands/queue/addid.rs @@ -1,6 +1,6 @@ use crate::{ commands::{ - Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, + get_next_and_parse_property, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{SongId, SongPosition}, @@ -39,17 +39,9 @@ impl Command for AddId { parts: ResponseAttributes<'_>, ) -> Result { let parts: Vec<_> = parts.into(); - let (key, value) = parts.first().ok_or(ResponseParserError::UnexpectedEOF)?; - debug_assert!(key == &"Id"); - let value = match value { - GenericResponseValue::Text(value) => value, - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType("Id", "Binary")) - } - }; - let id = value - .parse() - .map_err(|_| ResponseParserError::InvalidProperty("Id", value.to_owned()))?; + let mut iter = parts.into_iter(); + let (key, id) = get_next_and_parse_property!(iter, Text); + debug_assert!(key == "Id"); Ok(AddIdResponse { id }) } } diff --git a/src/commands/stickers/sticker_get.rs b/src/commands/stickers/sticker_get.rs index 4421f47..9a31582 100644 --- a/src/commands/stickers/sticker_get.rs +++ b/src/commands/stickers/sticker_get.rs @@ -1,5 +1,5 @@ use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, + get_next_property, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -36,22 +36,10 @@ impl Command for StickerGet { parts: ResponseAttributes<'_>, ) -> Result { let parts: Vec<_> = parts.into(); - let mut parts = parts.into_iter(); - let sticker = parts.next().ok_or(ResponseParserError::UnexpectedEOF)?; - - debug_assert!(parts.next().is_none()); - - debug_assert!(sticker.0 == "sticker"); - - let sticker = match sticker.1 { - GenericResponseValue::Text(s) => s.to_string(), - GenericResponseValue::Binary(_) => { - return Err(ResponseParserError::UnexpectedPropertyType( - "sticker", "Binary", - )) - } - }; - - Ok(sticker) + let mut parts = parts.into_iter().peekable(); + let (key, sticker) = get_next_property!(parts, Text); + debug_assert!(parts.peek().is_none()); + debug_assert!(key == "sticker"); + Ok(sticker.to_string()) } } diff --git a/src/commands/stickers/stickernames.rs b/src/commands/stickers/stickernames.rs index 838e3bd..0ca4db1 100644 --- a/src/commands/stickers/stickernames.rs +++ b/src/commands/stickers/stickernames.rs @@ -1,5 +1,5 @@ use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, + expect_property_type, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -24,13 +24,8 @@ impl Command for StickerNames { let list = parts .0 - .iter() - .map(|(_, v)| match v { - GenericResponseValue::Text(value) => Ok(value.to_string()), - GenericResponseValue::Binary(_) => Err( - ResponseParserError::UnexpectedPropertyType("name", "Binary"), - ), - }) + .into_iter() + .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string())) .collect::, ResponseParserError>>()?; Ok(list) diff --git a/src/commands/stickers/stickertypes.rs b/src/commands/stickers/stickertypes.rs index 1f8bc2e..b20a011 100644 --- a/src/commands/stickers/stickertypes.rs +++ b/src/commands/stickers/stickertypes.rs @@ -1,5 +1,5 @@ use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, + expect_property_type, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, }; @@ -24,13 +24,8 @@ impl Command for StickerTypes { let list = parts .0 - .iter() - .map(|(_, v)| match v { - GenericResponseValue::Text(value) => Ok(value.to_string()), - GenericResponseValue::Binary(_) => Err( - ResponseParserError::UnexpectedPropertyType("stickertype", "Binary"), - ), - }) + .into_iter() + .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string())) .collect::, ResponseParserError>>()?; Ok(list)