common/types: add type for ChannelName

This commit is contained in:
2025-11-25 04:13:32 +09:00
parent b03f60c985
commit e5f70ca87a
7 changed files with 69 additions and 9 deletions

View File

@@ -37,7 +37,10 @@ impl Command for Channels {
for (key, value) in parts {
debug_assert!(key == "channels");
let channel_name = expect_property_type!(Some(value), "channels", Text);
channel_names.push(channel_name.to_string());
let channel_name = channel_name
.parse()
.map_err(|_| ResponseParserError::SyntaxError(0, channel_name))?;
channel_names.push(channel_name);
}
Ok(ChannelsResponse {
@@ -64,7 +67,11 @@ mod tests {
assert_eq!(
response,
ChannelsResponse {
channels: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]
channels: vec![
"foo".parse().unwrap(),
"bar".parse().unwrap(),
"baz".parse().unwrap(),
]
}
);
}

View File

@@ -47,7 +47,11 @@ impl Command for ReadMessages {
debug_assert!(ckey == "channel");
debug_assert!(mkey == "message");
let channel = expect_property_type!(Some(cvalue), "channel", Text).to_string();
let channel = expect_property_type!(Some(cvalue), "channel", Text);
let channel = channel
.parse()
.map_err(|_| ResponseParserError::SyntaxError(0, channel))?;
let message = expect_property_type!(Some(mvalue), "message", Text).to_string();
messages.push(ReadMessagesResponseEntry { channel, message });
@@ -77,11 +81,11 @@ mod tests {
result,
Ok(vec![
ReadMessagesResponseEntry {
channel: "channel1".to_string(),
channel: "channel1".parse().unwrap(),
message: "message1".to_string(),
},
ReadMessagesResponseEntry {
channel: "channel2".to_string(),
channel: "channel2".parse().unwrap(),
message: "message2".to_string(),
},
])

View File

@@ -26,13 +26,16 @@ impl Command for SendMessage {
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel = channel
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel.to_owned()))?;
// TODO: SplitWhitespace::remainder() is unstable, use when stable
let message = parts.collect::<Vec<_>>().join(" ");
debug_assert!(!message.is_empty());
Ok((Request::SendMessage(channel.to_string(), message), ""))
Ok((Request::SendMessage(channel, message), ""))
}
fn parse_response(

View File

@@ -18,10 +18,13 @@ impl Command for Subscribe {
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel_name = channel_name
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel_name.to_owned()))?;
debug_assert!(parts.next().is_none());
Ok((Request::Subscribe(channel_name.to_string()), ""))
Ok((Request::Subscribe(channel_name), ""))
}
fn parse_response(

View File

@@ -18,10 +18,13 @@ impl Command for Unsubscribe {
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel_name = channel_name
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel_name.to_owned()))?;
debug_assert!(parts.next().is_none());
Ok((Request::Unsubscribe(channel_name.to_string()), ""))
Ok((Request::Unsubscribe(channel_name), ""))
}
fn parse_response(

View File

@@ -1,6 +1,7 @@
mod absolute_relative_song_position;
mod audio;
mod bool_or_oneshot;
mod channel_name;
mod group_type;
mod one_or_range;
mod replay_gain_mode_mode;
@@ -16,6 +17,7 @@ mod window_range;
pub use absolute_relative_song_position::AbsouluteRelativeSongPosition;
pub use audio::Audio;
pub use bool_or_oneshot::BoolOrOneshot;
pub use channel_name::ChannelName;
pub use group_type::GroupType;
pub use one_or_range::OneOrRange;
pub use replay_gain_mode_mode::ReplayGainModeMode;
@@ -39,7 +41,6 @@ pub type TimeWithFractions = f64;
// TODO: use a proper types
pub type AudioOutputId = String;
pub type ChannelName = String;
pub type Feature = String;
pub type PartitionName = String;
pub type Path = String;

View File

@@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ChannelName(String);
impl ChannelName {
fn new(name: String) -> Self {
ChannelName(name)
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl std::fmt::Display for ChannelName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for ChannelName {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s
.chars()
.all(|c| c.is_ascii_alphanumeric() || "-_.:".contains(c))
{
Err("Invalid channel name")
} else {
Ok(ChannelName::new(s.to_string()))
}
}
}