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 */ /* Parsing Helpers */
/*******************/ /*******************/
macro_rules! get_property { macro_rules! _expect_property_type {
($parts:expr, $name:literal, $variant:ident) => { ($property:expr, $name:expr, $variant:ident) => {
match $parts.get($name) { match $property {
Some(crate::commands::GenericResponseValue::$variant(value)) => *value, Some(crate::commands::GenericResponseValue::$variant(value)) => Some(value),
Some(value) => { Some(value) => {
let actual_type = match value { let actual_type = match value {
crate::commands::GenericResponseValue::Text(_) => "Text", 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)), 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 { macro_rules! get_optional_property {
($parts:expr, $name:literal, $variant:ident) => { ($parts:expr, $name:literal, $variant:ident) => {
match $parts.get($name) { crate::commands::_expect_property_type!({ $parts.get($name).map(|v| *v) }, $name, $variant)
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,
}
}; };
} }
macro_rules! get_and_parse_property { macro_rules! get_property {
($parts:ident, $name:literal, $variant:ident) => { ($parts:expr, $name:literal, $variant:ident) => {{
match $parts.get($name) { let prop = crate::commands::_expect_property_type!(
Some(crate::commands::GenericResponseValue::$variant(value)) => (*value) { $parts.get($name).map(|v| *v) },
.parse() $name,
.map_err(|_| crate::commands::ResponseParserError::InvalidProperty($name, value))?, $variant
Some(value) => { );
let actual_type = match value { crate::commands::_unwrap_optional_property_type!($name, prop)
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_and_parse_optional_property { macro_rules! get_and_parse_optional_property {
($parts:ident, $name:literal, $variant:ident) => { ($parts:ident, $name:literal, $variant:ident) => {{
match $parts.get($name) { let prop = crate::commands::_expect_property_type!(
Some(crate::commands::GenericResponseValue::$variant(value)) => { { $parts.get($name).map(|v| *v) },
Some((*value).parse().map_err(|_| { $name,
crate::commands::ResponseParserError::InvalidProperty($name, value) $variant
})?) );
} crate::commands::_parse_optional_property_type!($name, prop)
Some(value) => { }};
let actual_type = match value { }
crate::commands::GenericResponseValue::Text(_) => "Text",
crate::commands::GenericResponseValue::Binary(_) => "Binary", macro_rules! get_and_parse_property {
}; ($parts:ident, $name:literal, $variant:ident) => {{
return Err( let prop = crate::commands::_expect_property_type!(
crate::commands::ResponseParserError::UnexpectedPropertyType( { $parts.get($name).map(|v| *v) },
$name, $name,
actual_type, $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, 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_optional_property;
pub(crate) use get_and_parse_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_optional_property;
pub(crate) use get_property; pub(crate) use get_property;

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}; };
@@ -29,14 +29,7 @@ impl Command for Channels {
let mut channel_names = Vec::with_capacity(parts.len()); let mut channel_names = Vec::with_capacity(parts.len());
for (key, value) in parts { for (key, value) in parts {
debug_assert!(key == "channels"); debug_assert!(key == "channels");
let channel_name = match value { let channel_name = expect_property_type!(Some(value), "channels", Text);
GenericResponseValue::Text(s) => s,
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"channels", "Binary",
));
}
};
channel_names.push(channel_name.to_string()); channel_names.push(channel_name.to_string());
} }

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}; };
@@ -33,26 +33,12 @@ impl Command for ReadMessages {
for channel_message_pair in parts.chunks_exact(2) { for channel_message_pair in parts.chunks_exact(2) {
let (ckey, cvalue) = channel_message_pair[0]; let (ckey, cvalue) = channel_message_pair[0];
let (mkey, mvalue) = channel_message_pair[1]; let (mkey, mvalue) = channel_message_pair[1];
debug_assert!(ckey == "channel"); debug_assert!(ckey == "channel");
debug_assert!(mkey == "message"); debug_assert!(mkey == "message");
let channel = match cvalue { let channel = expect_property_type!(Some(cvalue), "channel", Text).to_string();
GenericResponseValue::Text(s) => s.to_string(), let message = expect_property_type!(Some(mvalue), "message", Text).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",
))
}
};
messages.push((channel, message)); messages.push((channel, message));
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
commands::{ commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, expect_property_type, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError, ResponseAttributes, ResponseParserError,
}, },
filter::parse_filter, filter::parse_filter,
@@ -49,13 +49,8 @@ impl Command for List {
let list = parts let list = parts
.0 .0
.iter() .into_iter()
.map(|(k, v)| match v { .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
GenericResponseValue::Text(value) => Ok(value.to_string()),
GenericResponseValue::Binary(_) => {
Err(ResponseParserError::UnexpectedPropertyType(k, "Binary"))
}
})
.collect::<Result<Vec<_>, ResponseParserError>>()?; .collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list) Ok(list)

View File

@@ -1,5 +1,5 @@
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}; };
@@ -24,17 +24,7 @@ impl Command for ListPartitions {
let mut partitions = Vec::with_capacity(parts.len()); let mut partitions = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() { for (key, value) in parts.into_iter() {
debug_assert_eq!(key, "partition"); debug_assert_eq!(key, "partition");
let partition = expect_property_type!(Some(value), "partition", Text).to_string();
let partition = match value {
GenericResponseValue::Text(name) => name.to_string(),
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"partition",
"Binary",
))
}
};
partitions.push(partition); partitions.push(partition);
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
commands::{ commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, get_next_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError, ResponseAttributes, ResponseParserError,
}, },
common::{SongId, SongPosition}, common::{SongId, SongPosition},
@@ -39,17 +39,9 @@ impl Command for AddId {
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into(); let parts: Vec<_> = parts.into();
let (key, value) = parts.first().ok_or(ResponseParserError::UnexpectedEOF)?; let mut iter = parts.into_iter();
debug_assert!(key == &"Id"); let (key, id) = get_next_and_parse_property!(iter, Text);
let value = match value { debug_assert!(key == "Id");
GenericResponseValue::Text(value) => value,
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType("Id", "Binary"))
}
};
let id = value
.parse()
.map_err(|_| ResponseParserError::InvalidProperty("Id", value.to_owned()))?;
Ok(AddIdResponse { id }) Ok(AddIdResponse { id })
} }
} }

View File

@@ -1,5 +1,5 @@
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, get_next_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError, ResponseAttributes, ResponseParserError,
}; };
@@ -36,22 +36,10 @@ impl Command for StickerGet {
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into(); let parts: Vec<_> = parts.into();
let mut parts = parts.into_iter(); let mut parts = parts.into_iter().peekable();
let sticker = parts.next().ok_or(ResponseParserError::UnexpectedEOF)?; let (key, sticker) = get_next_property!(parts, Text);
debug_assert!(parts.peek().is_none());
debug_assert!(parts.next().is_none()); debug_assert!(key == "sticker");
Ok(sticker.to_string())
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)
} }
} }

View File

@@ -1,5 +1,5 @@
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}; };
@@ -24,13 +24,8 @@ impl Command for StickerNames {
let list = parts let list = parts
.0 .0
.iter() .into_iter()
.map(|(_, v)| match v { .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
GenericResponseValue::Text(value) => Ok(value.to_string()),
GenericResponseValue::Binary(_) => Err(
ResponseParserError::UnexpectedPropertyType("name", "Binary"),
),
})
.collect::<Result<Vec<_>, ResponseParserError>>()?; .collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list) Ok(list)

View File

@@ -1,5 +1,5 @@
use crate::commands::{ use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}; };
@@ -24,13 +24,8 @@ impl Command for StickerTypes {
let list = parts let list = parts
.0 .0
.iter() .into_iter()
.map(|(_, v)| match v { .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
GenericResponseValue::Text(value) => Ok(value.to_string()),
GenericResponseValue::Binary(_) => Err(
ResponseParserError::UnexpectedPropertyType("stickertype", "Binary"),
),
})
.collect::<Result<Vec<_>, ResponseParserError>>()?; .collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list) Ok(list)