<commit_before>extern crate openzwave_stateful as openzwave;
extern crate foxbox_taxonomy as taxonomy;
extern crate transformable_channels;
#[macro_use]
extern crate log;

use taxonomy::util::Id as TaxId;
use taxonomy::services::{ Setter, Getter, AdapterId, ServiceId, Service, Channel, ChannelKind };
use taxonomy::values::*;
use taxonomy::api::{ ResultMap, Error as TaxError, InternalError, User };
use taxonomy::adapter::{ AdapterManagerHandle, AdapterWatchGuard, WatchEvent };
use transformable_channels::mpsc::ExtSender;

use openzwave::{ InitOptions, ZWaveManager, ZWaveNotification };
use openzwave::{ CommandClass, ValueGenre, ValueType, ValueID };
use openzwave::{ Controller };

use std::error;
use std::fmt;
use std::thread;
use std::sync::mpsc;
use std::sync::{ Arc, Mutex, RwLock, Weak };
use std::collections::{ HashMap, HashSet };

pub use self::OpenzwaveAdapter as Adapter;

#[derive(Debug)]
pub enum Error {
    TaxonomyError(TaxError),
    OpenzwaveError(openzwave::Error),
    UnknownError
}

impl From<TaxError> for Error {
    fn from(err: TaxError) -> Self {
        Error::TaxonomyError(err)
    }
}

impl From<()> for Error {
    fn from(_: ()) -> Self {
        Error::UnknownError
    }
}

impl From<openzwave::Error> for Error {
    fn from(error: openzwave::Error) -> Self {
        Error::OpenzwaveError(error)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TaxonomyError(ref err)  => write!(f, "{}: {}", error::Error::description(self), err),
            Error::OpenzwaveError(ref err) => write!(f, "{}: {}", error::Error::description(self), err),
            Error::UnknownError => write!(f, "{}", error::Error::description(self)),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TaxonomyError(_) => "Taxonomy Error",
            Error::OpenzwaveError(_) => "Openzwave Error",
            Error::UnknownError => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::TaxonomyError(ref err) => Some(err),
            Error::OpenzwaveError(ref err) => Some(err),
            Error::UnknownError => None,
        }
    }
}

#[derive(Debug, Clone)]
struct IdMap<Kind, Type> {
    map: Arc<RwLock<Vec<(TaxId<Kind>, Type)>>>
}

impl<Kind, Type> IdMap<Kind, Type> where Type: Eq + Clone, Kind: Clone {
    fn new() -> Self {
        IdMap {
            map: Arc::new(RwLock::new(Vec::new()))
        }
    }

    fn push(&mut self, id: TaxId<Kind>, ozw_object: Type) -> Result<(), ()> {
        let mut guard = try!(self.map.write().or(Err(())));
        guard.push((id, ozw_object));
        Ok(())
    }

    fn find_tax_id_from_ozw(&self, needle: &Type) -> Result<Option<TaxId<Kind>>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(_, ref controller)| controller == needle);
        Ok(find_result.map(|&(ref id, _)| id.clone()))
    }

    fn find_ozw_from_tax_id(&self, needle: &TaxId<Kind>) -> Result<Option<Type>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(ref id, _)| id == needle);
        Ok(find_result.map(|&(_, ref ozw_object)| ozw_object.clone()))
    }
}

type SyncExtSender = Mutex<Box<ExtSender<WatchEvent>>>;
type WatchersMap = HashMap<usize, Arc<SyncExtSender>>;
struct Watchers {
    current_index: usize,
    map: Arc<Mutex<WatchersMap>>,
    getter_map: HashMap<TaxId<Getter>, Vec<Weak<SyncExtSender>>>,
}

impl Watchers {
    fn new() -> Self {
        Watchers {
            current_index: 0,
            map: Arc::new(Mutex::new(HashMap::new())),
            getter_map: HashMap::new(),
        }
    }

    fn push(&mut self, tax_id: TaxId<Getter>, watcher: Arc<SyncExtSender>) -> WatcherGuard {
        let index = self.current_index;
        self.current_index += 1;
        {
            let mut map = self.map.lock().unwrap();
            map.insert(index, watcher.clone());
        }

        let entry = self.getter_map.entry(tax_id).or_insert(Vec::new());
        entry.push(Arc::downgrade(&watcher));

        WatcherGuard {
            key: index,
            map: self.map.clone()
        }
    }

    fn get(&self, index: usize) -> Option<Arc<SyncExtSender>> {
        let map = self.map.lock().unwrap();
        map.get(&index).cloned()
    }

    fn get_from_tax_id(&self, tax_id: &TaxId<Getter>) -> Option<Vec<Arc<SyncExtSender>>> {
        self.getter_map.get(tax_id).and_then(|vec| {
            let vec: Vec<_> = vec.iter().filter_map(|weak_sender| weak_sender.upgrade()).collect();
            if vec.len() == 0 { None } else { Some(vec) }
        })
    }
}

fn kind_from_value(value: ValueID) -> Option<ChannelKind> {
    value.get_command_class().map(|cc| match cc {
        CommandClass::SensorBinary => ChannelKind::OpenClosed,
        _ => ChannelKind::Ready // TODO
    })
}

fn to_open_closed(value: &ValueID) -> Option<Value> {
    debug_assert_eq!(value.get_type(), ValueType::ValueType_Bool);

    value.as_bool().ok().map(|val| {
        Value::OpenClosed(
            if val { OpenClosed::Open } else { OpenClosed::Closed }
        )
    })
}

struct WatcherGuard {
    key: usize,
    map: Arc<Mutex<WatchersMap>>,
}

impl Drop for WatcherGuard {
    fn drop(&mut self) {
        let mut map = self.map.lock().unwrap();
        map.remove(&self.key);
    }
}

impl AdapterWatchGuard for WatcherGuard {}

pub struct OpenzwaveAdapter {
    id: TaxId<AdapterId>,
    name: String,
    vendor: String,
    version: [u32; 4],
    ozw: ZWaveManager,
    controller_map: IdMap<ServiceId, Controller>,
    getter_map: IdMap<Getter, ValueID>,
    setter_map: IdMap<Setter, ValueID>,
    watchers: Arc<Mutex<Watchers>>,
}

impl OpenzwaveAdapter {
    pub fn init<T: AdapterManagerHandle + Send + Sync + 'static> (box_manager: &Arc<T>) -> Result<(), Error> {
        let options = InitOptions {
            device: None, // TODO we should expose this as a Value
            config_path: "./config/openzwave/".to_string(),
            user_path: "./config/openzwave/".to_string(),
        };

        let (ozw, rx) = try!(match openzwave::init(&options) {
            Err(openzwave::Error::NoDeviceFound) => {
                // early return: we should not impair foxbox startup for this error.
                // TODO concept of FatalError vs IgnoreableError
                error!("No ZWave device has been found.");
                return Ok(());
            }
            result => result
        });

        let name = String::from("OpenZwave Adapter");
        let adapter = Arc::new(OpenzwaveAdapter {
            id: TaxId::new(&name),
            name: name,
            vendor: String::from("Mozilla"),
            version: [1, 0, 0, 0],
            ozw: ozw,
            controller_map: IdMap::new(),
            getter_map: IdMap::new(),
            setter_map: IdMap::new(),
            watchers: Arc::new(Mutex::new(Watchers::new())),
        });

        adapter.spawn_notification_thread(rx, box_manager);
        try!(box_manager.add_adapter(adapter));

        info!("Started Openzwave adapter.");

        Ok(())
    }

    fn spawn_notification_thread<T: AdapterManagerHandle + Send + Sync + 'static>(&self, rx: mpsc::Receiver<ZWaveNotification>, box_manager: &Arc<T>) {
        let adapter_id = self.id.clone();
        let box_manager = box_manager.clone();
        let mut controller_map = self.controller_map.clone();
        let mut getter_map = self.getter_map.clone();
        let mut setter_map = self.setter_map.clone();
        let watchers = self.watchers.clone();

        thread::spawn(move || {
            for notification in rx {
                match notification {
                    ZWaveNotification::ControllerReady(controller) => {
                        let service = format!("OpenZWave/{}", controller.get_home_id());
                        let service_id = TaxId::new(&service);
                        controller_map.push(service_id.clone(), controller);

                        box_manager.add_service(Service::empty(service_id.clone(), adapter_id.clone()));
                    }
                    ZWaveNotification::NodeNew(node)               => {}
                    ZWaveNotification::NodeAdded(node)             => {}
                    ZWaveNotification::NodeRemoved(node)           => {}
                    ZWaveNotification::ValueAdded(value)           => {
                        if value.get_genre() != ValueGenre::ValueGenre_User { continue }

                        let value_id = format!("OpenZWave/{} ({})", value.get_id(), value.get_label());

                        let controller_id = controller_map.find_tax_id_from_ozw(&value.get_controller()).unwrap();
                        if controller_id.is_none() { continue }
                        let controller_id = controller_id.unwrap();

                        let has_getter = !value.is_write_only();
                        let has_setter = !value.is_read_only();

                        let kind = kind_from_value(value);
                        if kind.is_none() { continue }
                        let kind = kind.unwrap();

                        if has_getter {
                            let getter_id = TaxId::new(&value_id);
                            getter_map.push(getter_id.clone(), value);
                            box_manager.add_getter(Channel {
                                id: getter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Getter {
                                    kind: kind.clone(),
                                    updated: None
                                }
                            });
                        }

                        if has_setter {
                            let setter_id = TaxId::new(&value_id);
                            setter_map.push(setter_id.clone(), value);
                            box_manager.add_setter(Channel {
                                id: setter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Setter {
                                    kind: kind,
                                    updated: None
                                }
                            });
                        }
                    }
                    ZWaveNotification::ValueChanged(value)         => {
                        match value.get_type() {
                            ValueType::ValueType_Bool => {},
                            _ => continue // ignore non-bool vals for now
                        };

                        let tax_id = match getter_map.find_tax_id_from_ozw(&value) {
                            Ok(Some(tax_id)) => tax_id,
                            _ => continue
                        };

                        let watchers = watchers.lock().unwrap();

                        let watchers = match watchers.get_from_tax_id(&tax_id) {
                            Some(watchers) => watchers,
                            _ => continue
                        };

                        for sender in &watchers {
                            let sender = sender.lock().unwrap();
                            if let Some(value) = to_open_closed(&value) {
                                sender.send(
                                    WatchEvent::Enter { id: tax_id.clone(), value: value }
                                );
                            }
                        }
                    }
                    ZWaveNotification::ValueRemoved(value)         => {}
                    ZWaveNotification::Generic(string)             => {}
                    other => { warn!("Notification not handled {:?}", other)}
                }
            }
        });
    }
}

