<commit_before>use eventual::Async;
use protobuf::{self, Message, RepeatedField};
use std::borrow::Cow;
use std::sync::{Mutex, Arc};
use std::collections::HashMap;

use mercury::{MercuryRequest, MercuryMethod};
use player::{Player, PlayerState};
use session::Session;
use util;
use util::SpotifyId;
use version;

use protocol;
pub use protocol::spirc::{PlayStatus, MessageType};

#[derive(Clone)]
pub struct SpircManager(Arc<Mutex<SpircInternal>>);

struct SpircInternal {
    player: Player,
    session: Session,

    seq_nr: u32,

    name: String,
    ident: String,
    device_type: u8,
    can_play: bool,

    repeat: bool,
    shuffle: bool,

    is_active: bool,
    became_active_at: i64,

    last_command_ident: String,
    last_command_msgid: u32,

    tracks: Vec<SpotifyId>,
    index: u32,

    devices: HashMap<String, String>,
}

impl SpircManager {
    pub fn new(session: Session, player: Player) -> SpircManager {
        let ident = session.device_id().to_owned();
        let name = session.config().device_name.clone();

        SpircManager(Arc::new(Mutex::new(SpircInternal {
            player: player,
            session: session,

            seq_nr: 0,

            name: name,
            ident: ident,
            device_type: 5,
            can_play: true,

            repeat: false,
            shuffle: false,

            is_active: false,
            became_active_at: 0,

            last_command_ident: String::new(),
            last_command_msgid: 0,

            tracks: Vec::new(),
            index: 0,

            devices: HashMap::new(),
        })))
    }

    pub fn run(&self) {
        let rx = {
            let mut internal = self.0.lock().unwrap();

            let rx = internal.session.mercury_sub(internal.uri());

            internal.notify(true, None);

            // Use a weak pointer to avoid creating an Rc cycle between the player and the
            // SpircManager
            let _self = Arc::downgrade(&self.0);
            internal.player.add_observer(Box::new(move |state| {
                if let Some(_self) = _self.upgrade() {
                    let mut internal = _self.lock().unwrap();
                    internal.on_update(state);
                }
            }));

            rx
        };

        for pkt in rx {
            let data = pkt.payload.first().unwrap();
            let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(data).unwrap();

            debug!("{:?} {:?} {} {} {}",
                     frame.get_typ(),
                     frame.get_device_state().get_name(),
                     frame.get_ident(),
                     frame.get_seq_nr(),
                     frame.get_state_update_id());

            self.0.lock().unwrap().handle(frame);
        }
    }

    pub fn devices(&self) -> HashMap<String, String> {
        self.0.lock().unwrap().devices.clone()
    }

    pub fn send_play(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePlay)
            .recipient(recipient)
            .send();
    }

    pub fn send_pause(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePause)
            .recipient(recipient)
            .send();
    }

    pub fn send_prev(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePrev)
            .recipient(recipient)
            .send();
    }

    pub fn send_next(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeNext)
            .recipient(recipient)
            .send();
    }

    pub fn send_replace_tracks<I: Iterator<Item = SpotifyId>>(&mut self,
                                                              recipient: &str,
                                                              track_ids: I) {
        let state = track_ids_to_state(track_ids);
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeReplace)
            .recipient(recipient)
            .state(state)
            .send();
    }

    pub fn send_load_tracks<I: Iterator<Item = SpotifyId>>(&mut self,
                                                           recipient: &str,
                                                           track_ids: I) {
        let state = track_ids_to_state(track_ids);
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeLoad)
            .recipient(recipient)
            .state(state)
            .send();
    }

    pub fn get_queue(&self) -> Vec<SpotifyId> {
        self.0.lock().unwrap().tracks.clone()
    }
}

impl SpircInternal {
    fn on_update(&mut self, player_state: &PlayerState) {
        let end_of_track = player_state.end_of_track();
        if end_of_track {
            self.index = (self.index + 1) % self.tracks.len() as u32;
            let track = self.tracks[self.index as usize];
            self.player.load(track, true, 0);
        } else {
            self.notify_with_player_state(false, None, player_state);
        }
    }

    fn handle(&mut self, frame: protocol::spirc::Frame) {
        if frame.get_ident() == self.ident ||
           (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
            return;
        }

        if frame.get_recipient().len() > 0 {
            self.last_command_ident = frame.get_ident().to_owned();
            self.last_command_msgid = frame.get_seq_nr();
        }

        if frame.has_ident() && !frame.has_goodbye() && frame.has_device_state() {
            self.devices.insert(frame.get_ident().into(),
                                frame.get_device_state().get_name().into());
        }

        match frame.get_typ() {
            MessageType::kMessageTypeHello => {
                self.notify(false, Some(frame.get_ident()));
            }
            MessageType::kMessageTypeLoad => {
                if !self.is_active {
                    self.is_active = true;
                    self.became_active_at = util::now_ms();
                }

                self.reload_tracks(&frame);
                if self.tracks.len() > 0 {
                    let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
                    let track = self.tracks[self.index as usize];
                    let position = frame.get_state().get_position_ms();
                    self.player.load(track, play, position);
                } else {
                    self.notify(false, Some(frame.get_ident()));
                }
            }
            MessageType::kMessageTypePlay => {
                self.player.play();
            }
            MessageType::kMessageTypePause => {
                self.player.pause();
            }
            MessageType::kMessageTypeNext => {
                self.index = (self.index + 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            MessageType::kMessageTypePrev => {
                self.index = (self.index - 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            MessageType::kMessageTypeSeek => {
                self.player.seek(frame.get_position());
            }
            MessageType::kMessageTypeReplace => {
                self.reload_tracks(&frame);
            }
            MessageType::kMessageTypeNotify => {
                if self.is_active && frame.get_device_state().get_is_active() {
                    self.is_active = false;
                    self.player.stop();
                }
            }
            MessageType::kMessageTypeVolume => {
                self.player.volume(frame.get_volume() as u16);
            }
            MessageType::kMessageTypeGoodbye => {
                if frame.has_ident() {
                    self.devices.remove(frame.get_ident());
                }
            }
            _ => (),
        }
    }

    fn reload_tracks(&mut self, ref frame: &protocol::spirc::Frame) {
        self.index = frame.get_state().get_playing_track_index();
        self.tracks = frame.get_state()
                           .get_track()
                           .iter()
                           .filter(|track| track.has_gid())
                           .map(|track| SpotifyId::from_raw(track.get_gid()))
                           .collect();
    }

    fn notify(&mut self, hello: bool, recipient: Option<&str>) {
        let mut cs = CommandSender::new(self,
                                        if hello {
                                            MessageType::kMessageTypeHello
                                        } else {
                                            MessageType::kMessageTypeNotify
                                        });
        if let Some(s) = recipient {
            cs = cs.recipient(&s);
        }
        cs.send();
    }

    fn notify_with_player_state(&mut self,
                                hello: bool,
                                recipient: Option<&str>,
                                player_state: &PlayerState) {
        let mut cs = CommandSender::new(self,
                                        if hello {
                                            MessageType::kMessageTypeHello
                                        } else {
                                            MessageType::kMessageTypeNotify
                                        })
                         .player_state(player_state);
        if let Some(s) = recipient {
            cs = cs.recipient(&s);
        }
        cs.send();
    }

    fn spirc_state(&self, player_state: &PlayerState) -> protocol::spirc::State {
        let (position_ms, position_measured_at) = player_state.position();

        protobuf_init!(protocol::spirc::State::new(), {
            status: player_state.status(),
            position_ms: position_ms,
            position_measured_at: position_measured_at as u64,

            playing_track_index: self.index,
            track: self.tracks.iter().map(|track| {
                protobuf_init!(protocol::spirc::TrackRef::new(), {
                    gid: track.to_raw().to_vec()
                })
            }).collect(),

            shuffle: self.shuffle,
            repeat: self.repeat,

            playing_from_fallback: true,

            last_command_ident: self.last_command_ident.clone(),
            last_command_msgid: self.last_command_msgid
        })
    }

    fn device_state(&self, player_state: &PlayerState) -> protocol::spirc::DeviceState {
        protobuf_init!(protocol::spirc::DeviceState::new(), {
            sw_version: version::version_string(),
            is_active: self.is_active,
            can_play: self.can_play,
            volume: player_state.volume() as u32,
            name: self.name.clone(),
            error_code: 0,
            became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
            capabilities => [
                @{
                    typ: protocol::spirc::CapabilityType::kCanBePlayer,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kDeviceType,
                    intValue => [ self.device_type as i64 ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportsLogout,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kIsObservable,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kVolumeSteps,
                    intValue => [10]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedContexts,
                    stringValue => [
                        "album",
                        "playlist",
                        "search",
                        "inbox",
                        "toplist",
                        "starred",
                        "publishedstarred",
                        "track",
                    ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedTypes,
                    stringValue => [
                        "audio/local",
                        "audio/track",
                        "local",
                        "track",
                    ]
                }
            ],
        })
    }

    fn uri(&self) -> String {
        format!("hm://remote/user/{}", self.session.username())
    }
}

struct CommandSender<'a> {
    spirc_internal: &'a mut SpircInternal,
    cmd: MessageType,
    recipient: Option<&'a str>,
    player_state: Option<&'a PlayerState>,
    state: Option<protocol::spirc::State>,
}

impl<'a> CommandSender<'a> {
    fn new(spirc_internal: &'a mut SpircInternal, cmd: MessageType) -> CommandSender {
        CommandSender {
            spirc_internal: spirc_internal,
            cmd: cmd,
            recipient: None,
            player_state: None,
            state: None,
        }
    }

    fn recipient(mut self, r: &'a str) -> CommandSender {
        self.recipient = Some(r);
        self
    }

    fn player_state(mut self, s: &'a PlayerState) -> CommandSender {
        self.player_state = Some(s);
        self
    }

    fn state(mut self, s: protocol::spirc::State) -> CommandSender<'a> {
        self.state = Some(s);
        self
    }

    fn send(self) {
        let state = self.player_state.map_or_else(|| {
            Cow::Owned(self.spirc_internal.player.state())
        }, |s| {
            Cow::Borrowed(s)
        });

        let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
            version: 1,
            ident: self.spirc_internal.ident.clone(),
            protocol_version: "2.0.0",
            seq_nr: { self.spirc_internal.seq_nr += 1; self.spirc_internal.seq_nr  },
            typ: self.cmd,
            recipient: RepeatedField::from_vec(
                self.recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![])
                ),
            device_state: self.spirc_internal.device_state(&state),
            state_update_id: state.update_time()
        });

        if self.spirc_internal.is_active {
            pkt.set_state(self.spirc_internal.spirc_state(&state));
        }

        self.spirc_internal
            .session
            .mercury(MercuryRequest {
                method: MercuryMethod::SEND,
                uri: self.spirc_internal.uri(),
                content_type: None,
                payload: vec![pkt.write_to_bytes().unwrap()],
            })
            .fire();
    }
}

fn track_ids_to_state<I: Iterator<Item = SpotifyId>>(track_ids: I) -> protocol::spirc::State {
    let tracks: Vec<protocol::spirc::TrackRef> =
        track_ids.map(|i| {
                     protobuf_init!(protocol::spirc::TrackRef::new(), { gid: i.to_raw().to_vec()})
                 })
                 .collect();
    protobuf_init!(protocol::spirc::State::new(), {
                    track: RepeatedField::from_vec(tracks)
                })
}
<commit_msg>Add method that returns track list.<commit_after>use eventual::Async;
use protobuf::{self, Message, RepeatedField};
use std::borrow::Cow;
use std::sync::{Mutex, Arc};
use std::collections::HashMap;

use mercury::{MercuryRequest, MercuryMethod};
use player::{Player, PlayerState};
use session::Session;
use util;
use util::SpotifyId;
use version;

use protocol;
pub use protocol::spirc::{PlayStatus, MessageType};

#[derive(Clone)]
pub struct SpircManager(Arc<Mutex<SpircInternal>>);

struct SpircInternal {
    player: Player,
    session: Session,

