<commit_before>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 aho_corasick::AcAutomaton;
use memchr::memchr;

/// A prefix extracted from a compiled regular expression.
///
/// A regex prefix is a set of literal strings that *must* be matched at the
/// beginning of a regex in order for the entire regex to match.
///
/// There are a variety of ways to efficiently scan the search text for a
/// prefix. Currently, there are two implemented:
///
/// 1. The prefix is a single byte. Just use memchr.
/// 2. If the prefix is a set of two or more single byte prefixes, then
///    a single sparse map is created. Checking if there is a match is a lookup
///    in this map for each byte in the search text.
/// 3. In all other cases, build an Aho-Corasick automaton.
///
/// It's possible that there's room here for other substring algorithms,
/// such as Boyer-Moore for single-set prefixes greater than 1, or Rabin-Karp
/// for small sets of same-length prefixes.
#[derive(Clone, Debug)]
pub enum Prefix {
    /// No prefixes. (Never advances through the input.)
    Empty,
    /// A single byte prefix.
    Single(u8),
    /// A set of two or more single byte prefixes.
    /// This could be reduced to a bitset, which would use only 8 bytes,
    /// but I don't think we care.
    Singles(Vec<bool>),
    /// A full Aho-Corasick DFA automaton.
    Automaton(AcAutomaton),
}

impl Prefix {
    /// Create a new prefix matching machine.
    pub fn new(pfxs: Vec<String>) -> Prefix {
        if pfxs.len() == 0 || pfxs[0].len() == 0 {
            Prefix::Empty
        } else if pfxs.len() == 1 && pfxs[0].len() == 1 {
            Prefix::Single(pfxs[0].as_bytes()[0])
        } else if pfxs.len() >= 2 && pfxs.iter().all(|s| s.len() == 1) {
            let mut set = vec![false; 256];
            for p in pfxs {
                set[p.as_bytes()[0] as usize] = true;
            }
            Prefix::Singles(set)
        } else {
            Prefix::Automaton(AcAutomaton::new(pfxs))
        }
    }

    /// Find the position of a prefix in `haystack` if it exists.
    ///
    /// In the matching engines, we only actually need the starting index
    /// because the prefix is used to only skip ahead---the matching engine
    /// still needs to run over the prefix input. However, we return the ending
    /// location as well in case the prefix corresponds to the entire regex,
    /// in which case, you need the end of the match.
    pub fn find(&self, haystack: &str) -> Option<(usize, usize)> {
        use self::Prefix::*;
        match *self {
            Empty => Some((0, 0)),
            Single(b) => memchr(b, haystack.as_bytes()).map(|i| (i, i+1)),
            Singles(ref pats) => find_singles(pats, haystack.as_bytes()),
            Automaton(ref aut) => {
                aut.find(haystack).next().map(|m| (m.start, m.end))
            }
        }
    }

    /// Returns true iff this prefix is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of prefixes in this machine.
    pub fn len(&self) -> usize {
        match *self {
            Prefix::Empty => 0,
            Prefix::Single(_) => 1,
            Prefix::Singles(ref pats) => pats.len(),
            Prefix::Automaton(ref aut) => aut.len(),
        }
    }
}

/// A very quick scan for multiple single byte prefixes using a sparse map.
fn find_singles(pats: &[bool], haystack: &[u8]) -> Option<(usize, usize)> {
    for (hi, &b) in haystack.iter().enumerate() {
        if pats[b as usize] {
            return Some((hi, hi+1));
        }
    }
    None
}
<commit_msg>Use an always-sparse Aho-Corasick automaton.<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 aho_corasick::{AcAutomaton, Sparse};
use memchr::memchr;

/// A prefix extracted from a compiled regular expression.
///
/// A regex prefix is a set of literal strings that *must* be matched at the
/// beginning of a regex in order for the entire regex to match.
///
/// There are a variety of ways to efficiently scan the search text for a
/// prefix. Currently, there are two implemented:
///
/// 1. The prefix is a single byte. Just use memchr.
/// 2. If the prefix is a set of two or more single byte prefixes, then
///    a single sparse map is created. Checking if there is a match is a lookup
///    in this map for each byte in the search text.
/// 3. In all other cases, build an Aho-Corasick automaton.
///
/// It's possible that there's room here for other substring algorithms,
/// such as Boyer-Moore for single-set prefixes greater than 1, or Rabin-Karp
/// for small sets of same-length prefixes.
#[derive(Clone, Debug)]
pub enum Prefix {
    /// No prefixes. (Never advances through the input.)
    Empty,
    /// A single byte prefix.
    Single(u8),
    /// A set of two or more single byte prefixes.
    /// This could be reduced to a bitset, which would use only 8 bytes,
    /// but I don't think we care.
    Singles(Vec<bool>),
    /// A full Aho-Corasick DFA automaton.
    Automaton(AcAutomaton<Sparse>),
}

impl Prefix {
    /// Create a new prefix matching machine.
    pub fn new(pfxs: Vec<String>) -> Prefix {
        if pfxs.len() == 0 || pfxs[0].len() == 0 {
            Prefix::Empty
        } else if pfxs.len() == 1 && pfxs[0].len() == 1 {
            Prefix::Single(pfxs[0].as_bytes()[0])
        } else if pfxs.len() >= 2 && pfxs.iter().all(|s| s.len() == 1) {
            let mut set = vec![false; 256];
            for p in pfxs {
                set[p.as_bytes()[0] as usize] = true;
            }
            Prefix::Singles(set)
        } else {
            Prefix::Automaton(AcAutomaton::with_transitions(pfxs))
        }
    }

    /// Find the position of a prefix in `haystack` if it exists.
    ///
    /// In the matching engines, we only actually need the starting index
    /// because the prefix is used to only skip ahead---the matching engine
    /// still needs to run over the prefix input. However, we return the ending
    /// location as well in case the prefix corresponds to the entire regex,
    /// in which case, you need the end of the match.
    pub fn find(&self, haystack: &str) -> Option<(usize, usize)> {
        use self::Prefix::*;
        match *self {
            Empty => Some((0, 0)),
            Single(b) => memchr(b, haystack.as_bytes()).map(|i| (i, i+1)),
            Singles(ref pats) => find_singles(pats, haystack.as_bytes()),
            Automaton(ref aut) => {
                aut.find(haystack).next().map(|m| (m.start, m.end))
            }
        }
    }

    /// Returns true iff this prefix is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of prefixes in this machine.
    pub fn len(&self) -> usize {
        match *self {
            Prefix::Empty => 0,
            Prefix::Single(_) => 1,
            Prefix::Singles(ref pats) => pats.len(),
            Prefix::Automaton(ref aut) => aut.len(),
        }
    }
}

/// A very quick scan for multiple single byte prefixes using a sparse map.
fn find_singles(pats: &[bool], haystack: &[u8]) -> Option<(usize, usize)> {
    for (hi, &b) in haystack.iter().enumerate() {
        if pats[b as usize] {
            return Some((hi, hi+1));
        }
    }
    None
}
<|endoftext|>


<commit_before>mod component;
mod state;

use self::state::{InputState, Reactive, UIEvents, UIState};
use crate::key::Key;
use crate::Args;
use crate::{file::SerializedFile, remote::RemoteLogin, try_opt_r, user::Users};
use anime::remote::ScoreParser;
use anyhow::{anyhow, Context, Result};
use component::prompt::command::Command;
use component::prompt::command::InputResult;
use component::prompt::COMMAND_KEY;
use component::series_list::SeriesList;
use component::Component;
use component::{main_panel::MainPanel, prompt::command::CommandPrompt};
use crossterm::{event::KeyCode, terminal};
use state::{SharedState, UIErrorKind, UIEvent};
use std::{io, sync::Arc};
use tokio::sync::Notify;
use tui::{backend::CrosstermBackend, layout::Direction, Terminal};
use tui_utils::layout::{BasicConstraint, SimpleLayout};

pub async fn run(args: &Args) -> Result<()> {
    let mut ui = UI::init(&args).context("failed to init UI")?;
    let result = ui.run().await;

    ui.exit()?;
    result
}

type CrosstermTerminal = Terminal<CrosstermBackend<io::Stdout>>;

struct UI {
    events: UIEvents,
    terminal: CrosstermTerminal,
    state: SharedState,
    dirty_state_notify: Arc<Notify>,
    panels: Panels,
}

impl UI {
    fn init(args: &Args) -> Result<UI> {
        let events = UIEvents::new().context("UI events init")?;

        let mut state = UIState::init().context("UI state init")?;

        state
            .select_initial_series(args)
            .context("selecting initial series")?;

        let dirty_state_notify = Arc::new(Notify::const_new());
        let shared_state = SharedState::new(Reactive::new(state, Arc::clone(&dirty_state_notify)));

        let panels = Panels::init(&shared_state);

        if !args.offline {
            if let Some((user, token)) = Users::load_or_create()?.take_last_used_user() {
                shared_state.login_to_remote_async(RemoteLogin::AniList(user.username, token));
            }
        }

        let terminal = Self::init_terminal().context("initializing terminal")?;

        Ok(Self {
            events,
            terminal,
            state: shared_state,
            dirty_state_notify,
            panels,
        })
    }

    fn init_terminal() -> Result<CrosstermTerminal> {
        terminal::enable_raw_mode().context("failed to enable raw mode")?;

        let stdout = io::stdout();
        let backend = CrosstermBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context("terminal creation failed")?;

        terminal.clear().context("failed to clear terminal")?;

        terminal
            .hide_cursor()
            .context("failed to hide mouse cursor")?;

        Ok(terminal)
    }

    async fn run(&mut self) -> Result<()> {
        {
            let mut state = self.state.lock();

            if let Err(err) = self.panels.draw(state.get_mut(), &mut self.terminal) {
                return Err(err);
            }
        }

        loop {
            match self.next_cycle().await {
                CycleResult::Ok => (),
                CycleResult::Exit => break Ok(()),
                CycleResult::Error(err) => return Err(err),
            }
        }
    }

    async fn next_cycle(&mut self) -> CycleResult {
        let event = match self.events.next(&self.dirty_state_notify).await {
            Ok(Some(event)) => event,
            Ok(None) => return CycleResult::Ok,
            Err(UIErrorKind::ExitRequest) => return CycleResult::Exit,
            Err(UIErrorKind::Other(err)) => return CycleResult::Error(err),
        };

        let mut state = self.state.lock();
        let state = state.get_mut();

        let result = match event {
            UIEvent::Key(key) => self.panels.process_key(key, state).await,
            UIEvent::StateChange | UIEvent::Resize => CycleResult::Ok,
        };

        if let Err(err) = self.panels.draw(state, &mut self.terminal) {
            return CycleResult::Error(err);
        }

        result
    }