impl taxonomy::adapter::Adapter for OpenzwaveAdapter {
    fn id(&self) -> TaxId<AdapterId> {
        self.id.clone()
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn vendor(&self) -> &str {
        &self.vendor
    }

    fn version(&self) -> &[u32; 4] {
        &self.version
    }

    fn fetch_values(&self, mut set: Vec<TaxId<Getter>>, _: User) -> ResultMap<TaxId<Getter>, Option<Value>, TaxError> {
        set.drain(..).map(|id| {
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); //FIXME no unwrap

            let ozw_value: Option<Option<Value>> = ozw_value.map(|ozw_value: ValueID| {
                if !ozw_value.is_set() { return None }

                let result: Option<Value> = match ozw_value.get_type() {
                    ValueType::ValueType_Bool => to_open_closed(&ozw_value),
                    _ => Some(Value::Unit)
                };
                result
            });
            let value_result: Result<Option<Value>, TaxError> = ozw_value.ok_or(TaxError::InternalError(InternalError::NoSuchGetter(id.clone())));
            (id, value_result)
        }).collect()
    }

    fn send_values(&self, values: HashMap<TaxId<Setter>, Value>, _: User) -> ResultMap<TaxId<Setter>, (), TaxError> {
        unimplemented!()
    }

    fn register_watch(&self, mut values: Vec<(TaxId<Getter>, Option<Range>)>, sender: Box<ExtSender<WatchEvent>>) -> ResultMap<TaxId<Getter>, Box<AdapterWatchGuard>, TaxError> {
        let sender = Arc::new(Mutex::new(sender)); // Mutex is necessary because cb is not Sync.
        values.drain(..).map(|(id, _)| {
            let watch_guard = {
                let mut watchers = self.watchers.lock().unwrap();
                watchers.push(id.clone(), sender.clone())
            };
            let value_result: Result<Box<AdapterWatchGuard>, TaxError> = Ok(Box::new(watch_guard));

            // if there is a set value already, let's send it.
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); // FIXME no unwrap
            if let Some(value) = ozw_value {
                if value.is_set() && value.get_type() == ValueType::ValueType_Bool {
                    if let Some(value) = to_open_closed(&value) {
                        let sender = sender.lock().unwrap();
                        sender.send(
                            WatchEvent::Enter { id: id.clone(), value: value }
                        );
                    }
                }
            }

            (id, value_result)
        }).collect()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

<commit_msg>[fxbox/openzwave-adapter] Merge pull request #7 from julienw/use-references<commit_after>extern crate openzwave_stateful as openzwave;
extern crate foxbox_taxonomy as taxonomy;
extern crate transformable_channels;
#[macro_use]
extern crate log;

use taxonomy::util::Id as TaxId;
use taxonomy::services::{ Setter, Getter, AdapterId, ServiceId, Service, Channel, ChannelKind };
use taxonomy::values::*;
use taxonomy::api::{ ResultMap, Error as TaxError, InternalError, User };
use taxonomy::adapter::{ AdapterManagerHandle, AdapterWatchGuard, WatchEvent };
use transformable_channels::mpsc::ExtSender;

use openzwave::{ InitOptions, ZWaveManager, ZWaveNotification };
use openzwave::{ CommandClass, ValueGenre, ValueType, ValueID };
use openzwave::{ Controller };

use std::error;
use std::fmt;
use std::thread;
use std::sync::mpsc;
use std::sync::{ Arc, Mutex, RwLock, Weak };
use std::collections::{ HashMap, HashSet };

pub use self::OpenzwaveAdapter as Adapter;

#[derive(Debug)]
pub enum Error {
    TaxonomyError(TaxError),
    OpenzwaveError(openzwave::Error),
    UnknownError
}

impl From<TaxError> for Error {
    fn from(err: TaxError) -> Self {
        Error::TaxonomyError(err)
    }
}

impl From<()> for Error {
    fn from(_: ()) -> Self {
        Error::UnknownError
    }
}

impl From<openzwave::Error> for Error {
    fn from(error: openzwave::Error) -> Self {
        Error::OpenzwaveError(error)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TaxonomyError(ref err)  => write!(f, "{}: {}", error::Error::description(self), err),
            Error::OpenzwaveError(ref err) => write!(f, "{}: {}", error::Error::description(self), err),
            Error::UnknownError => write!(f, "{}", error::Error::description(self)),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TaxonomyError(_) => "Taxonomy Error",
            Error::OpenzwaveError(_) => "Openzwave Error",
            Error::UnknownError => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::TaxonomyError(ref err) => Some(err),
            Error::OpenzwaveError(ref err) => Some(err),
            Error::UnknownError => None,
        }
    }
}

#[derive(Debug, Clone)]
struct IdMap<Kind, Type> {
    map: Arc<RwLock<Vec<(TaxId<Kind>, Type)>>>
}

impl<Kind, Type> IdMap<Kind, Type> where Type: Eq + Clone, Kind: Clone {
    fn new() -> Self {
        IdMap {
            map: Arc::new(RwLock::new(Vec::new()))
        }
    }

    fn push(&mut self, id: TaxId<Kind>, ozw_object: Type) -> Result<(), ()> {
        let mut guard = try!(self.map.write().or(Err(())));
        guard.push((id, ozw_object));
        Ok(())
    }

    fn find_tax_id_from_ozw(&self, needle: &Type) -> Result<Option<TaxId<Kind>>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(_, ref controller)| controller == needle);
        Ok(find_result.map(|&(ref id, _)| id.clone()))
    }

    fn find_ozw_from_tax_id(&self, needle: &TaxId<Kind>) -> Result<Option<Type>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(ref id, _)| id == needle);
        Ok(find_result.map(|&(_, ref ozw_object)| ozw_object.clone()))
    }
}

type SyncExtSender = Mutex<Box<ExtSender<WatchEvent>>>;
type WatchersMap = HashMap<usize, Arc<SyncExtSender>>;
struct Watchers {
    current_index: usize,
    map: Arc<Mutex<WatchersMap>>,
    getter_map: HashMap<TaxId<Getter>, Vec<Weak<SyncExtSender>>>,
}

impl Watchers {
    fn new() -> Self {
        Watchers {
            current_index: 0,
            map: Arc::new(Mutex::new(HashMap::new())),
            getter_map: HashMap::new(),
        }
    }

    fn push(&mut self, tax_id: TaxId<Getter>, watcher: Arc<SyncExtSender>) -> WatcherGuard {
        let index = self.current_index;
        self.current_index += 1;
        {
            let mut map = self.map.lock().unwrap();
            map.insert(index, watcher.clone());
        }

        let entry = self.getter_map.entry(tax_id).or_insert(Vec::new());
        entry.push(Arc::downgrade(&watcher));

        WatcherGuard {
            key: index,
            map: self.map.clone()
        }
    }

    fn get(&self, index: usize) -> Option<Arc<SyncExtSender>> {
        let map = self.map.lock().unwrap();
        map.get(&index).cloned()
    }

    fn get_from_tax_id(&self, tax_id: &TaxId<Getter>) -> Option<Vec<Arc<SyncExtSender>>> {
        self.getter_map.get(tax_id).and_then(|vec| {
            let vec: Vec<_> = vec.iter().filter_map(|weak_sender| weak_sender.upgrade()).collect();
            if vec.len() == 0 { None } else { Some(vec) }
        })
    }
}

fn kind_from_value(value: ValueID) -> Option<ChannelKind> {
    value.get_command_class().map(|cc| match cc {
        CommandClass::SensorBinary => ChannelKind::OpenClosed,
        _ => ChannelKind::Ready // TODO
    })
}