    seq_nr: u32,

    name: String,
    ident: String,
    device_type: u8,
    can_play: bool,

    repeat: bool,
    shuffle: bool,

    is_active: bool,
    became_active_at: i64,

    last_command_ident: String,
    last_command_msgid: u32,

    tracks: Vec<SpotifyId>,
    index: u32,

    devices: HashMap<String, String>,
}

impl SpircManager {
    pub fn new(session: Session, player: Player) -> SpircManager {
        let ident = session.device_id().to_owned();
        let name = session.config().device_name.clone();

        SpircManager(Arc::new(Mutex::new(SpircInternal {
            player: player,
            session: session,

            seq_nr: 0,

            name: name,
            ident: ident,
            device_type: 5,
            can_play: true,

            repeat: false,
            shuffle: false,

            is_active: false,
            became_active_at: 0,

            last_command_ident: String::new(),
            last_command_msgid: 0,

            tracks: Vec::new(),
            index: 0,

            devices: HashMap::new(),
        })))
    }

    pub fn run(&self) {
        let rx = {
            let mut internal = self.0.lock().unwrap();

            let rx = internal.session.mercury_sub(internal.uri());

            internal.notify(true, None);

            // Use a weak pointer to avoid creating an Rc cycle between the player and the
            // SpircManager
            let _self = Arc::downgrade(&self.0);
            internal.player.add_observer(Box::new(move |state| {
                if let Some(_self) = _self.upgrade() {
                    let mut internal = _self.lock().unwrap();
                    internal.on_update(state);
                }
            }));

            rx
        };

        for pkt in rx {
            let data = pkt.payload.first().unwrap();
            let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(data).unwrap();

            debug!("{:?} {:?} {} {} {}",
                     frame.get_typ(),
                     frame.get_device_state().get_name(),
                     frame.get_ident(),
                     frame.get_seq_nr(),
                     frame.get_state_update_id());

            self.0.lock().unwrap().handle(frame);
        }
    }

    pub fn tracks(&self) -> Vec<SpotifyId> {
        self.0.lock().unwrap().tracks.clone()
    }

    pub fn devices(&self) -> HashMap<String, String> {
        self.0.lock().unwrap().devices.clone()
    }

    pub fn send_play(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePlay)
            .recipient(recipient)
            .send();
    }

    pub fn send_pause(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePause)
            .recipient(recipient)
            .send();
    }

    pub fn send_prev(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypePrev)
            .recipient(recipient)
            .send();
    }

    pub fn send_next(&self, recipient: &str) {
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeNext)
            .recipient(recipient)
            .send();
    }

    pub fn send_replace_tracks<I: Iterator<Item = SpotifyId>>(&mut self,
                                                              recipient: &str,
                                                              track_ids: I) {
        let state = track_ids_to_state(track_ids);
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeReplace)
            .recipient(recipient)
            .state(state)
            .send();
    }

    pub fn send_load_tracks<I: Iterator<Item = SpotifyId>>(&mut self,
                                                           recipient: &str,
                                                           track_ids: I) {
        let state = track_ids_to_state(track_ids);
        let mut internal = self.0.lock().unwrap();
        CommandSender::new(&mut *internal, MessageType::kMessageTypeLoad)
            .recipient(recipient)
            .state(state)
            .send();
    }

    pub fn get_queue(&self) -> Vec<SpotifyId> {
        self.0.lock().unwrap().tracks.clone()
    }
}

impl SpircInternal {
    fn on_update(&mut self, player_state: &PlayerState) {
        let end_of_track = player_state.end_of_track();
        if end_of_track {
            self.index = (self.index + 1) % self.tracks.len() as u32;
            let track = self.tracks[self.index as usize];
            self.player.load(track, true, 0);
        } else {
            self.notify_with_player_state(false, None, player_state);
        }
    }