    pub fn exit(mut self) -> Result<()> {
        self.terminal.clear().ok();
        terminal::disable_raw_mode().map_err(Into::into)
    }
}

pub enum CycleResult {
    Ok,
    Exit,
    Error(anyhow::Error),
}

struct Panels {
    command_prompt: CommandPrompt,
    main_panel: MainPanel,
    state: SharedState,
}

impl Panels {
    fn init(state: &SharedState) -> Self {
        Self {
            command_prompt: CommandPrompt::new(),
            main_panel: MainPanel::new(state.clone()),
            state: state.clone(),
        }
    }

    async fn process_key(&mut self, key: Key, state: &mut UIState) -> CycleResult {
        macro_rules! capture {
            ($result:expr) => {
                match $result {
                    Ok(value) => value,
                    Err(err) => {
                        state.log.push_error(&err);
                        return CycleResult::Ok;
                    }
                }
            };
        }

        macro_rules! process_key {
            ($component:ident) => {
                capture!(self.$component.process_key(key, state))
            };
        }

        match state.input_state {
            InputState::Idle => match *key {
                KeyCode::Char('q') => return CycleResult::Exit,
                _ if key == state.config.tui.keys.play_next_episode => {
                    capture!(state.play_next_series_episode(&self.state).await)
                }
                KeyCode::Char('a') => {
                    capture!(self.main_panel.switch_to_add_series(state))
                }
                KeyCode::Char('e') => {
                    capture!(self.main_panel.switch_to_update_series(state))
                }
                KeyCode::Char('D') => {
                    capture!(self.main_panel.switch_to_delete_series(state))
                }
                KeyCode::Char('u') => self.main_panel.switch_to_user_panel(state),
                KeyCode::Char('s') => {
                    capture!(self.main_panel.switch_to_split_series(state))
                }
                KeyCode::Char(COMMAND_KEY) => state.input_state = InputState::EnteringCommand,
                _ => SeriesList::process_key(key, state),
            },
            InputState::Locked => (),
            InputState::FocusedOnMainPanel => process_key!(main_panel),
            InputState::EnteringCommand => {
                let result = self.command_prompt.process_key(key, state);

                if !matches!(result, Ok(InputResult::Continue)) {
                    self.command_prompt.reset();
                    state.input_state.reset();
                }

                match capture!(result) {
                    InputResult::Command(cmd) => {
                        capture!(Self::process_command(cmd, state))
                    }
                    InputResult::Done | InputResult::Continue => (),
                }
            }
        }

        CycleResult::Ok
    }

    fn draw(&mut self, state: &UIState, terminal: &mut CrosstermTerminal) -> Result<()> {
        terminal
            .draw(|mut frame| {
                let horiz_splitter = SimpleLayout::new(Direction::Horizontal).split(
                    frame.size(),
                    &[
                        BasicConstraint::MinLenGrowthPcnt(20, 30),
                        BasicConstraint::Percentage(70),
                    ],
                );

                SeriesList::draw(state, horiz_splitter[0], &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = SimpleLayout::new(Direction::Vertical).split(
                    horiz_splitter[1],
                    &[
                        BasicConstraint::Percentage(80),
                        BasicConstraint::Percentage(20),
                    ],
                );

                self.main_panel
                    .draw(state, info_panel_splitter[0], &mut frame);

                match state.input_state {
                    InputState::EnteringCommand => {
                        self.command_prompt.draw(info_panel_splitter[1], frame)
                    }
                    _ => state.log.draw(info_panel_splitter[1], frame),
                }
            })
            .map_err(Into::into)
    }

    fn process_command(command: Command, state: &mut UIState) -> Result<()> {
        let remote = &mut state.remote;
        let config = &state.config;
        let db = &state.db;

        match command {
            Command::PlayerArgs(args) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());

                series.data.config.player_args = args.into();
                series.save(db)?;
                Ok(())
            }
            Command::Progress(direction) => {
                use component::prompt::command::ProgressDirection;

                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                match direction {
                    ProgressDirection::Forwards => series.episode_completed(remote, config, db),
                    ProgressDirection::Backwards => series.episode_regressed(remote, config, db),
                }
            }
            cmd @ Command::SyncFromRemote | cmd @ Command::SyncToRemote => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                match cmd {
                    Command::SyncFromRemote => series.data.force_sync_from_remote(remote)?,
                    Command::SyncToRemote => series.data.entry.force_sync_to_remote(remote)?,
                    _ => unreachable!(),
                }

                series.save(db)?;
                Ok(())
            }
            Command::Score(raw_score) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                let score = match remote.parse_score(&raw_score) {
                    Some(score) if score == 0 => None,
                    Some(score) => Some(score),
                    None => return Err(anyhow!("invalid score")),
                };

                series.data.entry.set_score(score.map(i16::from));
                series.data.entry.sync_to_remote(remote)?;
                series.save(db)?;

                Ok(())
            }
            Command::Status(status) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                series.data.entry.set_status(status, config);
                series.data.entry.sync_to_remote(remote)?;
                series.save(db)?;

                Ok(())
            }
        }
    }
}
<commit_msg>anup: Add new CrosstermTerminal struct to catch errors during terminal init & clean up on failure<commit_after>mod component;
mod state;

use self::state::{InputState, Reactive, UIEvents, UIState};
use crate::key::Key;
use crate::Args;
use crate::{file::SerializedFile, remote::RemoteLogin, try_opt_r, user::Users};
use anime::remote::ScoreParser;
use anyhow::{anyhow, Context, Result};
use component::prompt::command::Command;
use component::prompt::command::InputResult;
use component::prompt::COMMAND_KEY;
use component::series_list::SeriesList;
use component::Component;
use component::{main_panel::MainPanel, prompt::command::CommandPrompt};
use crossterm::{event::KeyCode, terminal};
use state::{SharedState, UIErrorKind, UIEvent};
use std::{
    io,
    ops::{Deref, DerefMut},
    sync::Arc,
};
use tokio::sync::Notify;
use tui::{backend::CrosstermBackend, layout::Direction, Terminal};
use tui_utils::layout::{BasicConstraint, SimpleLayout};

pub async fn run(args: &Args) -> Result<()> {
    let mut ui = UI::init(&args).context("failed to init UI")?;
    let result = ui.run().await;

    ui.exit()?;
    result
}

struct UI {
    events: UIEvents,
    terminal: CrosstermTerminal,
    state: SharedState,
    dirty_state_notify: Arc<Notify>,
    panels: Panels,
}

impl UI {
    fn init(args: &Args) -> Result<UI> {
        let events = UIEvents::new().context("UI events init")?;

        let mut state = UIState::init().context("UI state init")?;

        state
            .select_initial_series(args)
            .context("selecting initial series")?;

        let dirty_state_notify = Arc::new(Notify::const_new());
        let shared_state = SharedState::new(Reactive::new(state, Arc::clone(&dirty_state_notify)));

        let panels = Panels::init(&shared_state);

        if !args.offline {
            if let Some((user, token)) = Users::load_or_create()?.take_last_used_user() {
                shared_state.login_to_remote_async(RemoteLogin::AniList(user.username, token));
            }
        }

        let terminal = CrosstermTerminal::safe_init().context("initializing terminal")?;

        Ok(Self {
            events,
            terminal,
            state: shared_state,
            dirty_state_notify,
            panels,
        })
    }

    async fn run(&mut self) -> Result<()> {
        {
            let mut state = self.state.lock();

            if let Err(err) = self.panels.draw(state.get_mut(), &mut self.terminal) {
                return Err(err);
            }
        }

        loop {
            match self.next_cycle().await {
                CycleResult::Ok => (),
                CycleResult::Exit => break Ok(()),
                CycleResult::Error(err) => return Err(err),
            }
        }
    }

    async fn next_cycle(&mut self) -> CycleResult {
        let event = match self.events.next(&self.dirty_state_notify).await {
            Ok(Some(event)) => event,
            Ok(None) => return CycleResult::Ok,
            Err(UIErrorKind::ExitRequest) => return CycleResult::Exit,
            Err(UIErrorKind::Other(err)) => return CycleResult::Error(err),
        };

        let mut state = self.state.lock();
        let state = state.get_mut();

        let result = match event {
            UIEvent::Key(key) => self.panels.process_key(key, state).await,
            UIEvent::StateChange | UIEvent::Resize => CycleResult::Ok,
        };

        if let Err(err) = self.panels.draw(state, &mut self.terminal) {
            return CycleResult::Error(err);
        }

        result
    }

    pub fn exit(mut self) -> Result<()> {
        self.terminal.clear().ok();
        terminal::disable_raw_mode().map_err(Into::into)
    }
}

pub enum CycleResult {
    Ok,
    Exit,
    Error(anyhow::Error),
}

struct Panels {
    command_prompt: CommandPrompt,
    main_panel: MainPanel,
    state: SharedState,
}

impl Panels {
    fn init(state: &SharedState) -> Self {
        Self {
            command_prompt: CommandPrompt::new(),
            main_panel: MainPanel::new(state.clone()),
            state: state.clone(),
        }
    }

    async fn process_key(&mut self, key: Key, state: &mut UIState) -> CycleResult {
        macro_rules! capture {
            ($result:expr) => {
                match $result {
                    Ok(value) => value,
                    Err(err) => {
                        state.log.push_error(&err);
                        return CycleResult::Ok;
                    }
                }
            };
        }

        macro_rules! process_key {
            ($component:ident) => {
                capture!(self.$component.process_key(key, state))
            };
        }

        match state.input_state {
            InputState::Idle => match *key {
                KeyCode::Char('q') => return CycleResult::Exit,
                _ if key == state.config.tui.keys.play_next_episode => {
                    capture!(state.play_next_series_episode(&self.state).await)
                }
                KeyCode::Char('a') => {
                    capture!(self.main_panel.switch_to_add_series(state))
                }
                KeyCode::Char('e') => {
                    capture!(self.main_panel.switch_to_update_series(state))
                }
                KeyCode::Char('D') => {
                    capture!(self.main_panel.switch_to_delete_series(state))
                }
                KeyCode::Char('u') => self.main_panel.switch_to_user_panel(state),
                KeyCode::Char('s') => {
                    capture!(self.main_panel.switch_to_split_series(state))
                }
                KeyCode::Char(COMMAND_KEY) => state.input_state = InputState::EnteringCommand,
                _ => SeriesList::process_key(key, state),
            },
            InputState::Locked => (),
            InputState::FocusedOnMainPanel => process_key!(main_panel),
            InputState::EnteringCommand => {
                let result = self.command_prompt.process_key(key, state);

                if !matches!(result, Ok(InputResult::Continue)) {
                    self.command_prompt.reset();
                    state.input_state.reset();
                }

                match capture!(result) {
                    InputResult::Command(cmd) => {
                        capture!(Self::process_command(cmd, state))
                    }
                    InputResult::Done | InputResult::Continue => (),
                }
            }
        }

        CycleResult::Ok
    }