fn to_open_closed(value: &ValueID) -> Option<Value> {
    debug_assert_eq!(value.get_type(), ValueType::ValueType_Bool);

    value.as_bool().ok().map(|val| {
        Value::OpenClosed(
            if val { OpenClosed::Open } else { OpenClosed::Closed }
        )
    })
}

struct WatcherGuard {
    key: usize,
    map: Arc<Mutex<WatchersMap>>,
}

impl Drop for WatcherGuard {
    fn drop(&mut self) {
        let mut map = self.map.lock().unwrap();
        map.remove(&self.key);
    }
}

impl AdapterWatchGuard for WatcherGuard {}

pub struct OpenzwaveAdapter {
    id: TaxId<AdapterId>,
    name: String,
    vendor: String,
    version: [u32; 4],
    ozw: ZWaveManager,
    controller_map: IdMap<ServiceId, Controller>,
    getter_map: IdMap<Getter, ValueID>,
    setter_map: IdMap<Setter, ValueID>,
    watchers: Arc<Mutex<Watchers>>,
}

impl OpenzwaveAdapter {
    pub fn init<T: AdapterManagerHandle + Send + Sync + 'static> (box_manager: &Arc<T>) -> Result<(), Error> {
        let options = InitOptions {
            device: None, // TODO we should expose this as a Value
            config_path: "./config/openzwave/",
            user_path: "./config/openzwave/",
        };

        let (ozw, rx) = try!(match openzwave::init(&options) {
            Err(openzwave::Error::NoDeviceFound) => {
                // early return: we should not impair foxbox startup for this error.
                // TODO concept of FatalError vs IgnoreableError
                error!("No ZWave device has been found.");
                return Ok(());
            }
            result => result
        });

        let name = String::from("OpenZwave Adapter");
        let adapter = Arc::new(OpenzwaveAdapter {
            id: TaxId::new(&name),
            name: name,
            vendor: String::from("Mozilla"),
            version: [1, 0, 0, 0],
            ozw: ozw,
            controller_map: IdMap::new(),
            getter_map: IdMap::new(),
            setter_map: IdMap::new(),
            watchers: Arc::new(Mutex::new(Watchers::new())),
        });

        adapter.spawn_notification_thread(rx, box_manager);
        try!(box_manager.add_adapter(adapter));

        info!("Started Openzwave adapter.");

        Ok(())
    }

    fn spawn_notification_thread<T: AdapterManagerHandle + Send + Sync + 'static>(&self, rx: mpsc::Receiver<ZWaveNotification>, box_manager: &Arc<T>) {
        let adapter_id = self.id.clone();
        let box_manager = box_manager.clone();
        let mut controller_map = self.controller_map.clone();
        let mut getter_map = self.getter_map.clone();
        let mut setter_map = self.setter_map.clone();
        let watchers = self.watchers.clone();

        thread::spawn(move || {
            for notification in rx {
                match notification {
                    ZWaveNotification::ControllerReady(controller) => {
                        let service = format!("OpenZWave/{}", controller.get_home_id());
                        let service_id = TaxId::new(&service);
                        controller_map.push(service_id.clone(), controller);

                        box_manager.add_service(Service::empty(service_id.clone(), adapter_id.clone()));
                    }
                    ZWaveNotification::NodeNew(node)               => {}
                    ZWaveNotification::NodeAdded(node)             => {}
                    ZWaveNotification::NodeRemoved(node)           => {}
                    ZWaveNotification::ValueAdded(value)           => {
                        if value.get_genre() != ValueGenre::ValueGenre_User { continue }

                        let value_id = format!("OpenZWave/{} ({})", value.get_id(), value.get_label());

                        let controller_id = controller_map.find_tax_id_from_ozw(&value.get_controller()).unwrap();
                        if controller_id.is_none() { continue }
                        let controller_id = controller_id.unwrap();

                        let has_getter = !value.is_write_only();
                        let has_setter = !value.is_read_only();

                        let kind = kind_from_value(value);
                        if kind.is_none() { continue }
                        let kind = kind.unwrap();

                        if has_getter {
                            let getter_id = TaxId::new(&value_id);
                            getter_map.push(getter_id.clone(), value);
                            box_manager.add_getter(Channel {
                                id: getter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Getter {
                                    kind: kind.clone(),
                                    updated: None
                                }
                            });
                        }

                        if has_setter {
                            let setter_id = TaxId::new(&value_id);
                            setter_map.push(setter_id.clone(), value);
                            box_manager.add_setter(Channel {
                                id: setter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Setter {
                                    kind: kind,
                                    updated: None
                                }
                            });
                        }
                    }
                    ZWaveNotification::ValueChanged(value)         => {
                        match value.get_type() {
                            ValueType::ValueType_Bool => {},
                            _ => continue // ignore non-bool vals for now
                        };

                        let tax_id = match getter_map.find_tax_id_from_ozw(&value) {
                            Ok(Some(tax_id)) => tax_id,
                            _ => continue
                        };

                        let watchers = watchers.lock().unwrap();

                        let watchers = match watchers.get_from_tax_id(&tax_id) {
                            Some(watchers) => watchers,
                            _ => continue
                        };

                        for sender in &watchers {
                            let sender = sender.lock().unwrap();
                            if let Some(value) = to_open_closed(&value) {
                                sender.send(
                                    WatchEvent::Enter { id: tax_id.clone(), value: value }
                                );
                            }
                        }
                    }
                    ZWaveNotification::ValueRemoved(value)         => {}
                    ZWaveNotification::Generic(string)             => {}
                    other => { warn!("Notification not handled {:?}", other)}
                }
            }
        });
    }
}

impl taxonomy::adapter::Adapter for OpenzwaveAdapter {
    fn id(&self) -> TaxId<AdapterId> {
        self.id.clone()
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn vendor(&self) -> &str {
        &self.vendor
    }

    fn version(&self) -> &[u32; 4] {
        &self.version
    }

    fn fetch_values(&self, mut set: Vec<TaxId<Getter>>, _: User) -> ResultMap<TaxId<Getter>, Option<Value>, TaxError> {
        set.drain(..).map(|id| {
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); //FIXME no unwrap

            let ozw_value: Option<Option<Value>> = ozw_value.map(|ozw_value: ValueID| {
                if !ozw_value.is_set() { return None }

                let result: Option<Value> = match ozw_value.get_type() {
                    ValueType::ValueType_Bool => to_open_closed(&ozw_value),
                    _ => Some(Value::Unit)
                };
                result
            });
            let value_result: Result<Option<Value>, TaxError> = ozw_value.ok_or(TaxError::InternalError(InternalError::NoSuchGetter(id.clone())));
            (id, value_result)
        }).collect()
    }

    fn send_values(&self, values: HashMap<TaxId<Setter>, Value>, _: User) -> ResultMap<TaxId<Setter>, (), TaxError> {
        unimplemented!()
    }

    fn register_watch(&self, mut values: Vec<(TaxId<Getter>, Option<Range>)>, sender: Box<ExtSender<WatchEvent>>) -> ResultMap<TaxId<Getter>, Box<AdapterWatchGuard>, TaxError> {
        let sender = Arc::new(Mutex::new(sender)); // Mutex is necessary because cb is not Sync.
        values.drain(..).map(|(id, _)| {
            let watch_guard = {
                let mut watchers = self.watchers.lock().unwrap();
                watchers.push(id.clone(), sender.clone())
            };
            let value_result: Result<Box<AdapterWatchGuard>, TaxError> = Ok(Box::new(watch_guard));

            // if there is a set value already, let's send it.
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); // FIXME no unwrap
            if let Some(value) = ozw_value {
                if value.is_set() && value.get_type() == ValueType::ValueType_Bool {
                    if let Some(value) = to_open_closed(&value) {
                        let sender = sender.lock().unwrap();
                        sender.send(
                            WatchEvent::Enter { id: id.clone(), value: value }
                        );
                    }
                }
            }

            (id, value_result)
        }).collect()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap;
use std::fs;
use std::io::Write;

use common::ui::{UI, Status};
use export_docker;
use export_docker::Result;
use export_k8s::{Manifest, ManifestJson};

use chartfile::ChartFile;
use values::Values;

pub struct Chart<'a> {
    name: String,
    chartfile: ChartFile,
    manifest_template: ManifestJson,
    values: Values,
    ui: &'a mut UI,
}

impl<'a> Chart<'a> {
    pub fn new_for_cli_matches(ui: &'a mut UI, matches: &clap::ArgMatches) -> Result<Self> {
        let image = if !matches.is_present("NO_DOCKER_IMAGE") {
            export_docker::export_for_cli_matches(ui, &matches)?
        } else {
            None
        };
        let manifest = Manifest::new_from_cli_matches(ui, &matches, image)?;

        let name = matches
            .value_of("CHART")
            .unwrap_or(&manifest.metadata_name)
            .to_string();
        let version = matches.value_of("VERSION");
        let description = matches.value_of("DESCRIPTION");
        let chartfile = ChartFile::new(&name, version, description);

        Ok(Self::new_for_manifest(manifest, name, chartfile, ui))
    }