    fn handle(&mut self, frame: protocol::spirc::Frame) {
        if frame.get_ident() == self.ident ||
           (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
            return;
        }

        if frame.get_recipient().len() > 0 {
            self.last_command_ident = frame.get_ident().to_owned();
            self.last_command_msgid = frame.get_seq_nr();
        }

        if frame.has_ident() && !frame.has_goodbye() && frame.has_device_state() {
            self.devices.insert(frame.get_ident().into(),
                                frame.get_device_state().get_name().into());
        }

        match frame.get_typ() {
            MessageType::kMessageTypeHello => {
                self.notify(false, Some(frame.get_ident()));
            }
            MessageType::kMessageTypeLoad => {
                if !self.is_active {
                    self.is_active = true;
                    self.became_active_at = util::now_ms();
                }

                self.reload_tracks(&frame);
                if self.tracks.len() > 0 {
                    let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
                    let track = self.tracks[self.index as usize];
                    let position = frame.get_state().get_position_ms();
                    self.player.load(track, play, position);
                } else {
                    self.notify(false, Some(frame.get_ident()));
                }
            }
            MessageType::kMessageTypePlay => {
                self.player.play();
            }
            MessageType::kMessageTypePause => {
                self.player.pause();
            }
            MessageType::kMessageTypeNext => {
                self.index = (self.index + 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            MessageType::kMessageTypePrev => {
                self.index = (self.index - 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            MessageType::kMessageTypeSeek => {
                self.player.seek(frame.get_position());
            }
            MessageType::kMessageTypeReplace => {
                self.reload_tracks(&frame);
            }
            MessageType::kMessageTypeNotify => {
                if self.is_active && frame.get_device_state().get_is_active() {
                    self.is_active = false;
                    self.player.stop();
                }
            }
            MessageType::kMessageTypeVolume => {
                self.player.volume(frame.get_volume() as u16);
            }
            MessageType::kMessageTypeGoodbye => {
                if frame.has_ident() {
                    self.devices.remove(frame.get_ident());
                }
            }
            _ => (),
        }
    }

    fn reload_tracks(&mut self, ref frame: &protocol::spirc::Frame) {
        self.index = frame.get_state().get_playing_track_index();
        self.tracks = frame.get_state()
                           .get_track()
                           .iter()
                           .filter(|track| track.has_gid())
                           .map(|track| SpotifyId::from_raw(track.get_gid()))
                           .collect();
    }

    fn notify(&mut self, hello: bool, recipient: Option<&str>) {
        let mut cs = CommandSender::new(self,
                                        if hello {
                                            MessageType::kMessageTypeHello
                                        } else {
                                            MessageType::kMessageTypeNotify
                                        });
        if let Some(s) = recipient {
            cs = cs.recipient(&s);
        }
        cs.send();
    }

    fn notify_with_player_state(&mut self,
                                hello: bool,
                                recipient: Option<&str>,
                                player_state: &PlayerState) {
        let mut cs = CommandSender::new(self,
                                        if hello {
                                            MessageType::kMessageTypeHello
                                        } else {
                                            MessageType::kMessageTypeNotify
                                        })
                         .player_state(player_state);
        if let Some(s) = recipient {
            cs = cs.recipient(&s);
        }
        cs.send();
    }

    fn spirc_state(&self, player_state: &PlayerState) -> protocol::spirc::State {
        let (position_ms, position_measured_at) = player_state.position();

        protobuf_init!(protocol::spirc::State::new(), {
            status: player_state.status(),
            position_ms: position_ms,
            position_measured_at: position_measured_at as u64,

            playing_track_index: self.index,
            track: self.tracks.iter().map(|track| {
                protobuf_init!(protocol::spirc::TrackRef::new(), {
                    gid: track.to_raw().to_vec()
                })
            }).collect(),

            shuffle: self.shuffle,
            repeat: self.repeat,

            playing_from_fallback: true,

            last_command_ident: self.last_command_ident.clone(),
            last_command_msgid: self.last_command_msgid
        })
    }

    fn device_state(&self, player_state: &PlayerState) -> protocol::spirc::DeviceState {
        protobuf_init!(protocol::spirc::DeviceState::new(), {
            sw_version: version::version_string(),
            is_active: self.is_active,
            can_play: self.can_play,
            volume: player_state.volume() as u32,
            name: self.name.clone(),
            error_code: 0,
            became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
            capabilities => [
                @{
                    typ: protocol::spirc::CapabilityType::kCanBePlayer,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kDeviceType,
                    intValue => [ self.device_type as i64 ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportsLogout,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kIsObservable,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kVolumeSteps,
                    intValue => [10]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedContexts,
                    stringValue => [
                        "album",
                        "playlist",
                        "search",
                        "inbox",
                        "toplist",
                        "starred",
                        "publishedstarred",
                        "track",
                    ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedTypes,
                    stringValue => [
                        "audio/local",
                        "audio/track",
                        "local",
                        "track",
                    ]
                }
            ],
        })
    }

    fn uri(&self) -> String {
        format!("hm://remote/user/{}", self.session.username())
    }
}

struct CommandSender<'a> {
    spirc_internal: &'a mut SpircInternal,
    cmd: MessageType,
    recipient: Option<&'a str>,
    player_state: Option<&'a PlayerState>,
    state: Option<protocol::spirc::State>,
}

impl<'a> CommandSender<'a> {
    fn new(spirc_internal: &'a mut SpircInternal, cmd: MessageType) -> CommandSender {
        CommandSender {
            spirc_internal: spirc_internal,
            cmd: cmd,
            recipient: None,
            player_state: None,
            state: None,
        }
    }

    fn recipient(mut self, r: &'a str) -> CommandSender {
        self.recipient = Some(r);
        self
    }

    fn player_state(mut self, s: &'a PlayerState) -> CommandSender {
        self.player_state = Some(s);
        self
    }

    fn state(mut self, s: protocol::spirc::State) -> CommandSender<'a> {
        self.state = Some(s);
        self
    }

    fn send(self) {
        let state = self.player_state.map_or_else(|| {
            Cow::Owned(self.spirc_internal.player.state())
        }, |s| {
            Cow::Borrowed(s)
        });

        let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
            version: 1,
            ident: self.spirc_internal.ident.clone(),
            protocol_version: "2.0.0",
            seq_nr: { self.spirc_internal.seq_nr += 1; self.spirc_internal.seq_nr  },
            typ: self.cmd,
            recipient: RepeatedField::from_vec(
                self.recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![])
                ),
            device_state: self.spirc_internal.device_state(&state),
            state_update_id: state.update_time()
        });

        if self.spirc_internal.is_active {
            pkt.set_state(self.spirc_internal.spirc_state(&state));
        }

        self.spirc_internal
            .session
            .mercury(MercuryRequest {
                method: MercuryMethod::SEND,
                uri: self.spirc_internal.uri(),
                content_type: None,
                payload: vec![pkt.write_to_bytes().unwrap()],
            })
            .fire();
    }
}

fn track_ids_to_state<I: Iterator<Item = SpotifyId>>(track_ids: I) -> protocol::spirc::State {
    let tracks: Vec<protocol::spirc::TrackRef> =
        track_ids.map(|i| {
                     protobuf_init!(protocol::spirc::TrackRef::new(), { gid: i.to_raw().to_vec()})
                 })
                 .collect();
    protobuf_init!(protocol::spirc::State::new(), {
                    track: RepeatedField::from_vec(tracks)
                })
}
<|endoftext|>


<commit_before>//! Basic manipulation of rectangles.

use std::cmp;

/// A rectangular region of non-zero width and height.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rect {
    left: i32,
    top: i32,
    width: u32,
    height: u32,
}

/// A geometrical representation of a set of 2D points with coordinate type T.
pub trait Region<T> {
    fn contains(&self, x: T, y: T) -> bool;
}

impl Rect {
    /// Reduces possibility of confusing coordinates and dimensions
    /// when specifying rects.
    pub fn at(x: i32, y: i32) -> RectPosition {
        RectPosition { left: x, top: y }
    }

    /// Smallest y-coordinate reached by rect.
    pub fn top(&self) -> i32 {
        self.top
    }

    /// Smallest x-coordinate reached by rect.
    pub fn left(&self) -> i32 {
        self.left
    }

    /// Greatest y-coordinate reached by rect.
    pub fn bottom(&self) -> i32 {
        self.top + (self.height as i32) - 1
    }

    /// Greatest x-coordinate reached by rect.
    pub fn right(&self) -> i32 {
        self.left + (self.width as i32) - 1
    }

    /// Width of rect.
    pub fn width(&self) -> u32 {
        self.width
    }

    /// Height of rect.
    pub fn height(&self) -> u32 {
        self.height
    }

    /// Returns the intersection of self and other, or none if they are are disjoint.
    pub fn intersect(&self, other: Rect) -> Option<Rect> {
        let left = cmp::max(self.left, other.left);
        let top = cmp::max(self.top, other.top);
        let right = cmp::min(self.right(), other.right());
        let bottom = cmp::min(self.bottom(), other.bottom());

        if right < left || bottom < top {
            return None;
        }

        Some(Rect {
            left: left,
            top: top,
            width: (right - left) as u32 + 1,
            height: (bottom - top) as u32 + 1,
        })
    }
}

impl Region<i32> for Rect {
    fn contains(&self, x: i32, y: i32) -> bool {
        return self.left <= x && x <= self.right() &&
               self.top <= y && y <= self.bottom();
    }
}

impl Region<f32> for Rect {
    fn contains(&self, x: f32, y: f32) -> bool {
        return self.left as f32 <= x && x <= self.right() as f32 &&
               self.top as f32 <= y && y <= self.bottom() as f32;
    }
}

/// Position of the top left of a rectangle.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RectPosition {
    left: i32,
    top: i32,
}

impl RectPosition {
    /// Construct a rectangle form a position and size. Width and height
    /// are required to be strictly positive.
    pub fn of_size(self, width: u32, height: u32) -> Rect {
        assert!(width > 0, "width must be strictly positive");
        assert!(height > 0, "height must be strictly positive");
        Rect {
            left: self.left,
            top: self.top,
            width: width,
            height: height,
        }
    }
}

#[cfg(test)]
mod test {
    use super::{
        Rect, Region
    };

    #[test]
    #[should_panic]
    fn test_rejects_empty_rectangle() {
        Rect::at(1, 2).of_size(0, 1);
    }

    #[test]
    fn test_intersect_disjoint() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(10, 10).of_size(100, 12);
        assert_eq!(r.intersect(s), None);
    }

    #[test]
    fn test_intersect_overlapping() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(1, 4).of_size(10, 12);
        let i = Rect::at(1, 4).of_size(4, 1);
        assert_eq!(r.intersect(s), Some(i));
    }

    #[test]
    fn test_contains_i32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5, 5));
        assert!(r.contains(10, 10));
        assert!(!r.contains(10, 11));
        assert!(!r.contains(11, 10));
    }

    #[test]
    fn test_contains_f32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5f32, 5f32));
        assert!(!r.contains(10.1f32, 10f32));
    }
}
<commit_msg>Fix typo in comment<commit_after>//! Basic manipulation of rectangles.

use std::cmp;

/// A rectangular region of non-zero width and height.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rect {
    left: i32,
    top: i32,
    width: u32,
    height: u32,
}

/// A geometrical representation of a set of 2D points with coordinate type T.
pub trait Region<T> {
    fn contains(&self, x: T, y: T) -> bool;
}

impl Rect {
    /// Reduces possibility of confusing coordinates and dimensions
    /// when specifying rects.
    pub fn at(x: i32, y: i32) -> RectPosition {
        RectPosition { left: x, top: y }
    }

    /// Smallest y-coordinate reached by rect.
    pub fn top(&self) -> i32 {
        self.top
    }

    /// Smallest x-coordinate reached by rect.
    pub fn left(&self) -> i32 {
        self.left
    }

    /// Greatest y-coordinate reached by rect.
    pub fn bottom(&self) -> i32 {
        self.top + (self.height as i32) - 1
    }

    /// Greatest x-coordinate reached by rect.
    pub fn right(&self) -> i32 {
        self.left + (self.width as i32) - 1
    }

    /// Width of rect.
    pub fn width(&self) -> u32 {
        self.width
    }

    /// Height of rect.
    pub fn height(&self) -> u32 {
        self.height
    }

    /// Returns the intersection of self and other, or none if they are are disjoint.
    pub fn intersect(&self, other: Rect) -> Option<Rect> {
        let left = cmp::max(self.left, other.left);
        let top = cmp::max(self.top, other.top);
        let right = cmp::min(self.right(), other.right());
        let bottom = cmp::min(self.bottom(), other.bottom());

        if right < left || bottom < top {
            return None;
        }

        Some(Rect {
            left: left,
            top: top,
            width: (right - left) as u32 + 1,
            height: (bottom - top) as u32 + 1,
        })
    }
}