    fn draw(&mut self, state: &UIState, terminal: &mut CrosstermTerminal) -> Result<()> {
        terminal
            .draw(|mut frame| {
                let horiz_splitter = SimpleLayout::new(Direction::Horizontal).split(
                    frame.size(),
                    &[
                        BasicConstraint::MinLenGrowthPcnt(20, 30),
                        BasicConstraint::Percentage(70),
                    ],
                );

                SeriesList::draw(state, horiz_splitter[0], &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = SimpleLayout::new(Direction::Vertical).split(
                    horiz_splitter[1],
                    &[
                        BasicConstraint::Percentage(80),
                        BasicConstraint::Percentage(20),
                    ],
                );

                self.main_panel
                    .draw(state, info_panel_splitter[0], &mut frame);

                match state.input_state {
                    InputState::EnteringCommand => {
                        self.command_prompt.draw(info_panel_splitter[1], frame)
                    }
                    _ => state.log.draw(info_panel_splitter[1], frame),
                }
            })
            .map_err(Into::into)
    }

    fn process_command(command: Command, state: &mut UIState) -> Result<()> {
        let remote = &mut state.remote;
        let config = &state.config;
        let db = &state.db;

        match command {
            Command::PlayerArgs(args) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());

                series.data.config.player_args = args.into();
                series.save(db)?;
                Ok(())
            }
            Command::Progress(direction) => {
                use component::prompt::command::ProgressDirection;

                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                match direction {
                    ProgressDirection::Forwards => series.episode_completed(remote, config, db),
                    ProgressDirection::Backwards => series.episode_regressed(remote, config, db),
                }
            }
            cmd @ Command::SyncFromRemote | cmd @ Command::SyncToRemote => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                match cmd {
                    Command::SyncFromRemote => series.data.force_sync_from_remote(remote)?,
                    Command::SyncToRemote => series.data.entry.force_sync_to_remote(remote)?,
                    _ => unreachable!(),
                }

                series.save(db)?;
                Ok(())
            }
            Command::Score(raw_score) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                let score = match remote.parse_score(&raw_score) {
                    Some(score) if score == 0 => None,
                    Some(score) => Some(score),
                    None => return Err(anyhow!("invalid score")),
                };

                series.data.entry.set_score(score.map(i16::from));
                series.data.entry.sync_to_remote(remote)?;
                series.save(db)?;

                Ok(())
            }
            Command::Status(status) => {
                let series = try_opt_r!(state.series.get_valid_sel_series_mut());
                let remote = remote.get_logged_in()?;

                series.data.entry.set_status(status, config);
                series.data.entry.sync_to_remote(remote)?;
                series.save(db)?;

                Ok(())
            }
        }
    }
}

struct CrosstermTerminal(Terminal<CrosstermBackend<io::Stdout>>);

impl CrosstermTerminal {
    /// Initialize a new Crossterm terminal.
    ///
    /// This function should always be used instead of [`Self::unsafe_init`], as it will clean up the terminal should an error occur.
    fn safe_init() -> Result<Self> {
        match Self::unsafe_init() {
            result @ Ok(_) => result,
            result @ Err(_) => {
                terminal::disable_raw_mode().ok();
                result
            }
        }
    }

    /// Initialize a new Crossterm terminal.
    ///
    /// This function enables the terminal's raw mode. If this function returns an error, then the user's terminal may behave oddly
    /// unless `terminal::disable_raw_mode` is called. [`Self::safe_init`] should be used instead as it will disable raw mode automatically.
    fn unsafe_init() -> Result<Self> {
        terminal::enable_raw_mode().context("failed to enable raw mode")?;

        let stdout = io::stdout();
        let backend = CrosstermBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context("terminal creation failed")?;

        terminal.clear().context("failed to clear terminal")?;

        terminal
            .hide_cursor()
            .context("failed to hide mouse cursor")?;

        Ok(Self(terminal))
    }
}