    fn new_for_manifest(
        manifest: Manifest,
        name: String,
        chartfile: ChartFile,
        ui: &'a mut UI,
    ) -> Self {
        let main = json!({
            "metadata_name": "{{.Values.metadataName}}",
            "image": "{{.Values.imageName}}",
            "count": "{{.Values.instanceCount}}",
            "service_topology": "{{.Values.serviceTopology}}",
            "service_group": manifest.service_group.clone().map(|_| "{{.Values.serviceGroup}}"),
            "config": manifest.config
                .clone()
                .map(|_| "{{.Values.config}}"),
            "ring_secret_name": manifest.ring_secret_name
                .clone()
                .map(|_| "{{.Values.ringSecretName}}"),
            "bind": !manifest.binds.is_empty(),
        });

        let mut values = Values::new();
        values.add_entry("metadataName", &manifest.metadata_name);
        values.add_entry("imageName", &manifest.image);
        values.add_entry("instanceCount", &manifest.count.to_string());
        values.add_entry("serviceTopology", &manifest.service_topology.to_string());
        if let Some(ref group) = manifest.service_group {
            values.add_entry("serviceGroup", group);
        }
        if let Some(ref config) = manifest.config {
            values.add_entry("config", config);
        }
        if let Some(ref name) = manifest.ring_secret_name {
            values.add_entry("ringSecretName", name);
        }

        let mut binds = Vec::new();
        let mut i = 0;
        for bind in &manifest.binds {
            let name_var = format!("bindName{}", i);
            let service_var = format!("bindService{}", i);
            let group_var = format!("bindGroup{}", i);
            i = i + 1;

            values.add_entry(&name_var, &bind.name);
            values.add_entry(&service_var, &bind.service_group.service());
            values.add_entry(&group_var, &bind.service_group.group());

            let json = json!({
                "name": format!("{{{{.Values.{}}}}}", name_var),
                "service": format!("{{{{.Values.{}}}}}", service_var),
                "group": format!("{{{{.Values.{}}}}}", group_var),
            });

            binds.push(json);
        }

        let manifest_template = ManifestJson {
            main: main,
            binds: binds,
        };

        Chart {
            name,
            chartfile,
            manifest_template,
            values,
            ui,
        }
    }

    pub fn generate(mut self) -> Result<()> {
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", self.name),
        )?;
        fs::create_dir_all(&self.name)?;

        self.generate_chartfile()?;

        self.generate_values()?;

        self.generate_manifest_template()
    }

    fn generate_chartfile(&mut self) -> Result<()> {
        let path = format!("{}/Chart.yaml", self.name);
        self.ui.status(Status::Creating, format!("file `{}`", path))?;
        let mut write = fs::File::create(path)?;
        let out = self.chartfile.into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }

    fn generate_manifest_template(self) -> Result<()> {
        let template_path = format!("{}/{}", self.name, "templates");
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", template_path),
        )?;
        fs::create_dir_all(&template_path)?;

        let manifest_path = format!("{}/{}.yaml", template_path, self.name);
        self.ui.status(
            Status::Creating,
            format!("file `{}`", manifest_path),
        )?;
        let mut write = fs::File::create(manifest_path)?;
        let out: String = self.manifest_template.into();

        write.write(out.as_bytes())?;

        Ok(())
    }

    fn generate_values(&mut self) -> Result<()> {
        let path = format!("{}/values.yaml", self.name);
        self.ui.status(Status::Creating, format!("file `{}`", path))?;
        let mut write = fs::File::create(path)?;

        self.values.generate(&mut write)?;

        Ok(())
    }
}
<commit_msg>pkg-export-helm: Refactor file creation into separate method<commit_after>// Copyright (c) 2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap;
use std::fs;
use std::io::Write;

use common::ui::{UI, Status};
use export_docker;
use export_docker::Result;
use export_k8s::{Manifest, ManifestJson};

use chartfile::ChartFile;
use values::Values;

pub struct Chart<'a> {
    name: String,
    chartfile: ChartFile,
    manifest_template: ManifestJson,
    values: Values,
    ui: &'a mut UI,
}

impl<'a> Chart<'a> {
    pub fn new_for_cli_matches(ui: &'a mut UI, matches: &clap::ArgMatches) -> Result<Self> {
        let image = if !matches.is_present("NO_DOCKER_IMAGE") {
            export_docker::export_for_cli_matches(ui, &matches)?
        } else {
            None
        };
        let manifest = Manifest::new_from_cli_matches(ui, &matches, image)?;

        let name = matches
            .value_of("CHART")
            .unwrap_or(&manifest.metadata_name)
            .to_string();
        let version = matches.value_of("VERSION");
        let description = matches.value_of("DESCRIPTION");
        let chartfile = ChartFile::new(&name, version, description);

        Ok(Self::new_for_manifest(manifest, name, chartfile, ui))
    }

    fn new_for_manifest(
        manifest: Manifest,
        name: String,
        chartfile: ChartFile,
        ui: &'a mut UI,
    ) -> Self {
        let main = json!({
            "metadata_name": "{{.Values.metadataName}}",
            "image": "{{.Values.imageName}}",
            "count": "{{.Values.instanceCount}}",
            "service_topology": "{{.Values.serviceTopology}}",
            "service_group": manifest.service_group.clone().map(|_| "{{.Values.serviceGroup}}"),
            "config": manifest.config
                .clone()
                .map(|_| "{{.Values.config}}"),
            "ring_secret_name": manifest.ring_secret_name
                .clone()
                .map(|_| "{{.Values.ringSecretName}}"),
            "bind": !manifest.binds.is_empty(),
        });

        let mut values = Values::new();
        values.add_entry("metadataName", &manifest.metadata_name);
        values.add_entry("imageName", &manifest.image);
        values.add_entry("instanceCount", &manifest.count.to_string());
        values.add_entry("serviceTopology", &manifest.service_topology.to_string());
        if let Some(ref group) = manifest.service_group {
            values.add_entry("serviceGroup", group);
        }
        if let Some(ref config) = manifest.config {
            values.add_entry("config", config);
        }
        if let Some(ref name) = manifest.ring_secret_name {
            values.add_entry("ringSecretName", name);
        }

        let mut binds = Vec::new();
        let mut i = 0;
        for bind in &manifest.binds {
            let name_var = format!("bindName{}", i);
            let service_var = format!("bindService{}", i);
            let group_var = format!("bindGroup{}", i);
            i = i + 1;

            values.add_entry(&name_var, &bind.name);
            values.add_entry(&service_var, &bind.service_group.service());
            values.add_entry(&group_var, &bind.service_group.group());

            let json = json!({
                "name": format!("{{{{.Values.{}}}}}", name_var),
                "service": format!("{{{{.Values.{}}}}}", service_var),
                "group": format!("{{{{.Values.{}}}}}", group_var),
            });

            binds.push(json);
        }

        let manifest_template = ManifestJson {
            main: main,
            binds: binds,
        };

        Chart {
            name,
            chartfile,
            manifest_template,
            values,
            ui,
        }
    }

    pub fn generate(mut self) -> Result<()> {
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", self.name),
        )?;
        fs::create_dir_all(&self.name)?;

        self.generate_chartfile()?;

        self.generate_values()?;

        self.generate_manifest_template()
    }

    fn generate_chartfile(&mut self) -> Result<()> {
        let mut write = self.create_file("Chart.yaml")?;
        let out = self.chartfile.into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }

    fn generate_manifest_template(self) -> Result<()> {
        let template_path = format!("{}/{}", self.name, "templates");
        self.ui.status(
            Status::Creating,
            format!("directory `{}`", template_path),
        )?;
        fs::create_dir_all(&template_path)?;

        let manifest_path = format!("{}/{}.yaml", template_path, self.name);
        self.ui.status(
            Status::Creating,
            format!("file `{}`", manifest_path),
        )?;
        let mut write = fs::File::create(manifest_path)?;
        let out: String = self.manifest_template.into();

        write.write(out.as_bytes())?;

        Ok(())
    }

    fn generate_values(&mut self) -> Result<()> {
        let mut write = self.create_file("values.yaml")?;
        self.values.generate(&mut write)?;

        Ok(())
    }

    fn create_file(&mut self, name: &str) -> Result<fs::File> {
        let path = format!("{}/{}", self.name, name);
        self.ui.status(Status::Creating, format!("file `{}`", path))?;

        fs::File::create(path).map_err(From::from)
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, MutNullableJS};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::xmlname::namespace_from_domstring;
use dom::element::Element;
use dom::node::{Node, document_from_node};
use dom::window::Window;
use dom_struct::dom_struct;
use html5ever::{LocalName, QualName};
use servo_atoms::Atom;
use std::cell::Cell;
use style::str::split_html_space_chars;

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

// An optional u32, using maxint to represent None.
// It would be nicer just to use Option<u32> for this, but that would produce word
// alignment issues since Option<u32> uses 33 bits.
#[derive(Clone, Copy, JSTraceable, HeapSizeOf)]
struct OptionU32 {
    bits: u32,
}

impl OptionU32 {
    fn to_option(self) -> Option<u32> {
        if self.bits == u32::max_value() {
            None
        } else {
            Some(self.bits)
        }
    }

    fn some(bits: u32) -> OptionU32 {
        assert!(bits != u32::max_value());
        OptionU32 { bits: bits }
    }

    fn none() -> OptionU32 {
        OptionU32 { bits: u32::max_value() }
    }
}