impl Region<i32> for Rect {
    fn contains(&self, x: i32, y: i32) -> bool {
        return self.left <= x && x <= self.right() &&
               self.top <= y && y <= self.bottom();
    }
}

impl Region<f32> for Rect {
    fn contains(&self, x: f32, y: f32) -> bool {
        return self.left as f32 <= x && x <= self.right() as f32 &&
               self.top as f32 <= y && y <= self.bottom() as f32;
    }
}

/// Position of the top left of a rectangle.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RectPosition {
    left: i32,
    top: i32,
}

impl RectPosition {
    /// Construct a rectangle from a position and size. Width and height
    /// are required to be strictly positive.
    pub fn of_size(self, width: u32, height: u32) -> Rect {
        assert!(width > 0, "width must be strictly positive");
        assert!(height > 0, "height must be strictly positive");
        Rect {
            left: self.left,
            top: self.top,
            width: width,
            height: height,
        }
    }
}

#[cfg(test)]
mod test {
    use super::{
        Rect, Region
    };

    #[test]
    #[should_panic]
    fn test_rejects_empty_rectangle() {
        Rect::at(1, 2).of_size(0, 1);
    }

    #[test]
    fn test_intersect_disjoint() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(10, 10).of_size(100, 12);
        assert_eq!(r.intersect(s), None);
    }

    #[test]
    fn test_intersect_overlapping() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(1, 4).of_size(10, 12);
        let i = Rect::at(1, 4).of_size(4, 1);
        assert_eq!(r.intersect(s), Some(i));
    }

    #[test]
    fn test_contains_i32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5, 5));
        assert!(r.contains(10, 10));
        assert!(!r.contains(10, 11));
        assert!(!r.contains(11, 10));
    }

    #[test]
    fn test_contains_f32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5f32, 5f32));
        assert!(!r.contains(10.1f32, 10f32));
    }
}
<|endoftext|>


<commit_before>// NB: Not sure why this works. I expect the box argument to leak when
// we run out of stack. Maybe the box annihilator works it out?

// error-pattern:ran out of stack
fn main() {
    eat(move @0);
}

fn eat(
    +a: @int
) {
    eat(move a)
}
<commit_msg>xfail-test run-fail/out-of-stack-managed-box<commit_after>// xfail-test iloops with optimizations on

// NB: Not sure why this works. I expect the box argument to leak when
// we run out of stack. Maybe the box annihilator works it out?

// error-pattern:ran out of stack
fn main() {
    eat(move @0);
}

fn eat(
    +a: @int
) {
    eat(move a)
}
<|endoftext|>


<commit_before>use input::Input;
use ncurses;
use ncurses::ll::{attr_t, chtype, mmask_t};
use ncurses::NCURSES_ATTR_SHIFT;

pub use ncurses::{ACS_LLCORNER, ACS_LRCORNER, ACS_ULCORNER, ACS_URCORNER};
pub use ncurses::{ACS_BTEE, ACS_HLINE, ACS_LTEE, ACS_PLUS, ACS_RTEE, ACS_TTEE, ACS_VLINE};
pub use ncurses::{ACS_S1, ACS_S9, ACS_BULLET, ACS_CKBOARD, ACS_DEGREE, ACS_DIAMOND, ACS_PLMINUS};
pub use ncurses::{ACS_BLOCK, ACS_BOARD, ACS_DARROW, ACS_LANTERN, ACS_LARROW, ACS_RARROW,
                  ACS_UARROW};
pub use ncurses::{ACS_S3, ACS_S7, ACS_GEQUAL, ACS_LEQUAL, ACS_NEQUAL, ACS_PI, ACS_STERLING};
pub use ncurses::{ACS_BBSS, ACS_BSSB, ACS_SBBS, ACS_SBSS, ACS_SSBB, ACS_SSBS, ACS_SSSB};
pub use ncurses::{ACS_BSBS, ACS_BSSS, ACS_SBSB, ACS_SSSS};

pub use ncurses::{BUTTON1_CLICKED, BUTTON1_DOUBLE_CLICKED, BUTTON1_PRESSED, BUTTON1_RELEASED,
                  BUTTON1_TRIPLE_CLICKED};
pub use ncurses::{BUTTON2_CLICKED, BUTTON2_DOUBLE_CLICKED, BUTTON2_PRESSED, BUTTON2_RELEASED,
                  BUTTON2_TRIPLE_CLICKED};
pub use ncurses::{BUTTON3_CLICKED, BUTTON3_DOUBLE_CLICKED, BUTTON3_PRESSED, BUTTON3_RELEASED,
                  BUTTON3_TRIPLE_CLICKED};
pub use ncurses::{BUTTON4_CLICKED, BUTTON4_DOUBLE_CLICKED, BUTTON4_PRESSED, BUTTON4_RELEASED,
                  BUTTON4_TRIPLE_CLICKED};
pub use ncurses::{BUTTON5_CLICKED, BUTTON5_DOUBLE_CLICKED, BUTTON5_PRESSED, BUTTON5_RELEASED,
                  BUTTON5_TRIPLE_CLICKED};
pub use ncurses::REPORT_MOUSE_POSITION;

fn NCURSES_BITS(mask: u32, shift: u32) -> u32 {
    mask << (shift + NCURSES_ATTR_SHIFT) as usize
}