impl Deref for CrosstermTerminal {
    type Target = Terminal<CrosstermBackend<io::Stdout>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for CrosstermTerminal {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::{any_pointer};
use capnp::Error;
use capnp::capability::Promise;
use capnp::private::capability::{ClientHook, ParamsHook, PipelineHook, PipelineOp,
                                 ResultsHook, ResultsDoneHook};

use futures::Future;

use std::cell::RefCell;
use std::rc::{Rc};

use {broken, local, ForkedPromise};

struct PipelineInner {
    promise: ForkedPromise<Promise<Box<PipelineHook>, Error>>,

    // Once the promise resolves, this will become non-null and point to the underlying object.
    redirect: Option<Box<PipelineHook>>,

    // Represents the operation which will set `redirect` when possible.
    self_resolution_op: Promise<(), Error>,
}

pub struct Pipeline {
    inner: Rc<RefCell<PipelineInner>>,
}

impl Pipeline {
    pub fn new(promise_param: Promise<Box<PipelineHook>, Error>) -> Pipeline {
        let promise = ForkedPromise::new_queued(promise_param);
        let branch = promise.add_branch();
        let inner = Rc::new(RefCell::new(PipelineInner {
            promise: promise,
            redirect: None,
            self_resolution_op: Promise::ok(()),
        }));
        let this = Rc::downgrade(&inner);
        let self_res = ::eagerly_evaluate(branch.then(move |result| {

            let this = match this.upgrade(){
                Some(v) => v,
                None => return Err(Error::failed("dangling self reference in queued::Pipeline".into())),
            };

            match result {
                Ok(pipeline_hook) => {
                    this.borrow_mut().redirect = Some(pipeline_hook);
                }
                Err(e) => {
                    this.borrow_mut().redirect = Some(Box::new(broken::Pipeline::new(e)));
                }
            }
            Ok(())
        }));
        inner.borrow_mut().self_resolution_op = self_res;
        Pipeline { inner: inner }
    }
}

impl Clone for Pipeline {
    fn clone(&self) -> Pipeline {
        Pipeline { inner: self.inner.clone() }
    }
}

impl PipelineHook for Pipeline {
    fn add_ref(&self) -> Box<PipelineHook> {
        Box::new(self.clone())
    }
    fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Box<ClientHook> {
        self.get_pipelined_cap_move(ops.into())
    }

    fn get_pipelined_cap_move(&self, ops: Vec<PipelineOp>) -> Box<ClientHook> {
        match &self.inner.borrow().redirect {
            &Some(ref p) => {
                return p.get_pipelined_cap_move(ops)
            }
            &None => (),
        }
        let client_promise = self.inner.borrow_mut().promise.clone().map(move |pipeline| {
            pipeline.get_pipelined_cap_move(ops)
        });

        Box::new(Client::new(Promise::from_future(client_promise)))
    }
}

type ClientHookPromiseFork = ForkedPromise<Promise<Box<ClientHook>, Error>>;

struct ClientInner {
    // Once the promise resolves, this will become non-null and point to the underlying object.
    redirect: Option<Box<ClientHook>>,

    // Promise that resolves when we have a new ClientHook to forward to.
    //
    // This fork shall only have three branches:  `selfResolutionOp`, `promiseForCallForwarding`, and
    // `promiseForClientResolution`, in that order.
    _promise: ClientHookPromiseFork,

    // Represents the operation which will set `redirect` when possible.
    self_resolution_op: Promise<(), Error>,

    // When this promise resolves, each queued call will be forwarded to the real client.  This needs
    // to occur *before* any 'whenMoreResolved()' promises resolve, because we want to make sure
    // previously-queued calls are delivered before any new calls made in response to the resolution.
    promise_for_call_forwarding: ClientHookPromiseFork,

    // whenMoreResolved() returns forks of this promise.  These must resolve *after* queued calls
    // have been initiated (so that any calls made in the whenMoreResolved() handler are correctly
    // delivered after calls made earlier), but *before* any queued calls return (because it might
    // confuse the application if a queued call returns before the capability on which it was made
    // resolves).  Luckily, we know that queued calls will involve, at the very least, an
    // eventLoop.evalLater.
    promise_for_client_resolution: ClientHookPromiseFork,
}

pub struct Client {
    inner: Rc<RefCell<ClientInner>>,
}

impl Client {
    pub fn new(promise_param: Promise<Box<ClientHook>, Error>)
           -> Client
    {
        let promise = ForkedPromise::new_queued(promise_param);
        let branch1 = promise.add_branch();
        let branch2 = promise.add_branch();
        let branch3 = promise.add_branch();
        let inner = Rc::new(RefCell::new(ClientInner {
            redirect: None,
            _promise: promise,
            self_resolution_op: Promise::from_future(::futures::future::empty()),
            promise_for_call_forwarding: branch2,
            promise_for_client_resolution: branch3,
        }));
        let this = Rc::downgrade(&inner);
        let self_resolution_op = ::eagerly_evaluate(branch1.then(move |result| {
            let state = match this.upgrade() {
                Some(s) => s,
                None => return Err(Error::failed("dangling reference to QueuedClient".into())),
            };
            match result {
                Ok(clienthook) => {
                    state.borrow_mut().redirect = Some(clienthook);
                }
                Err(e) => {
                    state.borrow_mut().redirect = Some(broken::new_cap(e));
                }
            }
            Ok(())
        }));
        inner.borrow_mut().self_resolution_op = self_resolution_op;
        Client {
            inner: inner
        }
    }
}

impl ClientHook for Client {
    fn add_ref(&self) -> Box<ClientHook> {
        Box::new(Client {inner: self.inner.clone()})
    }
    fn new_call(&self, interface_id: u64, method_id: u16,
                size_hint: Option<::capnp::MessageSize>)
                -> ::capnp::capability::Request<any_pointer::Owned, any_pointer::Owned>
    {
        ::capnp::capability::Request::new(
            Box::new(local::Request::new(interface_id, method_id, size_hint, self.add_ref())))
    }

    fn call(&self, interface_id: u64, method_id: u16, params: Box<ParamsHook>, results: Box<ResultsHook>,
            results_done: Promise<Box<ResultsDoneHook>, Error>)
        -> (Promise<(), Error>, Box<PipelineHook>)
    {

        let promise_for_pair = self.inner.borrow_mut().promise_for_call_forwarding.clone().map(move |client| {
            client.call(interface_id, method_id, params, results, results_done)
        });

        let (promise_promise, pipeline_promise) = ::split::split(promise_for_pair);
        let pipeline = Pipeline::new(Promise::from_future(pipeline_promise));
        (Promise::from_future(promise_promise.flatten()), Box::new(pipeline))
    }

    fn get_ptr(&self) -> usize {
        (&*self.inner.borrow()) as * const _ as usize
    }

    fn get_brand(&self) -> usize {
        0
    }

    fn get_resolved(&self) -> Option<Box<ClientHook>> {
        match self.inner.borrow().redirect {
            Some(ref inner) => Some(inner.clone()),
            None => None,
        }
    }

    fn when_more_resolved(&self) -> Option<Promise<Box<ClientHook>, Error>> {
        Some(Promise::from_future(self.inner.borrow_mut().promise_for_client_resolution.clone()))
    }
}
<commit_msg>step one<commit_after>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::{any_pointer};
use capnp::Error;
use capnp::capability::Promise;
use capnp::private::capability::{ClientHook, ParamsHook, PipelineHook, PipelineOp,
                                 ResultsHook, ResultsDoneHook};

use futures::Future;

use std::cell::RefCell;
use std::rc::{Rc};

use {broken, local, ForkedPromise};

struct PipelineInner {
    promise: ForkedPromise<Promise<Box<PipelineHook>, Error>>,

    // Once the promise resolves, this will become non-null and point to the underlying object.
    redirect: Option<Box<PipelineHook>>,

    // Represents the operation which will set `redirect` when possible.
    self_resolution_op: Promise<(), Error>,
}

pub struct Pipeline {
    inner: Rc<RefCell<PipelineInner>>,
}

impl Pipeline {
    pub fn new(promise_param: Promise<Box<PipelineHook>, Error>) -> Pipeline {
        let promise = ForkedPromise::new_queued(promise_param);
        let branch = promise.add_branch();
        let inner = Rc::new(RefCell::new(PipelineInner {
            promise: promise,
            redirect: None,
            self_resolution_op: Promise::ok(()),
        }));
        let this = Rc::downgrade(&inner);
        let self_res = ::eagerly_evaluate(branch.then(move |result| {

            let this = match this.upgrade(){
                Some(v) => v,
                None => return Err(Error::failed("dangling self reference in queued::Pipeline".into())),
            };

            match result {
                Ok(pipeline_hook) => {
                    this.borrow_mut().redirect = Some(pipeline_hook);
                }
                Err(e) => {
                    this.borrow_mut().redirect = Some(Box::new(broken::Pipeline::new(e)));
                }
            }
            Ok(())
        }));
        inner.borrow_mut().self_resolution_op = self_res;
        Pipeline { inner: inner }
    }
}

impl Clone for Pipeline {
    fn clone(&self) -> Pipeline {
        Pipeline { inner: self.inner.clone() }
    }
}

impl PipelineHook for Pipeline {
    fn add_ref(&self) -> Box<PipelineHook> {
        Box::new(self.clone())
    }
    fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Box<ClientHook> {
        self.get_pipelined_cap_move(ops.into())
    }

    fn get_pipelined_cap_move(&self, ops: Vec<PipelineOp>) -> Box<ClientHook> {
        match &self.inner.borrow().redirect {
            &Some(ref p) => {
                return p.get_pipelined_cap_move(ops)
            }
            &None => (),
        }
        let client_promise = self.inner.borrow_mut().promise.clone().map(move |pipeline| {
            pipeline.get_pipelined_cap_move(ops)
        });

        Box::new(Client::new(Promise::from_future(client_promise)))
    }
}

type ClientHookPromiseFork = ForkedPromise<Promise<Box<ClientHook>, Error>>;

pub struct ClientInner {
    // Once the promise resolves, this will become non-null and point to the underlying object.
    redirect: Option<Box<ClientHook>>,

    // Promise that resolves when we have a new ClientHook to forward to.
    //
    // This fork shall only have three branches:  `selfResolutionOp`, `promiseForCallForwarding`, and
    // `promiseForClientResolution`, in that order.
    _promise: ClientHookPromiseFork,

    // Represents the operation which will set `redirect` when possible.
    self_resolution_op: Promise<(), Error>,

    // When this promise resolves, each queued call will be forwarded to the real client.  This needs
    // to occur *before* any 'whenMoreResolved()' promises resolve, because we want to make sure
    // previously-queued calls are delivered before any new calls made in response to the resolution.
    promise_for_call_forwarding: ClientHookPromiseFork,

    // whenMoreResolved() returns forks of this promise.  These must resolve *after* queued calls
    // have been initiated (so that any calls made in the whenMoreResolved() handler are correctly
    // delivered after calls made earlier), but *before* any queued calls return (because it might
    // confuse the application if a queued call returns before the capability on which it was made
    // resolves).  Luckily, we know that queued calls will involve, at the very least, an
    // eventLoop.evalLater.
    promise_for_client_resolution: ClientHookPromiseFork,
}

impl ClientInner {
    pub fn resolve(state: &Rc<RefCell<ClientInner>>, result: Result<Box<ClientHook>, Error>) {
        match result {
            Ok(clienthook) => {
                state.borrow_mut().redirect = Some(clienthook);
            }
            Err(e) => {
                state.borrow_mut().redirect = Some(broken::new_cap(e));
            }
        }
    }
}

pub struct Client {
    inner: Rc<RefCell<ClientInner>>,
}

impl Client {
    pub fn new(promise_param: Promise<Box<ClientHook>, Error>)
           -> Client
    {
        let promise = ForkedPromise::new_queued(promise_param);
        let branch1 = promise.add_branch();
        let branch2 = promise.add_branch();
        let branch3 = promise.add_branch();
        let inner = Rc::new(RefCell::new(ClientInner {
            redirect: None,
            _promise: promise,
            self_resolution_op: Promise::from_future(::futures::future::empty()),
            promise_for_call_forwarding: branch2,
            promise_for_client_resolution: branch3,
        }));
        let this = Rc::downgrade(&inner);
        let self_resolution_op = ::eagerly_evaluate(branch1.then(move |result| {
            let state = match this.upgrade() {
                Some(s) => s,
                None => return Err(Error::failed("dangling reference to QueuedClient".into())),
            };

            ClientInner::resolve(&state, result);
            Ok(())
        }));
        inner.borrow_mut().self_resolution_op = self_resolution_op;
        Client {
            inner: inner
        }
    }
}

impl ClientHook for Client {
    fn add_ref(&self) -> Box<ClientHook> {
        Box::new(Client {inner: self.inner.clone()})
    }
    fn new_call(&self, interface_id: u64, method_id: u16,
                size_hint: Option<::capnp::MessageSize>)
                -> ::capnp::capability::Request<any_pointer::Owned, any_pointer::Owned>
    {
        ::capnp::capability::Request::new(
            Box::new(local::Request::new(interface_id, method_id, size_hint, self.add_ref())))
    }

    fn call(&self, interface_id: u64, method_id: u16, params: Box<ParamsHook>, results: Box<ResultsHook>,
            results_done: Promise<Box<ResultsDoneHook>, Error>)
        -> (Promise<(), Error>, Box<PipelineHook>)
    {

        let promise_for_pair = self.inner.borrow_mut().promise_for_call_forwarding.clone().map(move |client| {
            client.call(interface_id, method_id, params, results, results_done)
        });

        let (promise_promise, pipeline_promise) = ::split::split(promise_for_pair);
        let pipeline = Pipeline::new(Promise::from_future(pipeline_promise));
        (Promise::from_future(promise_promise.flatten()), Box::new(pipeline))
    }

    fn get_ptr(&self) -> usize {
        (&*self.inner.borrow()) as * const _ as usize
    }

    fn get_brand(&self) -> usize {
        0
    }

    fn get_resolved(&self) -> Option<Box<ClientHook>> {
        match self.inner.borrow().redirect {
            Some(ref inner) => Some(inner.clone()),
            None => None,
        }
    }

    fn when_more_resolved(&self) -> Option<Promise<Box<ClientHook>, Error>> {
        Some(Promise::from_future(self.inner.borrow_mut().promise_for_client_resolution.clone()))
    }
}
<|endoftext|>


<commit_before>use rustc::lint::{Context, LintArray, LintPass};
use syntax::ast::*;
use syntax::codemap::Spanned;
use utils::match_type;

declare_lint! {
    pub RANGE_STEP_BY_ZERO, Warn,
    "using Range::step_by(0), which produces an infinite iterator"
}

#[derive(Copy,Clone)]
pub struct StepByZero;

impl LintPass for StepByZero {
    fn get_lints(&self) -> LintArray {
        lint_array!(RANGE_STEP_BY_ZERO)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let ExprMethodCall(Spanned { node: ref ident, .. }, _,
                              ref args) = expr.node {
            // Only warn on literal ranges.
            if ident.name.as_str() == "step_by" && args.len() == 2 &&
                is_range(cx, &args[0]) && is_lit_zero(&args[1]) {
                cx.span_lint(RANGE_STEP_BY_ZERO, expr.span,
                             "Range::step_by(0) produces an infinite iterator. \
                              Consider using `std::iter::repeat()` instead")
            }
        }
    }
}

fn is_range(cx: &Context, expr: &Expr) -> bool {
    // No need for walk_ptrs_ty here because step_by moves self, so it
    // can't be called on a borrowed range.
    let ty = cx.tcx.expr_ty(expr);
    // Note: RangeTo and RangeFull don't have step_by
    match_type(cx, ty, &["core", "ops", "Range"]) || match_type(cx, ty, &["core", "ops", "RangeFrom"])
}

fn is_lit_zero(expr: &Expr) -> bool {
    // FIXME: use constant folding
    if let ExprLit(ref spanned) = expr.node {
        if let LitInt(0, _) = spanned.node {
            return true;
        }
    }
    false
}
<commit_msg>ranges: remove unneeded as_str()<commit_after>use rustc::lint::{Context, LintArray, LintPass};
use syntax::ast::*;
use syntax::codemap::Spanned;
use utils::match_type;

declare_lint! {
    pub RANGE_STEP_BY_ZERO, Warn,
    "using Range::step_by(0), which produces an infinite iterator"
}

#[derive(Copy,Clone)]
pub struct StepByZero;

impl LintPass for StepByZero {
    fn get_lints(&self) -> LintArray {
        lint_array!(RANGE_STEP_BY_ZERO)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let ExprMethodCall(Spanned { node: ref ident, .. }, _,
                              ref args) = expr.node {
            // Only warn on literal ranges.
            if ident.name == "step_by" && args.len() == 2 &&
                is_range(cx, &args[0]) && is_lit_zero(&args[1]) {
                cx.span_lint(RANGE_STEP_BY_ZERO, expr.span,
                             "Range::step_by(0) produces an infinite iterator. \
                              Consider using `std::iter::repeat()` instead")
            }
        }
    }
}

fn is_range(cx: &Context, expr: &Expr) -> bool {
    // No need for walk_ptrs_ty here because step_by moves self, so it
    // can't be called on a borrowed range.
    let ty = cx.tcx.expr_ty(expr);
    // Note: RangeTo and RangeFull don't have step_by
    match_type(cx, ty, &["core", "ops", "Range"]) || match_type(cx, ty, &["core", "ops", "RangeFrom"])
}

fn is_lit_zero(expr: &Expr) -> bool {
    // FIXME: use constant folding
    if let ExprLit(ref spanned) = expr.node {
        if let LitInt(0, _) = spanned.node {
            return true;
        }
    }
    false
}
<|endoftext|>


<commit_before>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#![feature(coerce_unsized)]
#![feature(std_panic)]
#![feature(recover)]

extern crate libc;
extern crate iup_sys;
extern crate kernel32;
extern crate winapi;
extern crate smallvec;

#[cfg(windows)]
fn get_thread_id() -> isize {
    use kernel32::GetCurrentThreadId;
    unsafe { GetCurrentThreadId() as isize }
}

macro_rules! impl_control_traits {
    ($control:ident) => {
        unsafe impl Control for ::$control {
            fn handle(&self) -> *mut Ihandle {
                assert!(!self.0.get().is_null(), "attempted to use destroyed control");
                ::check_thread();
                self.0.get()
            }
        }

        unsafe impl UnwrapHandle for ::$control {
            fn try_unwrap_handle(self) -> Result<*mut Ihandle, Self> {
                assert!(!self.0.get().is_null(), "attempted to use destroyed control");
                self.0.try_unwrap().map_err(|handle_rc| ::$control(handle_rc))
            }
        }
    };
}

#[macro_use]
mod callbacks;
#[macro_use]
mod containers;

mod control_prelude;

mod attributes;
mod button;
mod canvas;
mod label;
mod menu;
mod text;
mod toggle;
mod dialog;
mod file_dlg;
mod handle_rc;
mod extra_refs;

pub use dialog::{Dialog, ShowCallbackToken, ShowState};
pub use file_dlg::{FileDlg, FileDialogType, FileExtFilter};
pub use button::Button;
pub use canvas::{Canvas, PaintingArgs};
pub use label::Label;
pub use text::{Text, CaretArgs};
pub use toggle::Toggle;
pub use menu::{Menu, MenuSubitem, Item, Separator, Submenu};
pub use containers::{Container, Fill, Hbox, Vbox, NumDiv, GridBox};
pub use callbacks::{CallbackAction, Event};

// With this layout, you can glob import this module's contents but selectively import the
// above types if you want.
pub mod common_attrs_cbs {
    pub use attributes::{
        ActiveAttribute,
        MinMaxSizeAttribute,
        TipAttribute,
        TitleAttribute,
        VisibleAttribute,
    };
    pub use callbacks::{
        MenuCommonCallbacks,
        GetKillFocusCallbacks,
        EnterLeaveWindowCallbacks,
        ButtonArgs,
        ButtonCallback,
        ValueChangedCallback,
        MotionArgs,
        CanvasCallbacks,
    };
}

use std::ptr;
use std::sync::atomic::{AtomicIsize, Ordering, ATOMIC_ISIZE_INIT};
use libc::{c_char, c_int};
use iup_sys::*;

// returns false if a panic is pending, but cannot be propagated because the main loop level is too high
fn propagate_panic() -> bool {
    if callbacks::is_panic_pending() && main_loop_level() > 0 {
        exit_loop();
        return false;
    }
    if let Some(payload) = callbacks::take_panic_payload() {
        // TODO: once a new nightly is released, use this
        //::std::panic::propagate(payload);
        panic!(payload.downcast_ref::<String>().unwrap_or(&String::new()).clone());
    }
    true
}

/// Executes the user interaction until `exit_loop` is called or the last visible dialog is hidden.
///
/// If you cascade many calls to `main_loop`, there must be a `exit_loop` call for each cascade
/// level, as hiding all dialogs will close only one level. Call `main_loop_level` to obtain the
/// current level.
///
/// If `main_loop` is called without any visible dialogs and no active timers, the application will
/// hang, and it will not be possible to close the main loop. The process will have to be
/// interrupted by the system.
///
/// When the last visible dialog is hidden, the `exit_loop` function is automatically called,
/// causing the `main_loop` to return. To avoid that, call `set_lock_loop(true)` before hiding the
/// last dialog.
pub fn main_loop() {
    unsafe {
        iup_open();

        // Callbacks can be called before main_loop() ever is. If they have been and have
        // panicked, we need to panic without calling IupMainLoop(). (IupMainLoopLevel() will
        // be 0 in a callback if it is running before IupMainLoop() has been called.)
        if !propagate_panic() { return; }
        IupMainLoop();
        propagate_panic();
    }
}

/// Returns the current cascade level of `main_loop`.
///
/// When no calls were done, return value is 0. Each time `main_loop` is called, the level
/// increases by one, and each time `main_loop` returns, the level decreases by one. You can use
/// this function to check if `main_loop` was already called and avoid calling it again.
///
/// A call to `Popup::popup` will increase the level by one.
pub fn main_loop_level() -> i32 {
    unsafe {
        IupMainLoopLevel()
    }
}

pub fn exit_loop() {
    unsafe {
        IupExitLoop();
    }
}

/// If this setting is set to false and the last visible dialog is closed, the `exit_loop`
/// function is called.
///
/// To avoid `exit_loop` being called, call `set_lock_loop(true)` before hiding the last dialog.
///
/// The default is false.
pub fn lock_loop() -> bool {
    unsafe {
        attributes::get_str_attribute_slice(ptr::null_mut(), "LOCKLOOP\0") == "YES"
    }
}

/// See `lock_loop`
pub fn set_lock_loop(lock: bool) {
    attributes::set_str_attribute(ptr::null_mut(), "LOCKLOOP\0", if lock { "YES\0" } else { "NO\0" });
}

fn iup_open() {
    check_thread();
    unsafe {
        if IupOpen(ptr::null_mut(), ptr::null_mut()) == IUP_OPENED {
            return;
        }
        attributes::set_str_attribute(ptr::null_mut(), "UTF8MODE", "YES");
        attributes::set_str_attribute(ptr::null_mut(), "UTF8MODE_FILE", "YES");
    }
}

static THREAD_ID: AtomicIsize = ATOMIC_ISIZE_INIT;

fn check_thread() {
    let thread_id = get_thread_id();
    let prev = THREAD_ID.compare_and_swap(0, thread_id, Ordering::SeqCst);
    assert!(prev == 0 || prev == thread_id, "IUP/Clear Coat functions must be called from a single thread");
}

// Part of the contract of implementing this trait is that no invalid handle
// is returned. Either the handle will stay valid for the life of the object or
// the method will panic.
pub unsafe trait Control {
    fn handle(&self) -> *mut Ihandle;

    fn detach(&self) {
        unsafe { IupDetach(self.handle()); }
    }

    fn reparent(&self, new_parent: &Container, ref_child: Option<&Control>) -> Result<(), ()> {
        unsafe {
            let ref_child = ref_child.map(|c| c.handle()).unwrap_or(ptr::null_mut());
            if IupReparent(self.handle(), new_parent.handle(), ref_child) == IUP_NOERROR {
                Ok(())
            } else {
                Err(())
            }
        }
    }

    fn get_dialog(&self) -> Option<Dialog> {
        unsafe {
            let handle = IupGetDialog(self.handle());
            if handle == ptr::null_mut() {
                None
            } else {
                Some(Dialog::from_handle(handle))
            }
        }
    }
}

// If this wrapper has the only reference, it gives up shared ownership of the *mut Ihandle.
pub unsafe trait UnwrapHandle : Sized {
    fn try_unwrap_handle(self) -> Result<*mut Ihandle, Self>;
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScreenPosition { // This is the name IUP uses: SCREENPOSITION attribute
    Absolute(i32),
    Left,
    Right,
    Top,
    Bottom,
    Center,
    MousePos,
    CenterParent,
    Current,
}

impl ScreenPosition {
    #[allow(dead_code)]
    fn from_int_x(i: c_int) -> ScreenPosition {
       match i {
           IUP_LEFT => ScreenPosition::Left,
           IUP_RIGHT => ScreenPosition::Right,
           IUP_CENTER => ScreenPosition::Center,
           IUP_MOUSEPOS => ScreenPosition::MousePos,
           IUP_CENTERPARENT => ScreenPosition::CenterParent,
           IUP_CURRENT => ScreenPosition::Current,
           _ => ScreenPosition::Absolute(i),
       }
    }

    #[allow(dead_code)]
    fn from_int_y(i: c_int) -> ScreenPosition {
       match i {
           IUP_TOP => ScreenPosition::Top,
           IUP_BOTTOM => ScreenPosition::Bottom,
           _ => Self::from_int_x(i),
       }
    }

    fn to_int(self) -> c_int {
        match self {
            ScreenPosition::Absolute(i) => i,
            ScreenPosition::Left => IUP_LEFT,
            ScreenPosition::Right => IUP_RIGHT,
            ScreenPosition::Top => IUP_TOP,
            ScreenPosition::Bottom => IUP_BOTTOM,
            ScreenPosition::Center => IUP_CENTER,
            ScreenPosition::MousePos => IUP_MOUSEPOS,
            ScreenPosition::CenterParent => IUP_CENTERPARENT,
            ScreenPosition::Current => IUP_CURRENT,
        }
    }
}

pub trait Popup : Control {
    fn popup(&self, x: ScreenPosition, y: ScreenPosition) -> Result<(), ()> {
        unsafe {
            if IupPopup(self.handle(), x.to_int(), y.to_int()) == IUP_NOERROR {
                Ok(())
            } else {
                Err(())
            }
        }
    }
}

#[derive(Copy,Clone)]
pub enum MouseButton {
    Button1,
    Button2,
    Button3,
    Button4,
    Button5,
}

impl MouseButton {
    fn from_int(i: c_int) -> MouseButton {
        match i {
            IUP_BUTTON1 => MouseButton::Button1,
            IUP_BUTTON2 => MouseButton::Button2,
            IUP_BUTTON3 => MouseButton::Button3,
            IUP_BUTTON4 => MouseButton::Button4,
            IUP_BUTTON5 => MouseButton::Button5,
            _ => panic!("unknown mouse button"),
        }
    }

    #[allow(dead_code)]
    fn to_int(self) -> c_int {
        match self {
            MouseButton::Button1 => IUP_BUTTON1,
            MouseButton::Button2 => IUP_BUTTON2,
            MouseButton::Button3 => IUP_BUTTON3,
            MouseButton::Button4 => IUP_BUTTON4,
            MouseButton::Button5 => IUP_BUTTON5,
        }
    }
}

#[derive(Clone)]
pub struct KeyboardMouseStatus {
    shift_pressed: bool,
    control_pressed: bool,
    alt_pressed: bool,
    sys_pressed: bool,
    button1_pressed: bool,
    button2_pressed: bool,
    button3_pressed: bool,
    button4_pressed: bool,
    button5_pressed: bool,
}

impl KeyboardMouseStatus {
    unsafe fn from_cstr(s: *const c_char) -> KeyboardMouseStatus {
        KeyboardMouseStatus {
            shift_pressed: iup_isshift(s),
            control_pressed: iup_iscontrol(s),
            alt_pressed: iup_isalt(s),
            sys_pressed: iup_issys(s),
            button1_pressed: iup_isbutton1(s),
            button2_pressed: iup_isbutton2(s),
            button3_pressed: iup_isbutton3(s),
            button4_pressed: iup_isbutton4(s),
            button5_pressed: iup_isbutton5(s),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum VAlignment {
    Top,
    Center,
    Bottom,
}

impl VAlignment {
    fn from_str(s: &[u8]) -> VAlignment {
        match s {
            b"ATOP" => VAlignment::Top,
            b"ACENTER" => VAlignment::Center,
            b"ABOTTOM" => VAlignment::Bottom,
            _ => panic!("unknown VAlignment"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            VAlignment::Top => "ATOP",
            VAlignment::Center => "ACENTER",
            VAlignment::Bottom => "ABOTTOM",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HAlignment {
    Left,
    Center,
    Right,
}

impl HAlignment {
    fn from_str(s: &[u8]) -> HAlignment {
        match s {
            b"ALEFT" => HAlignment::Left,
            b"ACENTER" => HAlignment::Center,
            b"ARIGHT" => HAlignment::Right,
            _ => panic!("unknown HAlignment"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            HAlignment::Left => "ALEFT",
            HAlignment::Center => "ACENTER",
            HAlignment::Right => "ARIGHT",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Orientation {
    Horizontal,
    Vertical,
}

impl Orientation {
    fn from_str(s: &[u8]) -> Orientation {
        match s {
            b"HORIZONTAL" => Orientation::Horizontal,
            b"VERTICAL" => Orientation::Vertical,
            _ => panic!("unknown Orientation"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            Orientation::Horizontal => "HORIZONTAL",
            Orientation::Vertical => "VERTICAL",
        }
    }
}
<commit_msg>Add update and redraw methods<commit_after>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#![feature(coerce_unsized)]
#![feature(std_panic)]
#![feature(recover)]

extern crate libc;
extern crate iup_sys;
extern crate kernel32;
extern crate winapi;
extern crate smallvec;

#[cfg(windows)]
fn get_thread_id() -> isize {
    use kernel32::GetCurrentThreadId;
    unsafe { GetCurrentThreadId() as isize }
}

macro_rules! impl_control_traits {
    ($control:ident) => {
        unsafe impl Control for ::$control {
            fn handle(&self) -> *mut Ihandle {
                assert!(!self.0.get().is_null(), "attempted to use destroyed control");
                ::check_thread();
                self.0.get()
            }
        }

        unsafe impl UnwrapHandle for ::$control {
            fn try_unwrap_handle(self) -> Result<*mut Ihandle, Self> {
                assert!(!self.0.get().is_null(), "attempted to use destroyed control");
                self.0.try_unwrap().map_err(|handle_rc| ::$control(handle_rc))
            }
        }
    };
}

#[macro_use]
mod callbacks;
#[macro_use]
mod containers;

mod control_prelude;

mod attributes;
mod button;
mod canvas;
mod label;
mod menu;
mod text;
mod toggle;
mod dialog;
mod file_dlg;
mod handle_rc;
mod extra_refs;

pub use dialog::{Dialog, ShowCallbackToken, ShowState};
pub use file_dlg::{FileDlg, FileDialogType, FileExtFilter};
pub use button::Button;
pub use canvas::{Canvas, PaintingArgs};
pub use label::Label;
pub use text::{Text, CaretArgs};
pub use toggle::Toggle;
pub use menu::{Menu, MenuSubitem, Item, Separator, Submenu};
pub use containers::{Container, Fill, Hbox, Vbox, NumDiv, GridBox};
pub use callbacks::{CallbackAction, Event};

// With this layout, you can glob import this module's contents but selectively import the
// above types if you want.
pub mod common_attrs_cbs {
    pub use attributes::{
        ActiveAttribute,
        MinMaxSizeAttribute,
        TipAttribute,
        TitleAttribute,
        VisibleAttribute,
    };
    pub use callbacks::{
        MenuCommonCallbacks,
        GetKillFocusCallbacks,
        EnterLeaveWindowCallbacks,
        ButtonArgs,
        ButtonCallback,
        ValueChangedCallback,
        MotionArgs,
        CanvasCallbacks,
    };
}

use std::ptr;
use std::sync::atomic::{AtomicIsize, Ordering, ATOMIC_ISIZE_INIT};
use libc::{c_char, c_int};
use iup_sys::*;

// returns false if a panic is pending, but cannot be propagated because the main loop level is too high
fn propagate_panic() -> bool {
    if callbacks::is_panic_pending() && main_loop_level() > 0 {
        exit_loop();
        return false;
    }
    if let Some(payload) = callbacks::take_panic_payload() {
        // TODO: once a new nightly is released, use this
        //::std::panic::propagate(payload);
        panic!(payload.downcast_ref::<String>().unwrap_or(&String::new()).clone());
    }
    true
}

/// Executes the user interaction until `exit_loop` is called or the last visible dialog is hidden.
///
/// If you cascade many calls to `main_loop`, there must be a `exit_loop` call for each cascade
/// level, as hiding all dialogs will close only one level. Call `main_loop_level` to obtain the
/// current level.
///
/// If `main_loop` is called without any visible dialogs and no active timers, the application will
/// hang, and it will not be possible to close the main loop. The process will have to be
/// interrupted by the system.
///
/// When the last visible dialog is hidden, the `exit_loop` function is automatically called,
/// causing the `main_loop` to return. To avoid that, call `set_lock_loop(true)` before hiding the
/// last dialog.
pub fn main_loop() {
    unsafe {
        iup_open();

        // Callbacks can be called before main_loop() ever is. If they have been and have
        // panicked, we need to panic without calling IupMainLoop(). (IupMainLoopLevel() will
        // be 0 in a callback if it is running before IupMainLoop() has been called.)
        if !propagate_panic() { return; }
        IupMainLoop();
        propagate_panic();
    }
}

/// Returns the current cascade level of `main_loop`.
///
/// When no calls were done, return value is 0. Each time `main_loop` is called, the level
/// increases by one, and each time `main_loop` returns, the level decreases by one. You can use
/// this function to check if `main_loop` was already called and avoid calling it again.
///
/// A call to `Popup::popup` will increase the level by one.
pub fn main_loop_level() -> i32 {
    unsafe {
        IupMainLoopLevel()
    }
}

pub fn exit_loop() {
    unsafe {
        IupExitLoop();
    }
}

/// If this setting is set to false and the last visible dialog is closed, the `exit_loop`
/// function is called.
///
/// To avoid `exit_loop` being called, call `set_lock_loop(true)` before hiding the last dialog.
///
/// The default is false.
pub fn lock_loop() -> bool {
    unsafe {
        attributes::get_str_attribute_slice(ptr::null_mut(), "LOCKLOOP\0") == "YES"
    }
}

/// See `lock_loop`
pub fn set_lock_loop(lock: bool) {
    attributes::set_str_attribute(ptr::null_mut(), "LOCKLOOP\0", if lock { "YES\0" } else { "NO\0" });
}

fn iup_open() {
    check_thread();
    unsafe {
        if IupOpen(ptr::null_mut(), ptr::null_mut()) == IUP_OPENED {
            return;
        }
        attributes::set_str_attribute(ptr::null_mut(), "UTF8MODE", "YES");
        attributes::set_str_attribute(ptr::null_mut(), "UTF8MODE_FILE", "YES");
    }
}

static THREAD_ID: AtomicIsize = ATOMIC_ISIZE_INIT;

fn check_thread() {
    let thread_id = get_thread_id();
    let prev = THREAD_ID.compare_and_swap(0, thread_id, Ordering::SeqCst);
    assert!(prev == 0 || prev == thread_id, "IUP/Clear Coat functions must be called from a single thread");
}

// Part of the contract of implementing this trait is that no invalid handle
// is returned. Either the handle will stay valid for the life of the object or
// the method will panic.
pub unsafe trait Control {
    fn handle(&self) -> *mut Ihandle;

    fn detach(&self) {
        unsafe { IupDetach(self.handle()); }
    }

    fn reparent(&self, new_parent: &Container, ref_child: Option<&Control>) -> Result<(), ()> {
        unsafe {
            let ref_child = ref_child.map(|c| c.handle()).unwrap_or(ptr::null_mut());
            if IupReparent(self.handle(), new_parent.handle(), ref_child) == IUP_NOERROR {
                Ok(())
            } else {
                Err(())
            }
        }
    }

    fn get_dialog(&self) -> Option<Dialog> {
        unsafe {
            let handle = IupGetDialog(self.handle());
            if handle == ptr::null_mut() {
                None
            } else {
                Some(Dialog::from_handle(handle))
            }
        }
    }

    fn update(&self) {
        unsafe {
            IupUpdate(self.handle());
        }
    }

    fn redraw(&self, include_children: bool) {
        unsafe {
            IupRedraw(self.handle(), if include_children { 1 } else { 0 });
        }
    }
}

// If this wrapper has the only reference, it gives up shared ownership of the *mut Ihandle.
pub unsafe trait UnwrapHandle : Sized {
    fn try_unwrap_handle(self) -> Result<*mut Ihandle, Self>;
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScreenPosition { // This is the name IUP uses: SCREENPOSITION attribute
    Absolute(i32),
    Left,
    Right,
    Top,
    Bottom,
    Center,
    MousePos,
    CenterParent,
    Current,
}

impl ScreenPosition {
    #[allow(dead_code)]
    fn from_int_x(i: c_int) -> ScreenPosition {
       match i {
           IUP_LEFT => ScreenPosition::Left,
           IUP_RIGHT => ScreenPosition::Right,
           IUP_CENTER => ScreenPosition::Center,
           IUP_MOUSEPOS => ScreenPosition::MousePos,
           IUP_CENTERPARENT => ScreenPosition::CenterParent,
           IUP_CURRENT => ScreenPosition::Current,
           _ => ScreenPosition::Absolute(i),
       }
    }

    #[allow(dead_code)]
    fn from_int_y(i: c_int) -> ScreenPosition {
       match i {
           IUP_TOP => ScreenPosition::Top,
           IUP_BOTTOM => ScreenPosition::Bottom,
           _ => Self::from_int_x(i),
       }
    }

    fn to_int(self) -> c_int {
        match self {
            ScreenPosition::Absolute(i) => i,
            ScreenPosition::Left => IUP_LEFT,
            ScreenPosition::Right => IUP_RIGHT,
            ScreenPosition::Top => IUP_TOP,
            ScreenPosition::Bottom => IUP_BOTTOM,
            ScreenPosition::Center => IUP_CENTER,
            ScreenPosition::MousePos => IUP_MOUSEPOS,
            ScreenPosition::CenterParent => IUP_CENTERPARENT,
            ScreenPosition::Current => IUP_CURRENT,
        }
    }
}

pub trait Popup : Control {
    fn popup(&self, x: ScreenPosition, y: ScreenPosition) -> Result<(), ()> {
        unsafe {
            if IupPopup(self.handle(), x.to_int(), y.to_int()) == IUP_NOERROR {
                Ok(())
            } else {
                Err(())
            }
        }
    }
}

#[derive(Copy,Clone)]
pub enum MouseButton {
    Button1,
    Button2,
    Button3,
    Button4,
    Button5,
}

impl MouseButton {
    fn from_int(i: c_int) -> MouseButton {
        match i {
            IUP_BUTTON1 => MouseButton::Button1,
            IUP_BUTTON2 => MouseButton::Button2,
            IUP_BUTTON3 => MouseButton::Button3,
            IUP_BUTTON4 => MouseButton::Button4,
            IUP_BUTTON5 => MouseButton::Button5,
            _ => panic!("unknown mouse button"),
        }
    }

    #[allow(dead_code)]
    fn to_int(self) -> c_int {
        match self {
            MouseButton::Button1 => IUP_BUTTON1,
            MouseButton::Button2 => IUP_BUTTON2,
            MouseButton::Button3 => IUP_BUTTON3,
            MouseButton::Button4 => IUP_BUTTON4,
            MouseButton::Button5 => IUP_BUTTON5,
        }
    }
}

#[derive(Clone)]
pub struct KeyboardMouseStatus {
    shift_pressed: bool,
    control_pressed: bool,
    alt_pressed: bool,
    sys_pressed: bool,
    button1_pressed: bool,
    button2_pressed: bool,
    button3_pressed: bool,
    button4_pressed: bool,
    button5_pressed: bool,
}

impl KeyboardMouseStatus {
    unsafe fn from_cstr(s: *const c_char) -> KeyboardMouseStatus {
        KeyboardMouseStatus {
            shift_pressed: iup_isshift(s),
            control_pressed: iup_iscontrol(s),
            alt_pressed: iup_isalt(s),
            sys_pressed: iup_issys(s),
            button1_pressed: iup_isbutton1(s),
            button2_pressed: iup_isbutton2(s),
            button3_pressed: iup_isbutton3(s),
            button4_pressed: iup_isbutton4(s),
            button5_pressed: iup_isbutton5(s),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum VAlignment {
    Top,
    Center,
    Bottom,
}

impl VAlignment {
    fn from_str(s: &[u8]) -> VAlignment {
        match s {
            b"ATOP" => VAlignment::Top,
            b"ACENTER" => VAlignment::Center,
            b"ABOTTOM" => VAlignment::Bottom,
            _ => panic!("unknown VAlignment"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            VAlignment::Top => "ATOP",
            VAlignment::Center => "ACENTER",
            VAlignment::Bottom => "ABOTTOM",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HAlignment {
    Left,
    Center,
    Right,
}

impl HAlignment {
    fn from_str(s: &[u8]) -> HAlignment {
        match s {
            b"ALEFT" => HAlignment::Left,
            b"ACENTER" => HAlignment::Center,
            b"ARIGHT" => HAlignment::Right,
            _ => panic!("unknown HAlignment"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            HAlignment::Left => "ALEFT",
            HAlignment::Center => "ACENTER",
            HAlignment::Right => "ARIGHT",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Orientation {
    Horizontal,
    Vertical,
}

impl Orientation {
    fn from_str(s: &[u8]) -> Orientation {
        match s {
            b"HORIZONTAL" => Orientation::Horizontal,
            b"VERTICAL" => Orientation::Vertical,
            _ => panic!("unknown Orientation"),
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            Orientation::Horizontal => "HORIZONTAL",
            Orientation::Vertical => "VERTICAL",
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
use std::iter::Iterator;

// Flat-maps an Option<I> where I: Iterator into any underlying Iterator.
pub struct FlatIter<I: Iterator> {
    wrapped: Option<I>,
}

impl<I: Iterator> FlatIter<I> {
    pub fn new(wrapped: Option<I>) -> Self {
        FlatIter { wrapped: wrapped, }
    }
}

impl<I: Iterator> Iterator for FlatIter<I> {
    type Item = <I as Iterator>::Item;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        match self.wrapped {
            Some(ref mut i) => i.next(),
            None => None,
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.wrapped {
            Some(ref i) => i.size_hint(),
            None => (0usize, Some(0usize)),
        }
    }
}

/// Main entrypoint for iteration via for comprehensions. Produces an iterator
/// over values `yield`ed in a sequence of semicolon-separated expressions.
///
/// Multiple `yield` expressions are permitted. If present, they will produce a
/// chained sequence of iterators. Iterator chaining functions according to the
/// scope of any immediately preceding `for` statement.
///
/// For example, `iterate![yield 0; yield 1; yield 2]` produces the sequence `1,
/// 2, 3`, but `iterate![for x in 0..2; yield x; for y in 2..4; yield y]`
/// produces the sequence `0, 2, 3, 1, 2, 3`, as if you had written:
///
/// ```rust,ignore
/// for x in 0..2 {
///   yield x;
///   for y in 2..4 {
///     yield y;
///   }
/// }
/// ```
///
/// Expressions are:
///
/// * `for x in xs`: introduces a new scope that iterates over `xs`, binding `x`
/// to each of its values.
/// * `if cond`: short-circuits subsequent expressions if `cond` is not `true`.
/// * `let a = b`: introduces a new scope that binds `a` to `b`.
/// * `yield r`: emits the value of the expression `r`.
///
/// Expressions are compiled into nested flat_map operations via closures that
/// move out of the enclosing environment. See the package README for details.
#[macro_export]
macro_rules! iterate {
    // Implementation note: adjacent wildcard matches aren't allowed, so each
    // recursive rule has three cases, wherein the wildcards matches are
    // separated by macro keywords.

    // yield
    (yield $r:expr) => (Some($r).into_iter());
    // chained yield
    (yield $r:expr; for $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![for $($rest)+]));
    (yield $r:expr; if $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![if $($rest)+]));
    (yield $r:expr; let $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![let $($rest)+]));
    (yield $r:expr; yield $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![yield $($rest)+]));
    // for
    (for $x:ident in $xs:expr; $($rest:tt)*) =>
        ($xs.flat_map(move |$x| { iterate![$($rest)*] }));
    // if if => if
    (if $a:expr; if $b:expr; $(if $c:expr;)* for $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; for $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* let $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; let $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* yield $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; yield $($rest)*]);
    // if
    (if $a:expr; for $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![for $($rest)*]) } else { None }));
    (if $a:expr; let $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![let $($rest)*]) } else { None }));
    (if $a:expr; yield $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![yield $($rest)*]) } else { None }));
    // let
    ($(let $lhs:pat = $rhs:expr;)+ for $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![for $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ if $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![if $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ yield $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![yield $($rest)*] });
}

#[cfg(test)]
mod tests {
    extern crate test;
    use std::fmt::Display;

    fn check_match<N: PartialEq + Display, I: Iterator<Item=N>, J: Iterator<Item=N>>(mut i: I, mut j: J) {
        let mut n = 0usize;
        loop {
            match (i.next(), j.next()) {
                (None, None) => return,
                (Some(ref x), None) => panic!["Mismatch on item {}: {} vs. None", n, x],
                (None, Some(ref y)) => panic!["Mismatch on item {}: None vs. {}", n, y],
                (Some(ref x), Some(ref y)) => if x != y { panic!["Mismatch on item {}: {} vs. {}", n, x, y] },
            }
            n += 1;
        }
    }

    #[test]
    fn test_basic() {
        check_match(vec![1, 2, 3, 4, 5].into_iter(),
                    iterate![for i in 1..6; yield i]);
    }

    #[test]
    fn test_basic_if() {
        check_match(vec![0, 2, 4, 6, 8].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             yield i]);
    }

    #[test]
    fn test_basic_if_let() {
        check_match(vec![1, 3, 5, 7, 9].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             let z = i + 1;
                             yield z]);
    }

    // This fails to compile because the closures generated by iterate! may
    // outlive values. (Using values.into_iter() also fails.)
    // fn test_move_fail() {
    //     let values = vec![1, 2, 3, 4];
    //     iterate![for i in 0..10; for v in values.iter(); yield v];
    // }

    // This provides an example of how to refer to an external variable without
    // moving everything out of it.
    #[test]
    fn test_move_succeed() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for i in 0..4;
                             for v in vs.iter();
                             yield *v]);
    }

    // Taking references like that is still prone to lifetime-checking,
    // however. The below also fails to compile.
    // fn test_move_fail() {
    //     let i = {
    //         let values = vec![1, 2, 3, 4];
    //         let vs = &values;
    //         iterate![for i in 0..4; for v in vs.iter(); yield *v]
    //     };
    //     for x in i {
    //         println!("{}", x)
    //     }
    // }

    #[test]
    fn test_nested_refs() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_inner_if() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 2, 2, 2].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             if *y == 2;
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_outer_let() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 6, 7, 8,
                         6, 7, 8, 9,
                         7, 8, 9, 10,
                         8, 9, 10, 11].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // Variable shadowing is allowed, even when variables differ by type. This
    // is consistent with ordinary "let".
    #[test]
    fn test_nested_refs_let_let_shadowing_1() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let a = 1;
                             yield *y + a]);
    }

    #[test]
    fn test_nested_refs_let_let_shadowing_2() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             let a = 1;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // The compiler will warn about unused loop variables.
    #[test]
    fn test_nested_refs_for_let_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 5, 5, 5,
                         6, 6, 6, 6,
                         7, 7, 7, 7,
                         8, 8, 8, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let y = 1;
                             yield y + a]);
    }

    #[test]
    fn test_nested_refs_for_let_for_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        let other_values = vec![5, 6, 7, 8];
        let ys = &other_values;
        check_match(vec![5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let x = *x + 1;
                             for x in ys.iter();
                             yield *x]);
    }

    #[test]
    fn test_just_multiple_yields() {
        check_match(vec![0, 1, 2, 3, 4, 5].into_iter(),
                    iterate![yield 0;
                             yield 1;
                             yield 2;
                             yield 3;
                             yield 4;
                             yield 5]);
    }

    #[test]
    fn test_multiple_yields_liked_nested_loops() {
        check_match(vec![0, 5, 6, 7, 8, 9,
                         1, 5, 6, 7, 8, 9,
                         2, 5, 6, 7, 8, 9,
                         3, 5, 6, 7, 8, 9,
                         4, 5, 6, 7, 8, 9].into_iter(),
                    iterate![for x in 0..5;
                             yield x;
                             for y in 5..10;
                             yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_1() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       1,
                       2, 5, 6, 7, 8, 9,
                       3,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           yield x;
                           if x % 2 == 0;
                           for y in 5..10;
                           yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_2() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       2, 5, 6, 7, 8, 9,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           if x % 2 == 0;
                           yield x;
                           for y in 5..10;
                           yield y]);
    }
}
<commit_msg>Documentation tweak.<commit_after>#![feature(test)]
use std::iter::Iterator;

// Flat-maps an Option<I> where I: Iterator into any underlying Iterator.
pub struct FlatIter<I: Iterator> {
    wrapped: Option<I>,
}

impl<I: Iterator> FlatIter<I> {
    pub fn new(wrapped: Option<I>) -> Self {
        FlatIter { wrapped: wrapped, }
    }
}

impl<I: Iterator> Iterator for FlatIter<I> {
    type Item = <I as Iterator>::Item;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        match self.wrapped {
            Some(ref mut i) => i.next(),
            None => None,
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.wrapped {
            Some(ref i) => i.size_hint(),
            None => (0usize, Some(0usize)),
        }
    }
}

/// Main entrypoint for iteration via for comprehensions. Produces an iterator
/// over values `yield`ed in a sequence of semicolon-separated expressions.
///
/// Multiple `yield` expressions are permitted. If present, they will produce a
/// chained sequence of iterators. Iterator chaining functions according to the
/// scope of the closest preceding `for` statement.
///
/// For example, `iterate![yield 0; yield 1; yield 2]` produces the sequence `1,
/// 2, 3`, but `iterate![for x in 0..2; yield x; for y in 2..4; yield y]`
/// produces the sequence `0, 2, 3, 1, 2, 3`, as if you had written:
///
/// ```rust,ignore
/// for x in 0..2 {
///   yield x;
///   for y in 2..4 {
///     yield y;
///   }
/// }
/// ```
///
/// Expressions are:
///
/// * `for x in xs`: introduces a new scope that iterates over `xs`, binding `x`
/// to each of its values.
/// * `if cond`: short-circuits subsequent expressions if `cond` is not `true`.
/// * `let a = b`: introduces a new scope that binds `a` to `b`.
/// * `yield r`: emits the value of the expression `r`.
///
/// Expressions are compiled into nested flat_map operations via closures that
/// move out of the enclosing environment. See the package README for details.
#[macro_export]
macro_rules! iterate {
    // Implementation note: adjacent wildcard matches aren't allowed, so each
    // recursive rule has three cases, wherein the wildcards matches are
    // separated by macro keywords.

    // yield
    (yield $r:expr) => (Some($r).into_iter());
    // chained yield
    (yield $r:expr; for $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![for $($rest)+]));
    (yield $r:expr; if $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![if $($rest)+]));
    (yield $r:expr; let $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![let $($rest)+]));
    (yield $r:expr; yield $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![yield $($rest)+]));
    // for
    (for $x:ident in $xs:expr; $($rest:tt)*) =>
        ($xs.flat_map(move |$x| { iterate![$($rest)*] }));
    // if if => if
    (if $a:expr; if $b:expr; $(if $c:expr;)* for $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; for $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* let $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; let $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* yield $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; yield $($rest)*]);
    // if
    (if $a:expr; for $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![for $($rest)*]) } else { None }));
    (if $a:expr; let $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![let $($rest)*]) } else { None }));
    (if $a:expr; yield $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![yield $($rest)*]) } else { None }));
    // let
    ($(let $lhs:pat = $rhs:expr;)+ for $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![for $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ if $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![if $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ yield $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![yield $($rest)*] });
}