#[dom_struct]
pub struct HTMLCollection {
    reflector_: Reflector,
    root: JS<Node>,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    filter: Box<CollectionFilter + 'static>,
    // We cache the version of the root node and all its decendents,
    // the length of the collection, and a cursor into the collection.
    // FIXME: make the cached cursor element a weak pointer
    cached_version: Cell<u64>,
    cached_cursor_element: MutNullableJS<Element>,
    cached_cursor_index: Cell<OptionU32>,
    cached_length: Cell<OptionU32>,
}

impl HTMLCollection {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(root: &Node, filter: Box<CollectionFilter + 'static>) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            root: JS::from_ref(root),
            filter: filter,
            // Default values for the cache
            cached_version: Cell::new(root.inclusive_descendants_version()),
            cached_cursor_element: MutNullableJS::new(None),
            cached_cursor_index: Cell::new(OptionU32::none()),
            cached_length: Cell::new(OptionU32::none()),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, root: &Node, filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(root, filter),
                           window, HTMLCollectionBinding::Wrap)
    }

    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, root, filter)
    }

    fn validate_cache(&self) {
        // Clear the cache if the root version is different from our cached version
        let cached_version = self.cached_version.get();
        let curr_version = self.root.inclusive_descendants_version();
        if curr_version != cached_version {
            // Default values for the cache
            self.cached_version.set(curr_version);
            self.cached_cursor_element.set(None);
            self.cached_length.set(OptionU32::none());
            self.cached_cursor_index.set(OptionU32::none());
        }
    }

    fn set_cached_cursor(&self, index: u32, element: Option<Root<Element>>) -> Option<Root<Element>> {
        if let Some(element) = element {
            self.cached_cursor_index.set(OptionU32::some(index));
            self.cached_cursor_element.set(Some(&element));
            Some(element)
        } else {
            None
        }
    }

    // https://dom.spec.whatwg.org/#concept-getelementsbytagname
    pub fn by_qualified_name(window: &Window, root: &Node, qualified_name: LocalName)
                             -> Root<HTMLCollection> {
        // case 1
        if qualified_name == local_name!("*") {
            #[derive(JSTraceable, HeapSizeOf)]
            struct AllFilter;
            impl CollectionFilter for AllFilter {
                fn filter(&self, _elem: &Element, _root: &Node) -> bool {
                    true
                }
            }
            return HTMLCollection::create(window, root, box AllFilter);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct HtmlDocumentFilter {
            qualified_name: LocalName,
            ascii_lower_qualified_name: LocalName,
        }
        impl CollectionFilter for HtmlDocumentFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                if root.is_in_html_doc() && elem.namespace() == &ns!(html) {    // case 2
                    HTMLCollection::match_element(elem, &self.ascii_lower_qualified_name)
                } else {    // case 2 and 3
                    HTMLCollection::match_element(elem, &self.qualified_name)
                }
            }
        }

        let filter = HtmlDocumentFilter {
            ascii_lower_qualified_name: qualified_name.to_ascii_lowercase(),
            qualified_name: qualified_name,
        };
        HTMLCollection::create(window, root, box filter)
    }

    fn match_element(elem: &Element, qualified_name: &LocalName) -> bool {
        match elem.prefix() {
            None => elem.local_name() == qualified_name,
            Some(prefix) => qualified_name.starts_with(&**prefix) &&
                qualified_name.find(":") == Some(prefix.len()) &&
                qualified_name.ends_with(&**elem.local_name()),
        }
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let local = LocalName::from(tag);
        let ns = namespace_from_domstring(maybe_ns);
        let qname = QualName::new(None, ns, local);
        HTMLCollection::by_qual_tag_name(window, root, qname)
    }

    pub fn by_qual_tag_name(window: &Window, root: &Node, qname: QualName) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            qname: QualName
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                    ((self.qname.ns == namespace_url!("*")) || (self.qname.ns == *elem.namespace())) &&
                    ((self.qname.local == local_name!("*")) || (self.qname.local == *elem.local_name()))
            }
        }
        let filter = TagNameNSFilter {
            qname: qname
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect();
        HTMLCollection::by_atomic_class_name(window, root, class_atoms)
    }

    pub fn by_atomic_class_name(window: &Window, root: &Node, classes: Vec<Atom>)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let case_sensitivity = document_from_node(elem)
                    .quirks_mode()
                    .classes_and_ids_case_sensitivity();
                self.classes.iter().all(|class| elem.has_class(class, case_sensitivity))
            }
        }
        let filter = ClassNameFilter {
            classes: classes
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(elem.upcast())
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    pub fn elements_iter_after<'a>(&'a self, after: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate forwards from a node.
        HTMLCollectionElementsIter {
            node_iter: after.following_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

    pub fn elements_iter<'a>(&'a self) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate forwards from the root.
        self.elements_iter_after(&*self.root)
    }

    pub fn elements_iter_before<'a>(&'a self, before: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate backwards from a node.
        HTMLCollectionElementsIter {
            node_iter: before.preceding_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

    pub fn root_node(&self) -> Root<Node> {
        Root::from_ref(&self.root)
    }
}

// TODO: Make this generic, and avoid code duplication
struct HTMLCollectionElementsIter<'a, I> {
    node_iter: I,
    root: Root<Node>,
    filter: &'a Box<CollectionFilter>,
}

impl<'a, I: Iterator<Item=Root<Node>>> Iterator for HTMLCollectionElementsIter<'a, I> {
    type Item = Root<Element>;

    fn next(&mut self) -> Option<Self::Item> {
        let filter = &self.filter;
        let root = &self.root;
        self.node_iter.by_ref()
                      .filter_map(Root::downcast)
                      .filter(|element| filter.filter(&element, root))
                      .next()
    }
}

impl HTMLCollectionMethods for HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(&self) -> u32 {
        self.validate_cache();

        if let Some(cached_length) = self.cached_length.get().to_option() {
            // Cache hit
            cached_length
        } else {
            // Cache miss, calculate the length
            let length = self.elements_iter().count() as u32;
            self.cached_length.set(OptionU32::some(length));
            length
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(&self, index: u32) -> Option<Root<Element>> {
        self.validate_cache();

        if let Some(element) = self.cached_cursor_element.get() {
            // Cache hit, the cursor element is set
            if let Some(cached_index) = self.cached_cursor_index.get().to_option() {
                if cached_index == index {
                    // The cursor is the element we're looking for
                    Some(element)
                } else if cached_index < index {
                    // The cursor is before the element we're looking for
                    // Iterate forwards, starting at the cursor.
                    let offset = index - (cached_index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    let mut iter = self.elements_iter_after(&node);
                    self.set_cached_cursor(index, iter.nth(offset as usize))
                } else {
                    // The cursor is after the element we're looking for
                    // Iterate backwards, starting at the cursor.
                    let offset = cached_index - (index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    let mut iter = self.elements_iter_before(&node);
                    self.set_cached_cursor(index, iter.nth(offset as usize))
                }
            } else {
                // Cache miss
                // Iterate forwards through all the nodes
                self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
            }
        } else {
            // Cache miss
            // Iterate forwards through all the nodes
            self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(&self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        self.elements_iter().find(|elem| {
            elem.get_string_attribute(&local_name!("id")) == key ||
            (elem.namespace() == &ns!(html) && elem.get_string_attribute(&local_name!("name")) == key)
        })
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> {
        self.Item(index)
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString) -> Option<Root<Element>> {
        self.NamedItem(name)
    }

    // https://dom.spec.whatwg.org/#interface-htmlcollection
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        // Step 1
        let mut result = vec![];

        // Step 2
        for elem in self.elements_iter() {
            // Step 2.1
            let id_attr = elem.get_string_attribute(&local_name!("id"));
            if !id_attr.is_empty() && !result.contains(&id_attr) {
                result.push(id_attr)
            }
            // Step 2.2
            let name_attr = elem.get_string_attribute(&local_name!("name"));
            if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(html) {
                result.push(name_attr)
            }
        }

        // Step 3
        result
    }
}
<commit_msg>Replace iterator struct with anonymous return iterator types.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, MutNullableJS};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::xmlname::namespace_from_domstring;
use dom::element::Element;
use dom::node::{Node, document_from_node};
use dom::window::Window;
use dom_struct::dom_struct;
use html5ever::{LocalName, QualName};
use servo_atoms::Atom;
use std::cell::Cell;
use style::str::split_html_space_chars;

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

// An optional u32, using maxint to represent None.
// It would be nicer just to use Option<u32> for this, but that would produce word
// alignment issues since Option<u32> uses 33 bits.
#[derive(Clone, Copy, JSTraceable, HeapSizeOf)]
struct OptionU32 {
    bits: u32,
}

impl OptionU32 {
    fn to_option(self) -> Option<u32> {
        if self.bits == u32::max_value() {
            None
        } else {
            Some(self.bits)
        }
    }

    fn some(bits: u32) -> OptionU32 {
        assert!(bits != u32::max_value());
        OptionU32 { bits: bits }
    }

    fn none() -> OptionU32 {
        OptionU32 { bits: u32::max_value() }
    }
}

#[dom_struct]
pub struct HTMLCollection {
    reflector_: Reflector,
    root: JS<Node>,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    filter: Box<CollectionFilter + 'static>,
    // We cache the version of the root node and all its decendents,
    // the length of the collection, and a cursor into the collection.
    // FIXME: make the cached cursor element a weak pointer
    cached_version: Cell<u64>,
    cached_cursor_element: MutNullableJS<Element>,
    cached_cursor_index: Cell<OptionU32>,
    cached_length: Cell<OptionU32>,
}

impl HTMLCollection {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(root: &Node, filter: Box<CollectionFilter + 'static>) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            root: JS::from_ref(root),
            filter: filter,
            // Default values for the cache
            cached_version: Cell::new(root.inclusive_descendants_version()),
            cached_cursor_element: MutNullableJS::new(None),
            cached_cursor_index: Cell::new(OptionU32::none()),
            cached_length: Cell::new(OptionU32::none()),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, root: &Node, filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(root, filter),
                           window, HTMLCollectionBinding::Wrap)
    }

    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, root, filter)
    }

    fn validate_cache(&self) {
        // Clear the cache if the root version is different from our cached version
        let cached_version = self.cached_version.get();
        let curr_version = self.root.inclusive_descendants_version();
        if curr_version != cached_version {
            // Default values for the cache
            self.cached_version.set(curr_version);
            self.cached_cursor_element.set(None);
            self.cached_length.set(OptionU32::none());
            self.cached_cursor_index.set(OptionU32::none());
        }
    }

    fn set_cached_cursor(&self, index: u32, element: Option<Root<Element>>) -> Option<Root<Element>> {
        if let Some(element) = element {
            self.cached_cursor_index.set(OptionU32::some(index));
            self.cached_cursor_element.set(Some(&element));
            Some(element)
        } else {
            None
        }
    }

    // https://dom.spec.whatwg.org/#concept-getelementsbytagname
    pub fn by_qualified_name(window: &Window, root: &Node, qualified_name: LocalName)
                             -> Root<HTMLCollection> {
        // case 1
        if qualified_name == local_name!("*") {
            #[derive(JSTraceable, HeapSizeOf)]
            struct AllFilter;
            impl CollectionFilter for AllFilter {
                fn filter(&self, _elem: &Element, _root: &Node) -> bool {
                    true
                }
            }
            return HTMLCollection::create(window, root, box AllFilter);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct HtmlDocumentFilter {
            qualified_name: LocalName,
            ascii_lower_qualified_name: LocalName,
        }
        impl CollectionFilter for HtmlDocumentFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                if root.is_in_html_doc() && elem.namespace() == &ns!(html) {    // case 2
                    HTMLCollection::match_element(elem, &self.ascii_lower_qualified_name)
                } else {    // case 2 and 3
                    HTMLCollection::match_element(elem, &self.qualified_name)
                }
            }
        }

        let filter = HtmlDocumentFilter {
            ascii_lower_qualified_name: qualified_name.to_ascii_lowercase(),
            qualified_name: qualified_name,
        };
        HTMLCollection::create(window, root, box filter)
    }