pub fn COLOR_PAIR(n: chtype) -> attr_t {
    NCURSES_BITS(n as u32, 0u32) as attr_t
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const A_ALTCHARSET: attr_t = (1u32 << (14u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_ATTRIBUTES: attr_t = (!0u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BLINK: attr_t = (1u32 << (11u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BOLD: attr_t = (1u32 << (13u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_CHARTEXT: attr_t = (1u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_COLOR: attr_t = ((((1u32) << 8) - 1u32) << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_DIM: attr_t = (1u32 << (12u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_ITALIC: attr_t = (1u32 << (23 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_INVIS: attr_t = (1u32 << (15u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_LEFTLINE: attr_t = 0; // Not supported on ncurses
pub const A_NORMAL: attr_t = 0u32 as attr_t;
pub const A_OVERLINE: attr_t = 0; // Not supported on ncurses
pub const A_REVERSE: attr_t = (1u32 << (10 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_RIGHTLINE: attr_t = 0; // Not supported on ncurses
pub const A_STANDOUT: attr_t = (1u32 << (8 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_STRIKEOUT: attr_t = 0; // Not supported on ncurses
pub const A_UNDERLINE: attr_t = (1u32 << (9 + NCURSES_ATTR_SHIFT)) as attr_t;

pub const KEY_RESIZE: i32 = 0632;

pub const KEY_OFFSET: i32 = 0o0400;
pub const KEY_F15: i32 = (KEY_OFFSET + 0x17);
pub const KEY_EVENT: i32 = (KEY_OFFSET + 0o633);

pub const SPECIAL_KEY_CODES: [Input; 108] = [
    Input::KeyCodeYes,
    Input::KeyBreak,
    Input::KeyDown,
    Input::KeyUp,
    Input::KeyLeft,
    Input::KeyRight,
    Input::KeyHome,
    Input::KeyBackspace,
    Input::KeyF0,
    Input::KeyF1,
    Input::KeyF2,
    Input::KeyF3,
    Input::KeyF4,
    Input::KeyF5,
    Input::KeyF6,
    Input::KeyF7,
    Input::KeyF8,
    Input::KeyF9,
    Input::KeyF10,
    Input::KeyF11,
    Input::KeyF12,
    Input::KeyF13,
    Input::KeyF14,
    Input::KeyF15,
    // ncurses reserves space for 64 function keys, but we've
    // only implemented 15. This has to be taken into account
    // when converting the integer into an index of this array
    Input::KeyDL,
    Input::KeyIL,
    Input::KeyDC,
    Input::KeyIC,
    Input::KeyEIC,
    Input::KeyClear,
    Input::KeyEOS,
    Input::KeyEOL,
    Input::KeySF,
    Input::KeySR,
    Input::KeyNPage,
    Input::KeyPPage,
    Input::KeySTab,
    Input::KeyCTab,
    Input::KeyCATab,
    Input::KeyEnter,
    Input::KeySReset,
    Input::KeyReset,
    Input::KeyPrint,
    Input::KeyLL,
    Input::KeyA1,
    Input::KeyA3,
    Input::KeyB2,
    Input::KeyC1,
    Input::KeyC3,
    Input::KeyBTab,
    Input::KeyBeg,
    Input::KeyCancel,
    Input::KeyClose,
    Input::KeyCommand,
    Input::KeyCopy,
    Input::KeyCreate,
    Input::KeyEnd,
    Input::KeyExit,
    Input::KeyFind,
    Input::KeyHelp,
    Input::KeyMark,
    Input::KeyMessage,
    Input::KeyMove,
    Input::KeyNext,
    Input::KeyOpen,
    Input::KeyOptions,
    Input::KeyPrevious,
    Input::KeyRedo,
    Input::KeyReference,
    Input::KeyRefresh,
    Input::KeyReplace,
    Input::KeyRestart,
    Input::KeyResume,
    Input::KeySave,
    Input::KeySBeg,
    Input::KeySCancel,
    Input::KeySCommand,
    Input::KeySCopy,
    Input::KeySCreate,
    Input::KeySDC,
    Input::KeySDL,
    Input::KeySelect,
    Input::KeySEnd,
    Input::KeySEOL,
    Input::KeySExit,
    Input::KeySFind,
    Input::KeySHelp,
    Input::KeySHome,
    Input::KeySIC,
    Input::KeySLeft,
    Input::KeySMessage,
    Input::KeySMove,
    Input::KeySNext,
    Input::KeySOptions,
    Input::KeySPrevious,
    Input::KeySPrint,
    Input::KeySRedo,
    Input::KeySReplace,
    Input::KeySRight,
    Input::KeySResume,
    Input::KeySSave,
    Input::KeySSuspend,
    Input::KeySUndo,
    Input::KeySuspend,
    Input::KeyUndo,
    Input::KeyMouse,
    Input::KeyResize,
    Input::KeyEvent,
];

pub const ALL_MOUSE_EVENTS: mmask_t = ncurses::ALL_MOUSE_EVENTS as mmask_t;
<commit_msg>Re-export the mouse button constants from ncurses-rs so that they match the type of MEVENT.bstate<commit_after>use input::Input;
use ncurses;
use ncurses::ll::{attr_t, chtype, mmask_t};
use ncurses::NCURSES_ATTR_SHIFT;

pub use ncurses::{ACS_LLCORNER, ACS_LRCORNER, ACS_ULCORNER, ACS_URCORNER};
pub use ncurses::{ACS_BTEE, ACS_HLINE, ACS_LTEE, ACS_PLUS, ACS_RTEE, ACS_TTEE, ACS_VLINE};
pub use ncurses::{ACS_S1, ACS_S9, ACS_BULLET, ACS_CKBOARD, ACS_DEGREE, ACS_DIAMOND, ACS_PLMINUS};
pub use ncurses::{ACS_BLOCK, ACS_BOARD, ACS_DARROW, ACS_LANTERN, ACS_LARROW, ACS_RARROW,
                  ACS_UARROW};
pub use ncurses::{ACS_S3, ACS_S7, ACS_GEQUAL, ACS_LEQUAL, ACS_NEQUAL, ACS_PI, ACS_STERLING};
pub use ncurses::{ACS_BBSS, ACS_BSSB, ACS_SBBS, ACS_SBSS, ACS_SSBB, ACS_SSBS, ACS_SSSB};
pub use ncurses::{ACS_BSBS, ACS_BSSS, ACS_SBSB, ACS_SSSS};

// We need to re-export the BUTTONX_Y constants as 'mmask_t's, otherwise you can't compare them
// to a MEVENT's bstate without casting
pub const BUTTON1_PRESSED: mmask_t = ncurses::BUTTON1_PRESSED as mmask_t;
pub const BUTTON1_RELEASED: mmask_t = ncurses::BUTTON1_RELEASED as mmask_t;
pub const BUTTON1_CLICKED: mmask_t = ncurses::BUTTON1_CLICKED as mmask_t;
pub const BUTTON1_DOUBLE_CLICKED: mmask_t = ncurses::BUTTON1_DOUBLE_CLICKED as mmask_t;
pub const BUTTON1_TRIPLE_CLICKED: mmask_t = ncurses::BUTTON1_TRIPLE_CLICKED as mmask_t;

pub const BUTTON2_PRESSED: mmask_t = ncurses::BUTTON2_PRESSED as mmask_t;
pub const BUTTON2_RELEASED: mmask_t = ncurses::BUTTON2_RELEASED as mmask_t;
pub const BUTTON2_CLICKED: mmask_t = ncurses::BUTTON2_CLICKED as mmask_t;
pub const BUTTON2_DOUBLE_CLICKED: mmask_t = ncurses::BUTTON2_DOUBLE_CLICKED as mmask_t;
pub const BUTTON2_TRIPLE_CLICKED: mmask_t = ncurses::BUTTON2_TRIPLE_CLICKED as mmask_t;

pub const BUTTON3_PRESSED: mmask_t = ncurses::BUTTON3_PRESSED as mmask_t;
pub const BUTTON3_RELEASED: mmask_t = ncurses::BUTTON3_RELEASED as mmask_t;
pub const BUTTON3_CLICKED: mmask_t = ncurses::BUTTON3_CLICKED as mmask_t;
pub const BUTTON3_DOUBLE_CLICKED: mmask_t = ncurses::BUTTON3_DOUBLE_CLICKED as mmask_t;
pub const BUTTON3_TRIPLE_CLICKED: mmask_t = ncurses::BUTTON3_TRIPLE_CLICKED as mmask_t;

pub const BUTTON4_PRESSED: mmask_t = ncurses::BUTTON4_PRESSED as mmask_t;
pub const BUTTON4_RELEASED: mmask_t = ncurses::BUTTON4_RELEASED as mmask_t;
pub const BUTTON4_CLICKED: mmask_t = ncurses::BUTTON4_CLICKED as mmask_t;
pub const BUTTON4_DOUBLE_CLICKED: mmask_t = ncurses::BUTTON4_DOUBLE_CLICKED as mmask_t;
pub const BUTTON4_TRIPLE_CLICKED: mmask_t = ncurses::BUTTON4_TRIPLE_CLICKED as mmask_t;

pub const BUTTON5_PRESSED: mmask_t = ncurses::BUTTON5_PRESSED as mmask_t;
pub const BUTTON5_RELEASED: mmask_t = ncurses::BUTTON5_RELEASED as mmask_t;
pub const BUTTON5_CLICKED: mmask_t = ncurses::BUTTON5_CLICKED as mmask_t;
pub const BUTTON5_DOUBLE_CLICKED: mmask_t = ncurses::BUTTON5_DOUBLE_CLICKED as mmask_t;
pub const BUTTON5_TRIPLE_CLICKED: mmask_t = ncurses::BUTTON5_TRIPLE_CLICKED as mmask_t;

pub use ncurses::REPORT_MOUSE_POSITION;

fn NCURSES_BITS(mask: u32, shift: u32) -> u32 {
    mask << (shift + NCURSES_ATTR_SHIFT) as usize
}

pub fn COLOR_PAIR(n: chtype) -> attr_t {
    NCURSES_BITS(n as u32, 0u32) as attr_t
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const A_ALTCHARSET: attr_t = (1u32 << (14u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_ATTRIBUTES: attr_t = (!0u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BLINK: attr_t = (1u32 << (11u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_BOLD: attr_t = (1u32 << (13u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_CHARTEXT: attr_t = (1u32 << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_COLOR: attr_t = ((((1u32) << 8) - 1u32) << (0u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_DIM: attr_t = (1u32 << (12u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_ITALIC: attr_t = (1u32 << (23 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_INVIS: attr_t = (1u32 << (15u32 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_LEFTLINE: attr_t = 0; // Not supported on ncurses
pub const A_NORMAL: attr_t = 0u32 as attr_t;
pub const A_OVERLINE: attr_t = 0; // Not supported on ncurses
pub const A_REVERSE: attr_t = (1u32 << (10 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_RIGHTLINE: attr_t = 0; // Not supported on ncurses
pub const A_STANDOUT: attr_t = (1u32 << (8 + NCURSES_ATTR_SHIFT)) as attr_t;
pub const A_STRIKEOUT: attr_t = 0; // Not supported on ncurses
pub const A_UNDERLINE: attr_t = (1u32 << (9 + NCURSES_ATTR_SHIFT)) as attr_t;

pub const KEY_RESIZE: i32 = 0632;

pub const KEY_OFFSET: i32 = 0o0400;
pub const KEY_F15: i32 = (KEY_OFFSET + 0x17);
pub const KEY_EVENT: i32 = (KEY_OFFSET + 0o633);

pub const SPECIAL_KEY_CODES: [Input; 108] = [
    Input::KeyCodeYes,
    Input::KeyBreak,
    Input::KeyDown,
    Input::KeyUp,
    Input::KeyLeft,
    Input::KeyRight,
    Input::KeyHome,
    Input::KeyBackspace,
    Input::KeyF0,
    Input::KeyF1,
    Input::KeyF2,
    Input::KeyF3,
    Input::KeyF4,
    Input::KeyF5,
    Input::KeyF6,
    Input::KeyF7,
    Input::KeyF8,
    Input::KeyF9,
    Input::KeyF10,
    Input::KeyF11,
    Input::KeyF12,
    Input::KeyF13,
    Input::KeyF14,
    Input::KeyF15,
    // ncurses reserves space for 64 function keys, but we've
    // only implemented 15. This has to be taken into account
    // when converting the integer into an index of this array
    Input::KeyDL,
    Input::KeyIL,
    Input::KeyDC,
    Input::KeyIC,
    Input::KeyEIC,
    Input::KeyClear,
    Input::KeyEOS,
    Input::KeyEOL,
    Input::KeySF,
    Input::KeySR,
    Input::KeyNPage,
    Input::KeyPPage,
    Input::KeySTab,
    Input::KeyCTab,
    Input::KeyCATab,
    Input::KeyEnter,
    Input::KeySReset,
    Input::KeyReset,
    Input::KeyPrint,
    Input::KeyLL,
    Input::KeyA1,
    Input::KeyA3,
    Input::KeyB2,
    Input::KeyC1,
    Input::KeyC3,
    Input::KeyBTab,
    Input::KeyBeg,
    Input::KeyCancel,
    Input::KeyClose,
    Input::KeyCommand,
    Input::KeyCopy,
    Input::KeyCreate,
    Input::KeyEnd,
    Input::KeyExit,
    Input::KeyFind,
    Input::KeyHelp,
    Input::KeyMark,
    Input::KeyMessage,
    Input::KeyMove,
    Input::KeyNext,
    Input::KeyOpen,
    Input::KeyOptions,
    Input::KeyPrevious,
    Input::KeyRedo,
    Input::KeyReference,
    Input::KeyRefresh,
    Input::KeyReplace,
    Input::KeyRestart,
    Input::KeyResume,
    Input::KeySave,
    Input::KeySBeg,
    Input::KeySCancel,
    Input::KeySCommand,
    Input::KeySCopy,
    Input::KeySCreate,
    Input::KeySDC,
    Input::KeySDL,
    Input::KeySelect,
    Input::KeySEnd,
    Input::KeySEOL,
    Input::KeySExit,
    Input::KeySFind,
    Input::KeySHelp,
    Input::KeySHome,
    Input::KeySIC,
    Input::KeySLeft,
    Input::KeySMessage,
    Input::KeySMove,
    Input::KeySNext,
    Input::KeySOptions,
    Input::KeySPrevious,
    Input::KeySPrint,
    Input::KeySRedo,
    Input::KeySReplace,
    Input::KeySRight,
    Input::KeySResume,
    Input::KeySSave,
    Input::KeySSuspend,
    Input::KeySUndo,
    Input::KeySuspend,
    Input::KeyUndo,
    Input::KeyMouse,
    Input::KeyResize,
    Input::KeyEvent,
];

pub const ALL_MOUSE_EVENTS: mmask_t = ncurses::ALL_MOUSE_EVENTS as mmask_t;
<|endoftext|>


<commit_before>use die::Die;
use die::DieType;
use uuid::Uuid;

#[derive(Debug, Deserialize, Serialize)]
pub struct Roll {
    /// Unique identifier for the roll
    pub _id: String,

    /// The dice that compose this roll
    pub dice: Vec<Die>,

    /// Modifiers to apply to the combined value
    pub modifiers: Vec<i8>,

    /// The combined value of the die before modifiers
    pub raw_value: i16,

    /// The associated token (optional)
    pub token: Option<String>,

    /// The final combined value of the die after modifiers
    pub value: i16,
}

impl Roll {
    pub fn new(mut dice: Vec<Die>) -> Roll {
        // Roll each dice
        for die in &mut dice {
            die.roll();
        }

        let value = dice.iter().fold(0, |sum, d| sum + d.value as i16);

        Roll {
            _id: Uuid::new_v4().to_string(),
            dice,
            modifiers: Vec::new(),
            raw_value: value,
            token: None,
            value,
        }
    }

    /// Associate this roll with a token
    pub fn add_token(&mut self, token: String) {
        self.token = Some(token)
    }

    /// Keep the highest rolled dice
    pub fn keep_high(&mut self, keep: u8) {
        // Sort the dice by value, drop everything below the keep value
        let mut count = 0;
        self.dice.sort_by(|a, b| b.value.cmp(&a.value));
        for die in &mut self.dice {
            if count >= keep {
                die.drop();
                self.value -= die.value as i16;
            }
            count += 1;
        }
        // sort by timestamp again before finishing the method
        self.dice.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
    }

    /// Keep the lowest rolled dice
    pub fn keep_low(&mut self, keep: u8) {
        // Sort the dice by value, drop everything below the keep value
        let mut count = 0;
        self.dice.sort_by(|a, b| a.value.cmp(&b.value));
        for die in &mut self.dice {
            if count >= keep {
                die.drop();
                self.value -= die.value as i16;
            }
            count += 1;
        }
        // sort by timestamp again before finishing the method
        self.dice.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
    }

    /// Reroll dice one time that are above a certain threshold
    pub fn reroll_dice_once_above(&mut self, threshold: i8) {
        let mut new_dice = Vec::new();
        for die in &mut self.dice {
            if !die.is_rerolled && die.value >= threshold {
                let mut d = Die::new(die.die);
                d.roll();
                &die.rerolled(&d);
                new_dice.push(d);
            }
        }

        self.dice.append(&mut new_dice);
    }

    /// Reroll dice one time that are below a certain threshold
    pub fn reroll_dice_once_below(&mut self, threshold: i8) {
        let mut new_dice = Vec::new();
        for die in &mut self.dice {
            if !die.is_rerolled && die.value <= threshold {
                let mut d = Die::new(die.die);
                d.roll();
                &die.rerolled(&d);
                new_dice.push(d);
            }
        }

        self.dice.append(&mut new_dice);
    }

    /// Reroll dice forever that are above a certain threshold (e.g. Exploding Dice)
    pub fn reroll_dice_forever_above(&mut self, threshold: i8) {
        // Reroll any dice that need to be rerolled
        self.reroll_dice_once_above(threshold);

        let mut has_more = false;
        for die in self.dice.iter() {
            if !die.is_rerolled && die.value >= threshold {
                has_more = true
            }
        }
        if has_more {
            self.reroll_dice_forever_below(threshold);
        }
    }

    /// Reroll dice forever that are below a certain threshold
    pub fn reroll_dice_forever_below(&mut self, threshold: i8) {
        // Reroll any dice that need to be rerolled
        self.reroll_dice_once_below(threshold);

        let mut has_more = false;
        for die in self.dice.iter() {
            if !die.is_rerolled && die.value <= threshold {
                has_more = true
            }
        }
        if has_more {
            self.reroll_dice_forever_below(threshold);
        }
    }
}

pub fn roll_and_keep_high(count: i8, die: DieType, keep: u8) -> Roll {
    let mut roll = roll_type(count, die);
    roll.keep_high(keep);
    roll
}

pub fn roll_and_keep_low(count: i8, die: DieType, keep: u8) -> Roll {
    let mut roll = roll_type(count, die);
    roll.keep_low(keep);
    roll
}

pub fn roll_d4(count: i8) -> Roll {
    roll_type(count, DieType::D4)
}

pub fn roll_d6(count: i8) -> Roll {
    roll_type(count, DieType::D6)
}

pub fn roll_d8(count: i8) -> Roll {
    roll_type(count, DieType::D8)
}

pub fn roll_d10(count: i8) -> Roll {
    roll_type(count, DieType::D10)
}

pub fn roll_d12(count: i8) -> Roll {
    roll_type(count, DieType::D12)
}

pub fn roll_d20(count: i8) -> Roll {
    roll_type(count, DieType::D20)
}

fn roll_type(count: i8, die: DieType) -> Roll {
    let mut dice = Vec::new();
    for _ in 0..count {
        dice.push(Die::new(die));
    }
    Roll::new(dice)
}

pub fn roll_with_advantage() -> Roll {
    let mut roll = roll_d20(2);
    roll.keep_high(1);
    roll
}

pub fn roll_with_disadvantage() -> Roll {
    let mut roll = roll_d20(2);
    roll.keep_low(1);
    roll
}
<commit_msg>bugfix reroll dice forever above<commit_after>use die::Die;
use die::DieType;
use uuid::Uuid;

#[derive(Debug, Deserialize, Serialize)]
pub struct Roll {
    /// Unique identifier for the roll
    pub _id: String,

    /// The dice that compose this roll
    pub dice: Vec<Die>,

    /// Modifiers to apply to the combined value
    pub modifiers: Vec<i8>,

    /// The combined value of the die before modifiers
    pub raw_value: i16,

    /// The associated token (optional)
    pub token: Option<String>,

    /// The final combined value of the die after modifiers
    pub value: i16,
}

impl Roll {
    pub fn new(mut dice: Vec<Die>) -> Roll {
        // Roll each dice
        for die in &mut dice {
            die.roll();
        }

        let value = dice.iter().fold(0, |sum, d| sum + d.value as i16);

        Roll {
            _id: Uuid::new_v4().to_string(),
            dice,
            modifiers: Vec::new(),
            raw_value: value,
            token: None,
            value,
        }
    }

    /// Associate this roll with a token
    pub fn add_token(&mut self, token: String) {
        self.token = Some(token)
    }

    /// Keep the highest rolled dice
    pub fn keep_high(&mut self, keep: u8) {
        // Sort the dice by value, drop everything below the keep value
        let mut count = 0;
        self.dice.sort_by(|a, b| b.value.cmp(&a.value));
        for die in &mut self.dice {
            if count >= keep {
                die.drop();
                self.value -= die.value as i16;
            }
            count += 1;
        }
        // sort by timestamp again before finishing the method
        self.dice.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
    }

    /// Keep the lowest rolled dice
    pub fn keep_low(&mut self, keep: u8) {
        // Sort the dice by value, drop everything below the keep value
        let mut count = 0;
        self.dice.sort_by(|a, b| a.value.cmp(&b.value));
        for die in &mut self.dice {
            if count >= keep {
                die.drop();
                self.value -= die.value as i16;
            }
            count += 1;
        }
        // sort by timestamp again before finishing the method
        self.dice.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
    }

    /// Reroll dice one time that are above a certain threshold
    pub fn reroll_dice_once_above(&mut self, threshold: i8) {
        let mut new_dice = Vec::new();
        for die in &mut self.dice {
            if !die.is_rerolled && die.value >= threshold {
                let mut d = Die::new(die.die);
                d.roll();
                &die.rerolled(&d);
                new_dice.push(d);
            }
        }

        self.dice.append(&mut new_dice);
    }

    /// Reroll dice one time that are below a certain threshold
    pub fn reroll_dice_once_below(&mut self, threshold: i8) {
        let mut new_dice = Vec::new();
        for die in &mut self.dice {
            if !die.is_rerolled && die.value <= threshold {
                let mut d = Die::new(die.die);
                d.roll();
                &die.rerolled(&d);
                new_dice.push(d);
            }
        }

        self.dice.append(&mut new_dice);
    }

    /// Reroll dice forever that are above a certain threshold (e.g. Exploding Dice)
    pub fn reroll_dice_forever_above(&mut self, threshold: i8) {
        // Reroll any dice that need to be rerolled
        self.reroll_dice_once_above(threshold);

        let mut has_more = false;
        for die in self.dice.iter() {
            if !die.is_rerolled && die.value >= threshold {
                has_more = true
            }
        }
        if has_more {
            self.reroll_dice_forever_above(threshold);
        }
    }

    /// Reroll dice forever that are below a certain threshold
    pub fn reroll_dice_forever_below(&mut self, threshold: i8) {
        // Reroll any dice that need to be rerolled
        self.reroll_dice_once_below(threshold);

        let mut has_more = false;
        for die in self.dice.iter() {
            if !die.is_rerolled && die.value <= threshold {
                has_more = true
            }
        }
        if has_more {
            self.reroll_dice_forever_below(threshold);
        }
    }
}

pub fn roll_and_keep_high(count: i8, die: DieType, keep: u8) -> Roll {
    let mut roll = roll_type(count, die);
    roll.keep_high(keep);
    roll
}

pub fn roll_and_keep_low(count: i8, die: DieType, keep: u8) -> Roll {
    let mut roll = roll_type(count, die);
    roll.keep_low(keep);
    roll
}

pub fn roll_d4(count: i8) -> Roll {
    roll_type(count, DieType::D4)
}

pub fn roll_d6(count: i8) -> Roll {
    roll_type(count, DieType::D6)
}

pub fn roll_d8(count: i8) -> Roll {
    roll_type(count, DieType::D8)
}

pub fn roll_d10(count: i8) -> Roll {
    roll_type(count, DieType::D10)
}

pub fn roll_d12(count: i8) -> Roll {
    roll_type(count, DieType::D12)
}

pub fn roll_d20(count: i8) -> Roll {
    roll_type(count, DieType::D20)
}

fn roll_type(count: i8, die: DieType) -> Roll {
    let mut dice = Vec::new();
    for _ in 0..count {
        dice.push(Die::new(die));
    }
    Roll::new(dice)
}

pub fn roll_with_advantage() -> Roll {
    let mut roll = roll_d20(2);
    roll.keep_high(1);
    roll
}

pub fn roll_with_disadvantage() -> Roll {
    let mut roll = roll_d20(2);
    roll.keep_low(1);
    roll
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::UnknownUnit;
use length::Length;
use scale_factor::ScaleFactor;
use num::*;

use num_traits::NumCast;
use std::fmt;
use std::ops::{Mul, Div};
use std::marker::PhantomData;

/// A 2d size tagged with a unit.
define_matrix! {
    #[derive(RustcDecodable, RustcEncodable)]
    pub struct TypedSize2D<T, U> {
        pub width: T,
        pub height: T,
    }
}

/// Default 2d size type with no unit.
///
/// `Size2D` provides the same methods as `TypedSize2D`.
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;

impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}×{:?}", self.width, self.height)
    }
}

impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "({}x{})", self.width, self.height)
    }
}

impl<T, U> TypedSize2D<T, U> {
    /// Constructor taing scalar values.
    pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
        TypedSize2D {
            width: width,
            height: height,
            _unit: PhantomData,
        }
    }
}

impl<T: Clone, U> TypedSize2D<T, U> {
    /// Constructor taing scalar stronlgy typed lengths.
    pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(width.get(), height.get())
    }
}

impl<T: Round, U> TypedSize2D<T, U> {
    /// Rounds each component to the nearest integer value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn round(&self) -> Self {
        TypedSize2D::new(self.width.round(), self.height.round())
    }
}

impl<T: Ceil, U> TypedSize2D<T, U> {
    /// Rounds each component to the smallest integer equal or greater than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn ceil(&self) -> Self {
        TypedSize2D::new(self.height.ceil(), self.width.ceil())
    }
}

impl<T: Floor, U> TypedSize2D<T, U> {
    /// Rounds each component to the biggest integer equal or lower than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn floor(&self) -> Self {
        TypedSize2D::new(self.width.floor(), self.height.floor())
    }
}

impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
    pub fn area(&self) -> U { self.width * self.height }
}

impl<T: Zero, U> TypedSize2D<T, U> {
    pub fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Zero, U> Zero for TypedSize2D<T, U> {
    fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn mul(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width * scale, self.height * scale)
    }
}

impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn div(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width / scale, self.height / scale)
    }
}

impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
    type Output = TypedSize2D<T, U2>;
    #[inline]
    fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U2> {
        TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
    }
}

impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
    type Output = TypedSize2D<T, U1>;
    #[inline]
    fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U1> {
        TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
    }
}

impl<T: Copy, U> TypedSize2D<T, U> {
    /// Returns self.width as a Length carrying the unit.
    #[inline]
    pub fn width_typed(&self) -> Length<T, U> { Length::new(self.width) }

    /// Returns self.height as a Length carrying the unit.
    #[inline]
    pub fn height_typed(&self) -> Length<T, U> { Length::new(self.height) }

    #[inline]
    pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }

    /// Drop the units, preserving only the numeric value.
    pub fn to_untyped(&self) -> Size2D<T> {
        TypedSize2D::new(self.width, self.height)
    }

    /// Tag a unitless value with units.
    pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
        TypedSize2D::new(p.width, p.height)
    }
}

impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
    /// Cast from one numeric representation to another, preserving the units.
    ///
    /// When casting from floating point to integer coordinates, the decimals are truncated
    /// as one would expect from a simple cast, but this behavior does not always marke sense
    /// geometrically. Consider using round(), ceil or floor() before casting.
    pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
        match (NumCast::from(self.width), NumCast::from(self.height)) {
            (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
            _ => None
        }
    }

    // Convenience functions for common casts

    /// Cast into an f32 size.
    pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an usize size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_uint(&self) -> TypedSize2D<usize, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i32 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i64 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
        self.cast().unwrap()
    }
}
<commit_msg>Implement Add and Sub traits for Size2D<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::UnknownUnit;
use length::Length;
use scale_factor::ScaleFactor;
use num::*;

use num_traits::NumCast;
use std::fmt;
use std::ops::{Add, Div, Mul, Sub};
use std::marker::PhantomData;

/// A 2d size tagged with a unit.
define_matrix! {
    #[derive(RustcDecodable, RustcEncodable)]
    pub struct TypedSize2D<T, U> {
        pub width: T,
        pub height: T,
    }
}

/// Default 2d size type with no unit.
///
/// `Size2D` provides the same methods as `TypedSize2D`.
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;

impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}×{:?}", self.width, self.height)
    }
}

impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "({}x{})", self.width, self.height)
    }
}

impl<T, U> TypedSize2D<T, U> {
    /// Constructor taing scalar values.
    pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
        TypedSize2D {
            width: width,
            height: height,
            _unit: PhantomData,
        }
    }
}

impl<T: Clone, U> TypedSize2D<T, U> {
    /// Constructor taing scalar stronlgy typed lengths.
    pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(width.get(), height.get())
    }
}

impl<T: Round, U> TypedSize2D<T, U> {
    /// Rounds each component to the nearest integer value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn round(&self) -> Self {
        TypedSize2D::new(self.width.round(), self.height.round())
    }
}

impl<T: Ceil, U> TypedSize2D<T, U> {
    /// Rounds each component to the smallest integer equal or greater than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn ceil(&self) -> Self {
        TypedSize2D::new(self.height.ceil(), self.width.ceil())
    }
}

impl<T: Floor, U> TypedSize2D<T, U> {
    /// Rounds each component to the biggest integer equal or lower than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn floor(&self) -> Self {
        TypedSize2D::new(self.width.floor(), self.height.floor())
    }
}

impl<T: Copy + Add<T, Output=T>, U> Add for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    fn add(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width + other.width, self.height + other.height)
    }
}

impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    fn sub(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width - other.width, self.height - other.height)
    }
}

impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
    pub fn area(&self) -> U { self.width * self.height }
}

impl<T: Zero, U> TypedSize2D<T, U> {
    pub fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Zero, U> Zero for TypedSize2D<T, U> {
    fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn mul(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width * scale, self.height * scale)
    }
}

impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn div(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width / scale, self.height / scale)
    }
}

impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
    type Output = TypedSize2D<T, U2>;
    #[inline]
    fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U2> {
        TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
    }
}

impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
    type Output = TypedSize2D<T, U1>;
    #[inline]
    fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U1> {
        TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
    }
}

