commands: deduplicate logic in macros, add more macros
Some checks failed
Build and test / test (push) Failing after 14s
Build and test / build (push) Successful in 58s
Build and test / docs (push) Has been cancelled
Build and test / check (push) Has been cancelled

This commit is contained in:
2025-02-23 19:16:58 +01:00
parent ed7f9a6917
commit 4ca5e05653
9 changed files with 157 additions and 152 deletions

View File

@@ -200,10 +200,10 @@ impl<'a> From<Vec<(&'a str, GenericResponseValue<'a>)>> 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,153 @@ 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_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_optional_property;
pub(crate) use get_next_and_parse_property;
pub(crate) use get_next_optional_property;
pub(crate) use get_next_property;
pub(crate) use get_optional_property;
pub(crate) use get_property;

View File

@@ -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());
}

View File

@@ -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));
}

View File

@@ -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::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)

View File

@@ -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);
}

View File

@@ -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<Self::Response, ResponseParserError> {
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 })
}
}

View File

@@ -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<Self::Response, ResponseParserError> {
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())
}
}

View File

@@ -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::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)

View File

@@ -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::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)