summaryrefslogtreecommitdiff
path: root/vendored/mpd/src/message.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendored/mpd/src/message.rs')
-rw-r--r--vendored/mpd/src/message.rs73
1 files changed, 73 insertions, 0 deletions
diff --git a/vendored/mpd/src/message.rs b/vendored/mpd/src/message.rs
new file mode 100644
index 0000000..8be122a
--- /dev/null
+++ b/vendored/mpd/src/message.rs
@@ -0,0 +1,73 @@
+//! The module defines structures for MPD client-to-client messaging/subscription protocol
+//!
+//! The MPD client-to-client messaging protocol is fairly easy one, and is based on channels.
+//! Any client can subscribe to arbitrary number of channels, and some other client
+//! can send messages to a channel by name. Then, at some point of time, subscribed
+//! client can read all queued messages for all channels, it was subscribed to.
+//!
+//! Also client can get asynchronous notifications about new messages from subscribed
+//! channels with `idle` command, by waiting for `message` subsystem events.
+
+use crate::convert::FromMap;
+use crate::error::{Error, ProtoError};
+
+use std::collections::BTreeMap;
+use std::fmt;
+
+/// Message
+#[derive(Debug, PartialEq, Clone, RustcEncodable)]
+pub struct Message {
+ /// channel
+ pub channel: Channel,
+ /// message payload
+ pub message: String,
+}
+
+impl FromMap for Message {
+ fn from_map(map: BTreeMap<String, String>) -> Result<Message, Error> {
+ Ok(Message {
+ channel: Channel(map.get("channel").map(|v| v.to_owned()).ok_or(Error::Proto(ProtoError::NoField("channel")))?),
+ message: map.get("message").map(|v| v.to_owned()).ok_or(Error::Proto(ProtoError::NoField("message")))?,
+ })
+ }
+}
+
+/// Channel
+#[derive(Debug, PartialEq, PartialOrd, Clone, RustcEncodable)]
+pub struct Channel(String);
+
+impl fmt::Display for Channel {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+impl Channel {
+ /// Create channel with given name
+ pub fn new(name: &str) -> Option<Channel> {
+ if Channel::is_valid_name(name) {
+ Some(Channel(name.to_owned()))
+ } else {
+ None
+ }
+ }
+
+ /// Create channel with arbitrary name, bypassing name validity checks
+ ///
+ /// Not recommened! Use `new()` method above instead.
+ pub unsafe fn new_unchecked(name: String) -> Channel {
+ Channel(name)
+ }
+
+ /// Check if given name is a valid channel name
+ ///
+ /// Valid channel name can contain only English letters (`A`-`Z`, `a`-`z`),
+ /// numbers (`0`-`9`), underscore, forward slash, dot and colon (`_`, `/`, `.`, `:`)
+ pub fn is_valid_name(name: &str) -> bool {
+ name.bytes()
+ .all(|b| {
+ (0x61 <= b && b <= 0x7a) || (0x41 <= b && b <= 0x5a) || (0x30 <= b && b <= 0x39) ||
+ (b == 0x5f || b == 0x2f || b == 0x2e || b == 0x3a)
+ })
+ }
+}