    fn match_element(elem: &Element, qualified_name: &LocalName) -> bool {
        match elem.prefix() {
            None => elem.local_name() == qualified_name,
            Some(prefix) => qualified_name.starts_with(&**prefix) &&
                qualified_name.find(":") == Some(prefix.len()) &&
                qualified_name.ends_with(&**elem.local_name()),
        }
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let local = LocalName::from(tag);
        let ns = namespace_from_domstring(maybe_ns);
        let qname = QualName::new(None, ns, local);
        HTMLCollection::by_qual_tag_name(window, root, qname)
    }

    pub fn by_qual_tag_name(window: &Window, root: &Node, qname: QualName) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            qname: QualName
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                    ((self.qname.ns == namespace_url!("*")) || (self.qname.ns == *elem.namespace())) &&
                    ((self.qname.local == local_name!("*")) || (self.qname.local == *elem.local_name()))
            }
        }
        let filter = TagNameNSFilter {
            qname: qname
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect();
        HTMLCollection::by_atomic_class_name(window, root, class_atoms)
    }

    pub fn by_atomic_class_name(window: &Window, root: &Node, classes: Vec<Atom>)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let case_sensitivity = document_from_node(elem)
                    .quirks_mode()
                    .classes_and_ids_case_sensitivity();
                self.classes.iter().all(|class| elem.has_class(class, case_sensitivity))
            }
        }
        let filter = ClassNameFilter {
            classes: classes
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(elem.upcast())
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    pub fn elements_iter_after<'a>(&'a self, after: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate forwards from a node.
        after.following_nodes(&self.root)
            .filter_map(Root::downcast)
            .filter(move |element| self.filter.filter(&element, &self.root))
    }

    pub fn elements_iter<'a>(&'a self) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate forwards from the root.
        self.elements_iter_after(&*self.root)
    }

    pub fn elements_iter_before<'a>(&'a self, before: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a {
        // Iterate backwards from a node.
        before.preceding_nodes(&self.root)
            .filter_map(Root::downcast)
            .filter(move |element| self.filter.filter(&element, &self.root))
    }

    pub fn root_node(&self) -> Root<Node> {
        Root::from_ref(&self.root)
    }
}

impl HTMLCollectionMethods for HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(&self) -> u32 {
        self.validate_cache();

