From 68a397bb18b2b8fb600a1521f94437ccf2dfcad6 Mon Sep 17 00:00:00 2001 From: Alex Hamilton Date: Sat, 22 Dec 2018 21:25:36 -0600 Subject: [PATCH] Add combined selectors. Selectors can be combined to create arbitrary-length chains using Selector::combine followed by Selectors::combine. Selectors can be used in place of Selector for relevant methods, so this was basically drop-in. Yay generics! This closes #1. --- src/http/client/states.rs | 4 ++- src/http/selector.rs | 74 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/http/client/states.rs b/src/http/client/states.rs index 26cd414..e032fdb 100644 --- a/src/http/client/states.rs +++ b/src/http/client/states.rs @@ -83,8 +83,10 @@ impl<'a, T: Select> AsRequest<()> for Toggle<'a, T> { /// use lifxi::http::*; /// # fn run() { /// let client = Client::new("foo"); +/// let office = Selector::Label("Office".to_string()); +/// let basement = Selector::Label("Basement".to_string()); /// let result = client -/// .select(Selector::All) +/// .select(office.combine(basement)) /// .set_state() /// .power(true) /// .color(Color::Red) diff --git a/src/http/selector.rs b/src/http/selector.rs index 62478c6..1fa567e 100644 --- a/src/http/selector.rs +++ b/src/http/selector.rs @@ -4,6 +4,54 @@ use std::str::FromStr; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +/// A struct representing an aggregate of selectors. +/// +/// Up to 25 selectors may be chained together this way. No validation is performed client-side, +/// however, so please take care to enforce this limit in consumer code. +#[derive(Clone, Default)] +pub struct Selectors { + selectors: Vec, +} + +impl Selectors { + /// Adds another selector to the chain. + /// + /// ## Example + /// ``` + /// use lifxi::http::*; + /// let foo = Selector::Label("foo".to_string()); + /// let bar = Selector::Label("bar".to_string()); + /// let baz = Selector::Label("baz".to_string()); + /// let combined = foo.combine(bar).combine(baz); + /// assert_eq!(&format!("{}", combined), "label:foo,label:bar,label:baz"); + /// ``` + #[allow(clippy::needless_pass_by_value)] + pub fn combine(mut self, sel: T) -> Self { + self.selectors.push(format!("{}", sel)); + self + } +} + +impl fmt::Display for Selectors { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for sel in self.selectors.iter().rev().skip(1).rev() { + write!(f, "{},", sel)?; + } + if let Some(last) = self.selectors.last() { + write!(f, "{}", last)?; + } + Ok(()) + } +} + +impl Serialize for Selectors { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&format!("{}", self)) + } +} + +impl Select for Selectors {} + /// Selectors are used to identify one or more lights belonging to a particular account. /// /// All resolutions of selectors are treated as sets, even if they are logically a single device, @@ -347,6 +395,32 @@ where } } +/// Enables chaining of non-randomized selectors. +pub trait Combine { + /// Combines two selectors to begin a chain. + /// + /// ## Examples + /// ``` + /// use lifxi::http::*; + /// let foo = Selector::Label("foo".to_string()); + /// let bar = Selector::Label("bar".to_string()); + /// let combined = foo.combine(bar); + /// assert_eq!(&format!("{}", combined), "label:foo,label:bar"); + /// let foo = Selector::Label("foo".to_string()); + /// let bar = Selector::Label("bar".to_string()); + /// let baz = Selector::Label("baz".to_string()); + /// let combined = foo.combine(bar).combine(baz); + /// assert_eq!(&format!("{}", combined), "label:foo,label:bar,label:baz"); + /// ``` + fn combine(self, other: T) -> Selectors; +} + +impl Combine for T { + fn combine(self, other: U) -> Selectors { + Selectors::default().combine(self).combine(other) + } +} + #[cfg(test)] mod tests { use super::*;