impl<T: Copy, U> TypedSize2D<T, U> {
    /// Returns self.width as a Length carrying the unit.
    #[inline]
    pub fn width_typed(&self) -> Length<T, U> { Length::new(self.width) }

    /// Returns self.height as a Length carrying the unit.
    #[inline]
    pub fn height_typed(&self) -> Length<T, U> { Length::new(self.height) }

    #[inline]
    pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }

    /// Drop the units, preserving only the numeric value.
    pub fn to_untyped(&self) -> Size2D<T> {
        TypedSize2D::new(self.width, self.height)
    }

    /// Tag a unitless value with units.
    pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
        TypedSize2D::new(p.width, p.height)
    }
}

impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
    /// Cast from one numeric representation to another, preserving the units.
    ///
    /// When casting from floating point to integer coordinates, the decimals are truncated
    /// as one would expect from a simple cast, but this behavior does not always marke sense
    /// geometrically. Consider using round(), ceil or floor() before casting.
    pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
        match (NumCast::from(self.width), NumCast::from(self.height)) {
            (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
            _ => None
        }
    }

    // Convenience functions for common casts

    /// Cast into an f32 size.
    pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an usize size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_uint(&self) -> TypedSize2D<usize, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i32 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i64 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
        self.cast().unwrap()
    }
}