        if let Some(cached_length) = self.cached_length.get().to_option() {
            // Cache hit
            cached_length
        } else {
            // Cache miss, calculate the length
            let length = self.elements_iter().count() as u32;
            self.cached_length.set(OptionU32::some(length));
            length
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(&self, index: u32) -> Option<Root<Element>> {
        self.validate_cache();

        if let Some(element) = self.cached_cursor_element.get() {
            // Cache hit, the cursor element is set
            if let Some(cached_index) = self.cached_cursor_index.get().to_option() {
                if cached_index == index {
                    // The cursor is the element we're looking for
                    Some(element)
                } else if cached_index < index {
                    // The cursor is before the element we're looking for
                    // Iterate forwards, starting at the cursor.
                    let offset = index - (cached_index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    let mut iter = self.elements_iter_after(&node);
                    self.set_cached_cursor(index, iter.nth(offset as usize))
                } else {
                    // The cursor is after the element we're looking for
                    // Iterate backwards, starting at the cursor.
                    let offset = cached_index - (index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    let mut iter = self.elements_iter_before(&node);
                    self.set_cached_cursor(index, iter.nth(offset as usize))
                }
            } else {
                // Cache miss
                // Iterate forwards through all the nodes
                self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
            }
        } else {
            // Cache miss
            // Iterate forwards through all the nodes
            self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(&self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        self.elements_iter().find(|elem| {
            elem.get_string_attribute(&local_name!("id")) == key ||
            (elem.namespace() == &ns!(html) && elem.get_string_attribute(&local_name!("name")) == key)
        })
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> {
        self.Item(index)
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString) -> Option<Root<Element>> {
        self.NamedItem(name)
    }

    // https://dom.spec.whatwg.org/#interface-htmlcollection
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        // Step 1
        let mut result = vec![];

        // Step 2
        for elem in self.elements_iter() {
            // Step 2.1
            let id_attr = elem.get_string_attribute(&local_name!("id"));
            if !id_attr.is_empty() && !result.contains(&id_attr) {
                result.push(id_attr)
            }
            // Step 2.2
            let name_attr = elem.get_string_attribute(&local_name!("name"));
            if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(html) {
                result.push(name_attr)
            }
        }

        // Step 3
        result
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::str;

#[derive(Debug)]
pub struct Response {
    pub size: u16,
    pub source: u32,
    pub mac_address: String,
    pub firmware: String,
    pub sequence_number: u16,
    pub reserved_1: u32,
    pub message_type: u16,
    pub reserved_2: u16,
    pub payload: Payload,
}

#[derive(Debug)]
pub struct ResponseString {
    pub size: String,
    pub source: String,
    pub mac_address: String,
    pub firmware: String,
    pub sequence_number: String,
    pub reserved_1: String,
    pub message_type: String,
    pub reserved_2: String,
    pub payload: PayloadString,
}

pub fn parse_response(resp_msg: ResponseData) -> Response {
    let mut resp = parse_header(&resp_msg);

    let payload = match resp.message_type {
        3 => parse_payload_3(&resp_msg),
        107 => parse_payload_107(&resp_msg),
        _ => Payload::None(()),
    };

    resp.payload = payload;

    resp
}
pub fn parse_response_string(resp_msg: ResponseMessage) -> ResponseString {
    let mut resp = parse_header_string(&resp_msg);

    let payload = match resp.message_type.as_str() {
        "3" => parse_payload_3_string(&resp_msg),
        "107" => parse_payload_107_string(&resp_msg),
        _ => PayloadString::None(()),
    };

    resp.payload = payload;

    resp
}

fn parse_header(resp: &ResponseData) -> Response {
    Response {
        size: ResponseData::size(resp),
        source: ResponseData::source(resp),
        mac_address: ResponseData::mac_address(resp),
        firmware: ResponseData::firmware(resp),

        // TODO: packed byte
        sequence_number: ResponseData::sequence_number(&resp),

        // Message segment: protocol header
        reserved_1: ResponseData::reserved_1(&resp), // timestamp?
        message_type: ResponseData::message_type(&resp),
        reserved_2: ResponseData::reserved_2(&resp),
        payload: Payload::None(()),
    }
}
fn parse_header_string(resp: &ResponseMessage) -> ResponseString {
    ResponseString {
        size: ResponseMessage::size(&resp),
        source: ResponseMessage::source(&resp),
        mac_address: ResponseMessage::mac_address(&resp),
        firmware: ResponseMessage::firmware(&resp),

        // TODO: packed byte
        sequence_number: ResponseMessage::sequence_number(&resp),

        // Message segment: protocol header
        reserved_1: ResponseMessage::reserved_1(&resp), // timestamp?
        message_type: ResponseMessage::message_type(&resp),
        reserved_2: ResponseMessage::reserved_2(&resp),
        payload: PayloadString::None(()),
    }
}

#[derive(Debug)]
pub enum Payload {
    None(()),
    StateService(StateServicePayload),
    State(StatePayload),
}

#[derive(Debug)]
pub enum PayloadString {
    None(()),
    StateService(StateServicePayloadString),
    State_String(StatePayload_String),
}

#[derive(Debug)]
pub struct StateServicePayload {
    pub service: u16, // String,
    pub port: u32, // String,
    pub unknown: String,
}
#[derive(Debug)]
pub struct StateServicePayloadString {
    pub service: String,
    pub port: String,
    pub unknown: String,
}

#[derive(Debug)]
pub struct StatePayload {
    pub body: String,
    pub hsbk: PayloadHSBK,
}

#[derive(Debug)]
pub struct StatePayload_String {
    pub body: String,
    pub hsbk: PayloadHSBK_String,
}

#[derive(Debug)]
pub struct PayloadHSBK {
    pub hue: u16,
    pub saturation: u16,
    pub brightness: u16,
    pub kelvin: u16,
}

#[derive(Debug)]
pub struct PayloadHSBK_String {
    pub hue: String,
    pub saturation: String,
    pub brightness: String,
    pub kelvin: String,
}

fn parse_payload_3(resp: &ResponseData) -> Payload {
    Payload::StateService(StateServicePayload {
        service: ResponseData::service(&resp),
        port: ResponseData::port(&resp),
        unknown: ResponseData::unknown(&resp),
    })
}
fn parse_payload_3_string(resp: &ResponseMessage) -> PayloadString {
    PayloadString::StateService(StateServicePayloadString {
        service: ResponseMessage::service(&resp),
        port: ResponseMessage::port(&resp),
        unknown: ResponseMessage::unknown(&resp),
    })
}

fn parse_payload_107(resp: &ResponseData) -> Payload {
    Payload::State(StatePayload {
        body: ResponseData::body(&resp),
        hsbk: PayloadHSBK {
            hue: ResponseData::hue(&resp),
            saturation: ResponseData::saturation(&resp),
            brightness: ResponseData::brightness(&resp),
            kelvin: ResponseData::kelvin(&resp),
        },
    })
}

fn parse_payload_107_string(resp: &ResponseMessage) -> PayloadString {
    PayloadString::State_String(StatePayload_String {
        body: ResponseMessage::body(&resp),
        hsbk: PayloadHSBK_String {
            hue: ResponseMessage::hue(&resp),
            saturation: ResponseMessage::saturation(&resp),
            brightness: ResponseMessage::brightness(&resp),
            kelvin: ResponseMessage::kelvin(&resp),
        },
    })
}

pub struct ResponseData(pub Vec<u8>);
pub struct ResponseMessage(pub Vec<u8>);

impl ResponseData {
    fn size(resp: &ResponseData) -> u16 {
        let mut b = extract(&resp, 0, 2);
        b.reverse();
        as_base10(b)
    }

    fn source(resp: &ResponseData) -> u32 {
        let mut b = extract(&resp, 4, 4);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn mac_address(resp: &ResponseData) -> String {
        as_hex(extract(&resp, 8, 8))
    }

    fn firmware(resp: &ResponseData) -> String {
        as_ascii(extract(&resp, 16, 6))
    }

    fn sequence_number(resp: &ResponseData) -> u16 {
        as_base10(extract(&resp, 23, 1))
    }

    fn reserved_1(resp: &ResponseData) -> u32 {
        let mut b = extract(&resp, 24, 8);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn message_type(resp: &ResponseData) -> u16 {
        let mut b = extract(&resp, 32, 2);
        b.reverse();
        as_base10(b)
    }

    fn reserved_2(resp: &ResponseData) -> u16 {
        let b = extract(&resp, 34, 2);
        as_base10(b)  // TODO: may not be base10, but undocumented.
    }

    fn service(resp: &ResponseData) -> u16 {
        as_base10(extract(&resp, 36, 1))
    }

    fn port(resp: &ResponseData) -> u32 {
        let mut b = extract(resp, 37, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn unknown(resp: &ResponseData) -> String {
        let end = resp.0.len() - 39;
        let b = extract(resp, 39, end);
        // as_base10(b)  // TODO: may not be base10, but undocumented.
        as_hex(b)
    }

    fn body(resp: &ResponseData) -> String {
        let end = resp.0.len() - 36;
        as_hex(extract(resp, 36, end))
    }

    fn hue(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 36, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn saturation(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 38, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn brightness(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 40, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn kelvin(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 42, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }
}

impl ResponseMessage {
    fn size(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 0, 2);
        b.reverse();
        as_base10_string(b)
    }

    fn source(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 4, 4);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn mac_address(resp: &ResponseMessage) -> String {
        as_hex(extract_string(resp, 8, 8))
    }

    fn firmware(resp: &ResponseMessage) -> String {
        as_ascii(extract_string(resp, 16, 6))
    }

    fn sequence_number(resp: &ResponseMessage) -> String {
        as_base10_string(extract_string(resp, 23, 1))
    }

    fn reserved_1(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 24, 8);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn message_type(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 32, 2);
        b.reverse();
        as_base10_string(b)
    }

    fn reserved_2(resp: &ResponseMessage) -> String {
        let b = extract_string(resp, 34, 2);
        as_base10_string(b)  // TODO: may not be base10, but undocumented.
    }

    fn service(resp: &ResponseMessage) -> String {
        as_base10_string(extract_string(resp, 36, 1))
    }

    fn port(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 37, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn unknown(resp: &ResponseMessage) -> String {
        let end = resp.0.len() - 39;
        let b = extract_string(resp, 39, end);
        // as_base10(b)  // TODO: may not be base10, but undocumented.
        as_hex(b)
    }

    fn body(resp: &ResponseMessage) -> String {
        let end = resp.0.len() - 36;
        as_hex(extract_string(&resp, 36, end))
    }

    fn hue(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 36, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn saturation(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 38, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn brightness(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 40, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }

    fn kelvin(resp: &ResponseMessage) -> String {
        let mut b = extract_string(resp, 42, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr).to_string()
    }
}

fn extract(resp: &ResponseData, start: usize, len: usize) -> Vec<u8> {
    let mut sub = vec![0u8; len];
    sub[..len].clone_from_slice(&resp.0[start..start + len]);
    sub
}

fn extract_string(resp: &ResponseMessage, start: usize, len: usize) -> Vec<u8> {
    let mut sub = vec![0u8; len];
    sub[..len].clone_from_slice(&resp.0[start..start + len]);
    sub
}

fn as_base10_string(v: Vec<u8>) -> String {
    let mut s = "".to_string();
    for b in v {
        s.push_str(format!("{}", b).as_str());
    }
    let n = s.parse::<u16>().unwrap();
    n.to_string()
}

fn as_base10(v: Vec<u8>) -> u16 {
    let mut s = "".to_string();
    for b in v {
        s.push_str(format!("{}", b).as_str());
    }
    s.parse::<u16>().unwrap()
}

fn as_ascii(arr: Vec<u8>) -> String {
    str::from_utf8(&arr).unwrap().to_string()
}

fn as_boolean(v: Vec<u8>) -> String {
    let mut s = "".to_string();
    for b in v {
        s.push_str(format!("{:08b}", b).as_str());
    }
    s
}

fn as_hex(arr: Vec<u8>) -> String {
    let mut s: Vec<String> = vec![];
    for b in arr {
        s.push(format!("{:02X}", b));
    }
    s.join(":")
}

fn bitstr_to_u8(bits: &str) -> u8 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

fn bitstr_to_u16(bits: &str) -> u16 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

fn bitstr_to_u32(bits: &str) -> u32 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

#[cfg(test)]
mod tests {
    use super::{ResponseData, ResponseMessage, extract, as_base10, as_ascii, as_boolean, as_hex,
                bitstr_to_u32};

    #[test]
    fn test_extract() {
        let resp = ResponseData(vec![41, 42, 43, 44, 45, 46, 47, 48, 49]);
        assert_eq!(extract(&resp, 2, 3), vec![43, 44, 45]);
    }

    #[test]
    fn test_as_base10() {
        assert_eq!(as_base10(vec![00, 41]), 41);
    }

    #[test]
    fn test_as_boolean() {
        assert_eq!(as_boolean(vec![221, 124]), "1101110101111100");
    }

    #[test]
    fn test_bitstr_to_u32() {
        assert_eq!(bitstr_to_u32("1101110101111100"), 56700);
    }

    #[test]
    fn test_as_ascii() {
        assert_eq!(as_ascii(vec![76, 73, 70, 88, 86, 50]), "LIFXV2");
    }

    #[test]
    fn test_from_hex() {
        assert_eq!(as_hex(vec![209, 114, 214, 20, 224, 14, 0, 0]),
                   "D1:72:D6:14:E0:0E:00:00");
    }
}
<commit_msg>Remove deprecated stringified response. (#17)<commit_after>#![allow(dead_code)]

use std::str;

#[derive(Debug)]
pub struct Response {
    pub size: u16,
    pub source: u32,
    pub mac_address: String,
    pub firmware: String,
    pub sequence_number: u16,
    pub reserved_1: u32,
    pub message_type: u16,
    pub reserved_2: u16,
    pub payload: Payload,
}

pub fn parse_response(resp_msg: ResponseData) -> Response {
    let mut resp = parse_header(&resp_msg);

    let payload = match resp.message_type {
        3 => parse_payload_3(&resp_msg),
        107 => parse_payload_107(&resp_msg),
        _ => Payload::None(()),
    };

    resp.payload = payload;

    resp
}

fn parse_header(resp: &ResponseData) -> Response {
    Response {
        size: ResponseData::size(resp),
        source: ResponseData::source(resp),
        mac_address: ResponseData::mac_address(resp),
        firmware: ResponseData::firmware(resp),

        // TODO: packed byte
        sequence_number: ResponseData::sequence_number(&resp),

        // Message segment: protocol header
        reserved_1: ResponseData::reserved_1(&resp), // timestamp?
        message_type: ResponseData::message_type(&resp),
        reserved_2: ResponseData::reserved_2(&resp),
        payload: Payload::None(()),
    }
}

#[derive(Debug)]
pub enum Payload {
    None(()),
    StateService(StateServicePayload),
    State(StatePayload),
}

#[derive(Debug)]
pub struct StateServicePayload {
    pub service: u16,
    pub port: u32,
    pub unknown: String,
}

#[derive(Debug)]
pub struct StatePayload {
    pub body: String,
    pub hsbk: PayloadHSBK,
}

#[derive(Debug)]
pub struct PayloadHSBK {
    pub hue: u16,
    pub saturation: u16,
    pub brightness: u16,
    pub kelvin: u16,
}

fn parse_payload_3(resp: &ResponseData) -> Payload {
    Payload::StateService(StateServicePayload {
        service: ResponseData::service(&resp),
        port: ResponseData::port(&resp),
        unknown: ResponseData::unknown(&resp),
    })
}

fn parse_payload_107(resp: &ResponseData) -> Payload {
    Payload::State(StatePayload {
        body: ResponseData::body(&resp),
        hsbk: PayloadHSBK {
            hue: ResponseData::hue(&resp),
            saturation: ResponseData::saturation(&resp),
            brightness: ResponseData::brightness(&resp),
            kelvin: ResponseData::kelvin(&resp),
        },
    })
}

pub struct ResponseData(pub Vec<u8>);
// pub struct ResponseMessage(pub Vec<u8>);

impl ResponseData {
    fn size(resp: &ResponseData) -> u16 {
        let mut b = extract(&resp, 0, 2);
        b.reverse();
        as_base10(b)
    }

    fn source(resp: &ResponseData) -> u32 {
        let mut b = extract(&resp, 4, 4);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn mac_address(resp: &ResponseData) -> String {
        as_hex(extract(&resp, 8, 8))
    }

    fn firmware(resp: &ResponseData) -> String {
        as_ascii(extract(&resp, 16, 6))
    }

    fn sequence_number(resp: &ResponseData) -> u16 {
        as_base10(extract(&resp, 23, 1))
    }

    fn reserved_1(resp: &ResponseData) -> u32 {
        let mut b = extract(&resp, 24, 8);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn message_type(resp: &ResponseData) -> u16 {
        let mut b = extract(&resp, 32, 2);
        b.reverse();
        as_base10(b)
    }

    fn reserved_2(resp: &ResponseData) -> u16 {
        let b = extract(&resp, 34, 2);
        as_base10(b)  // TODO: may not be base10, but undocumented.
    }

    fn service(resp: &ResponseData) -> u16 {
        as_base10(extract(&resp, 36, 1))
    }

    fn port(resp: &ResponseData) -> u32 {
        let mut b = extract(resp, 37, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u32(&bstr)
    }

    fn unknown(resp: &ResponseData) -> String {
        let end = resp.0.len() - 39;
        let b = extract(resp, 39, end);
        // as_base10(b)  // TODO: may not be base10, but undocumented.
        as_hex(b)
    }

    fn body(resp: &ResponseData) -> String {
        let end = resp.0.len() - 36;
        as_hex(extract(resp, 36, end))
    }

    fn hue(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 36, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn saturation(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 38, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn brightness(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 40, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }

    fn kelvin(resp: &ResponseData) -> u16 {
        let mut b = extract(resp, 42, 2);
        b.reverse();
        let bstr = as_boolean(b);
        bitstr_to_u16(&bstr)
    }
}

// impl ResponseMessage {
// fn size(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 0, 2);
// b.reverse();
// as_base10_string(b)
// }
//
// fn source(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 4, 4);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn mac_address(resp: &ResponseMessage) -> String {
// as_hex(extract_string(resp, 8, 8))
// }
//
// fn firmware(resp: &ResponseMessage) -> String {
// as_ascii(extract_string(resp, 16, 6))
// }
//
// fn sequence_number(resp: &ResponseMessage) -> String {
// as_base10_string(extract_string(resp, 23, 1))
// }
//
// fn reserved_1(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 24, 8);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn message_type(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 32, 2);
// b.reverse();
// as_base10_string(b)
// }
//
// fn reserved_2(resp: &ResponseMessage) -> String {
// let b = extract_string(resp, 34, 2);
// as_base10_string(b)  // TODO: may not be base10, but undocumented.
// }
//
// fn service(resp: &ResponseMessage) -> String {
// as_base10_string(extract_string(resp, 36, 1))
// }
//
// fn port(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 37, 2);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn unknown(resp: &ResponseMessage) -> String {
// let end = resp.0.len() - 39;
// let b = extract_string(resp, 39, end);
// as_base10(b)  // TODO: may not be base10, but undocumented.
// as_hex(b)
// }
//
// fn body(resp: &ResponseMessage) -> String {
// let end = resp.0.len() - 36;
// as_hex(extract_string(&resp, 36, end))
// }
//
// fn hue(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 36, 2);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn saturation(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 38, 2);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn brightness(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 40, 2);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
//
// fn kelvin(resp: &ResponseMessage) -> String {
// let mut b = extract_string(resp, 42, 2);
// b.reverse();
// let bstr = as_boolean(b);
// bitstr_to_u32(&bstr).to_string()
// }
// }
//

fn extract(resp: &ResponseData, start: usize, len: usize) -> Vec<u8> {
    let mut sub = vec![0u8; len];
    sub[..len].clone_from_slice(&resp.0[start..start + len]);
    sub
}

fn as_base10(v: Vec<u8>) -> u16 {
    let mut s = "".to_string();
    for b in v {
        s.push_str(format!("{}", b).as_str());
    }
    s.parse::<u16>().unwrap()
}

fn as_ascii(arr: Vec<u8>) -> String {
    str::from_utf8(&arr).unwrap().to_string()
}

fn as_boolean(v: Vec<u8>) -> String {
    let mut s = "".to_string();
    for b in v {
        s.push_str(format!("{:08b}", b).as_str());
    }
    s
}

fn as_hex(arr: Vec<u8>) -> String {
    let mut s: Vec<String> = vec![];
    for b in arr {
        s.push(format!("{:02X}", b));
    }
    s.join(":")
}

fn bitstr_to_u8(bits: &str) -> u8 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

fn bitstr_to_u16(bits: &str) -> u16 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

fn bitstr_to_u32(bits: &str) -> u32 {
    bits.as_bytes().iter().fold(0, |acc, b| (acc << 1) + if *b == 48 { 0 } else { 1 })
}

#[cfg(test)]
mod tests {
    use super::{ResponseData, extract, as_base10, as_ascii, as_boolean, as_hex, bitstr_to_u32};

    #[test]
    fn test_extract() {
        let resp = ResponseData(vec![41, 42, 43, 44, 45, 46, 47, 48, 49]);
        assert_eq!(extract(&resp, 2, 3), vec![43, 44, 45]);
    }

    #[test]
    fn test_as_base10() {
        assert_eq!(as_base10(vec![00, 41]), 41);
    }

    #[test]
    fn test_as_boolean() {
        assert_eq!(as_boolean(vec![221, 124]), "1101110101111100");
    }

    #[test]
    fn test_bitstr_to_u32() {
        assert_eq!(bitstr_to_u32("1101110101111100"), 56700);
    }

    #[test]
    fn test_as_ascii() {
        assert_eq!(as_ascii(vec![76, 73, 70, 88, 86, 50]), "LIFXV2");
    }

    #[test]
    fn test_from_hex() {
        assert_eq!(as_hex(vec![209, 114, 214, 20, 224, 14, 0, 0]),
                   "D1:72:D6:14:E0:0E:00:00");
    }
}
<|endoftext|>