#[cfg(test)]
mod tests {
    extern crate test;
    use std::fmt::Display;

    fn check_match<N: PartialEq + Display, I: Iterator<Item=N>, J: Iterator<Item=N>>(mut i: I, mut j: J) {
        let mut n = 0usize;
        loop {
            match (i.next(), j.next()) {
                (None, None) => return,
                (Some(ref x), None) => panic!["Mismatch on item {}: {} vs. None", n, x],
                (None, Some(ref y)) => panic!["Mismatch on item {}: None vs. {}", n, y],
                (Some(ref x), Some(ref y)) => if x != y { panic!["Mismatch on item {}: {} vs. {}", n, x, y] },
            }
            n += 1;
        }
    }

    #[test]
    fn test_basic() {
        check_match(vec![1, 2, 3, 4, 5].into_iter(),
                    iterate![for i in 1..6; yield i]);
    }

    #[test]
    fn test_basic_if() {
        check_match(vec![0, 2, 4, 6, 8].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             yield i]);
    }

    #[test]
    fn test_basic_if_let() {
        check_match(vec![1, 3, 5, 7, 9].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             let z = i + 1;
                             yield z]);
    }

    // This fails to compile because the closures generated by iterate! may
    // outlive values. (Using values.into_iter() also fails.)
    // fn test_move_fail() {
    //     let values = vec![1, 2, 3, 4];
    //     iterate![for i in 0..10; for v in values.iter(); yield v];
    // }

    // This provides an example of how to refer to an external variable without
    // moving everything out of it.
    #[test]
    fn test_move_succeed() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for i in 0..4;
                             for v in vs.iter();
                             yield *v]);
    }

    // Taking references like that is still prone to lifetime-checking,
    // however. The below also fails to compile.
    // fn test_move_fail() {
    //     let i = {
    //         let values = vec![1, 2, 3, 4];
    //         let vs = &values;
    //         iterate![for i in 0..4; for v in vs.iter(); yield *v]
    //     };
    //     for x in i {
    //         println!("{}", x)
    //     }
    // }

    #[test]
    fn test_nested_refs() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_inner_if() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 2, 2, 2].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             if *y == 2;
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_outer_let() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 6, 7, 8,
                         6, 7, 8, 9,
                         7, 8, 9, 10,
                         8, 9, 10, 11].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // Variable shadowing is allowed, even when variables differ by type. This
    // is consistent with ordinary "let".
    #[test]
    fn test_nested_refs_let_let_shadowing_1() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let a = 1;
                             yield *y + a]);
    }

    #[test]
    fn test_nested_refs_let_let_shadowing_2() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             let a = 1;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // The compiler will warn about unused loop variables.
    #[test]
    fn test_nested_refs_for_let_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 5, 5, 5,
                         6, 6, 6, 6,
                         7, 7, 7, 7,
                         8, 8, 8, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let y = 1;
                             yield y + a]);
    }

    #[test]
    fn test_nested_refs_for_let_for_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        let other_values = vec![5, 6, 7, 8];
        let ys = &other_values;
        check_match(vec![5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let x = *x + 1;
                             for x in ys.iter();
                             yield *x]);
    }

    #[test]
    fn test_just_multiple_yields() {
        check_match(vec![0, 1, 2, 3, 4, 5].into_iter(),
                    iterate![yield 0;
                             yield 1;
                             yield 2;
                             yield 3;
                             yield 4;
                             yield 5]);
    }

    #[test]
    fn test_multiple_yields_liked_nested_loops() {
        check_match(vec![0, 5, 6, 7, 8, 9,
                         1, 5, 6, 7, 8, 9,
                         2, 5, 6, 7, 8, 9,
                         3, 5, 6, 7, 8, 9,
                         4, 5, 6, 7, 8, 9].into_iter(),
                    iterate![for x in 0..5;
                             yield x;
                             for y in 5..10;
                             yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_1() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       1,
                       2, 5, 6, 7, 8, 9,
                       3,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           yield x;
                           if x % 2 == 0;
                           for y in 5..10;
                           yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_2() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       2, 5, 6, 7, 8, 9,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           if x % 2 == 0;
                           yield x;
                           for y in 5..10;
                           yield y]);
    }
}
<|endoftext|>