#[cfg(test)]
mod size2d {
    use super::Size2D;

    #[test]
    pub fn test_add() {
        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(3.0, 4.0);
        assert_eq!(p1 + p2, Size2D::new(4.0, 6.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 + p2, Size2D::new(1.0, 2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(-3.0, -4.0);
        assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0));

        let p1 = Size2D::new(0.0, 0.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 + p2, Size2D::new(0.0, 0.0));
    }

    #[test]
    pub fn test_sub() {
        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(3.0, 4.0);
        assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 - p2, Size2D::new(1.0, 2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(-3.0, -4.0);
        assert_eq!(p1 - p2, Size2D::new(4.0, 6.0));

        let p1 = Size2D::new(0.0, 0.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 - p2, Size2D::new(0.0, 0.0));
    }
}
<|endoftext|>


<commit_before>macro_rules! skip_bytes (
  ($input: expr, $length: expr, $offset: expr) => (
    {
      match take!($input, $length) {
        $crate::nom::IResult::Done(i, bytes)   => {
          let head        = bytes[0] << $offset;
          let tail        = &bytes[1..];
          let is_all_zero = tail.iter().all(|byte| *byte == 0);

          if head == 0 && is_all_zero {
            $crate::nom::IResult::Done(i, bytes)
          } else {
            $crate::nom::IResult::Error($crate::nom::Err::Position(
              $crate::nom::ErrorCode::Digit as u32, $input))
          }
        }
        $crate::nom::IResult::Error(error)     =>
          $crate::nom::IResult::Error(error),
        $crate::nom::IResult::Incomplete(need) =>
          $crate::nom::IResult::Incomplete(need),
      }
    }
  );
);

macro_rules! count_slice (
  ($input: expr, $submac: ident!( $($args:tt)* ), $result: expr) => (
    {
      let mut input    = $input;
      let mut count    = 0;
      let mut is_error = false;

      for result in $result {
        match $submac!(input, $($args)*) {
          $crate::nom::IResult::Done(i, o)    => {
            *result = o;

            input   = i;
            count  += 1;
          }
          $crate::nom::IResult::Error(_)      => {
            is_error = true;
            break;
          }
          $crate::nom::IResult::Incomplete(_) => break,
        }
      }

      if is_error {
        $crate::nom::IResult::Error($crate::nom::Err::Position(
          $crate::nom::ErrorCode::Count as u32, $input.0))
      } else if count == $result.len() {
        $crate::nom::IResult::Done(input, ())
      } else {
        $crate::nom::IResult::Incomplete($crate::nom::Needed::Unknown)
      }
    }
  );
  ($i: expr, $f: expr, $count: expr) => (
    count_slice!($i, call!($f), $count);
  );
);

macro_rules! count_bits (
  ($input: expr, $submac: ident!( $($args:tt)* ), $count: expr) => (
    {
      let mut input    = $input;
      let mut count    = 0;
      let mut is_error = false;
      let mut result   = Vec::with_capacity($count);

      loop {
        if count == $count {
          break;
        }

        match $submac!(input, $($args)*) {
          $crate::nom::IResult::Done(i, o)    => {
            result.push(o);

            input  = i;
            count += 1;
          }
          $crate::nom::IResult::Error(_)      => {
            is_error = true;
            break;
          }
          $crate::nom::IResult::Incomplete(_) => break,
        }
      }

      if is_error {
        $crate::nom::IResult::Error($crate::nom::Err::Position(
          $crate::nom::ErrorCode::Count as u32, $input.0))
      } else if count == $count {
        $crate::nom::IResult::Done(input, result)
      } else {
        $crate::nom::IResult::Incomplete($crate::nom::Needed::Unknown)
      }
    }
  );
  ($i: expr, $f: expr, $count: expr) => (
    count_bits!($i, call!($f), $count);
  );
);

macro_rules! take_signed_bits (
  ($input: expr, $signed_type: ty, $count: expr) => (
    map!($input, take_bits!(u32, $count), |value| {
      ::utility::extend_sign(value, $count) as $signed_type
    });
  );
  ($input: expr, $count: expr) => (
    take_signed_bits!($input, i32, $count);
  );
);

#[cfg(test)]
mod tests {
  use super::*;
  use nom::{
    IResult,
    Err, ErrorCode
  };

  #[test]
  fn test_take_signed_bits() {
    let inputs      = [ (&[0b00100000][..], 2)
                      , (&[0b00011111][..], 2)
                      , (&[0b00001000, 0b00000000][..], 4)
                      , (&[0b00000111, 0b11111111][..], 4)
                      , (&[0b10000000, 0b00000000, 0b00000000][..], 0)
                      , (&[0b01111111, 0b11111111, 0b11111111][..], 0)
                      ];
    let results_i8  = [ IResult::Done((&[][..], 0), -32)
                      , IResult::Done((&[][..], 0), 31)
                      ];
    let results_i16 = [ IResult::Done((&[][..], 0), -2048)
                      , IResult::Done((&[][..], 0), 2047)
                      ];
    let results_i32 = [ IResult::Done((&[][..], 0), -8388608)
                      , IResult::Done((&[][..], 0), 8388607)
                      ];

    assert_eq!(take_signed_bits!(inputs[0], i8, 6), results_i8[0]);
    assert_eq!(take_signed_bits!(inputs[1], i8, 6), results_i8[1]);
    assert_eq!(take_signed_bits!(inputs[2], i16, 12), results_i16[0]);
    assert_eq!(take_signed_bits!(inputs[3], i16, 12), results_i16[1]);
    assert_eq!(take_signed_bits!(inputs[4], i32, 24), results_i32[0]);
    assert_eq!(take_signed_bits!(inputs[5], i32, 24), results_i32[1]);
  }
}
<commit_msg>Remove tests for the `take_signed_bits!` macro<commit_after>macro_rules! skip_bytes (
  ($input: expr, $length: expr, $offset: expr) => (
    {
      match take!($input, $length) {
        $crate::nom::IResult::Done(i, bytes)   => {
          let head        = bytes[0] << $offset;
          let tail        = &bytes[1..];
          let is_all_zero = tail.iter().all(|byte| *byte == 0);

          if head == 0 && is_all_zero {
            $crate::nom::IResult::Done(i, bytes)
          } else {
            $crate::nom::IResult::Error($crate::nom::Err::Position(
              $crate::nom::ErrorCode::Digit as u32, $input))
          }
        }
        $crate::nom::IResult::Error(error)     =>
          $crate::nom::IResult::Error(error),
        $crate::nom::IResult::Incomplete(need) =>
          $crate::nom::IResult::Incomplete(need),
      }
    }
  );
);

macro_rules! count_slice (
  ($input: expr, $submac: ident!( $($args:tt)* ), $result: expr) => (
    {
      let mut input    = $input;
      let mut count    = 0;
      let mut is_error = false;

      for result in $result {
        match $submac!(input, $($args)*) {
          $crate::nom::IResult::Done(i, o)    => {
            *result = o;

            input   = i;
            count  += 1;
          }
          $crate::nom::IResult::Error(_)      => {
            is_error = true;
            break;
          }
          $crate::nom::IResult::Incomplete(_) => break,
        }
      }

      if is_error {
        $crate::nom::IResult::Error($crate::nom::Err::Position(
          $crate::nom::ErrorCode::Count as u32, $input.0))
      } else if count == $result.len() {
        $crate::nom::IResult::Done(input, ())
      } else {
        $crate::nom::IResult::Incomplete($crate::nom::Needed::Unknown)
      }
    }
  );
  ($i: expr, $f: expr, $count: expr) => (
    count_slice!($i, call!($f), $count);
  );
);

macro_rules! count_bits (
  ($input: expr, $submac: ident!( $($args:tt)* ), $count: expr) => (
    {
      let mut input    = $input;
      let mut count    = 0;
      let mut is_error = false;
      let mut result   = Vec::with_capacity($count);

      loop {
        if count == $count {
          break;
        }

        match $submac!(input, $($args)*) {
          $crate::nom::IResult::Done(i, o)    => {
            result.push(o);

            input  = i;
            count += 1;
          }
          $crate::nom::IResult::Error(_)      => {
            is_error = true;
            break;
          }
          $crate::nom::IResult::Incomplete(_) => break,
        }
      }

      if is_error {
        $crate::nom::IResult::Error($crate::nom::Err::Position(
          $crate::nom::ErrorCode::Count as u32, $input.0))
      } else if count == $count {
        $crate::nom::IResult::Done(input, result)
      } else {
        $crate::nom::IResult::Incomplete($crate::nom::Needed::Unknown)
      }
    }
  );
  ($i: expr, $f: expr, $count: expr) => (
    count_bits!($i, call!($f), $count);
  );
);

macro_rules! take_signed_bits (
  ($input: expr, $signed_type: ty, $count: expr) => (
    map!($input, take_bits!(u32, $count), |value| {
      ::utility::extend_sign(value, $count) as $signed_type
    });
  );
  ($input: expr, $count: expr) => (
    take_signed_bits!($input, i32, $count);
  );
);
<|endoftext|>