<commit_before>mod component;
mod ui;

use crate::config::Config;
use crate::err::{self, Result};
use crate::file::SaveFile;
use crate::series::{SavedSeries, Series};
use anime::remote::RemoteService;
use chrono::{DateTime, Duration, Utc};
use clap::ArgMatches;
use component::command_prompt::{Command, CommandPrompt};
use component::log::{LogItem, StatusLog};
use snafu::ResultExt;
use std::mem;
use std::process;
use termion::event::Key;
use ui::{Event, Events, UI};

pub fn run(args: &ArgMatches) -> Result<()> {
    let mut ui = UI::init()?;

    let mut cstate = {
        let config = Config::load_or_create()?;
        let remote = init_remote(args, &mut ui.status_log);

        CommonState { config, remote }
    };

    let mut ui_state = init_ui_state(&cstate, args)?;
    let events = Events::new(Duration::seconds(1));

    loop {
        ui.draw(&ui_state, cstate.remote.as_ref())?;

        match events.next()? {
            Event::Input(key) => match key {
                // Exit
                Key::Char('q') if !ui_state.status_bar_state.in_input_dialog() => {
                    // Prevent ruining the user's terminal
                    ui.clear().ok();
                    break Ok(());
                }
                key => match ui_state.process_key(&mut cstate, &mut ui.status_log, key) {
                    Ok(_) => (),
                    Err(err) => {
                        ui.status_log.push(LogItem::failed("Processing key", err));
                    }
                },
            },
            Event::Tick => match ui_state.process_tick(&cstate, &mut ui.status_log) {
                Ok(_) => (),
                Err(err) => ui.status_log.push(LogItem::failed("Processing tick", err)),
            },
        }
    }
}

fn init_remote(args: &ArgMatches, log: &mut StatusLog) -> Box<dyn RemoteService> {
    use anime::remote::anilist;
    use anime::remote::offline::Offline;

    match crate::init_remote(args, true) {
        Ok(remote) => remote,
        Err(err) => {
            match err {
                err::Error::NeedAniListToken => {
                    log.push(format!(
                        "No access token found. Go to {} \
                         and set your token with the 'token' command",
                        anilist::auth_url(crate::ANILIST_CLIENT_ID)
                    ));
                }
                _ => {
                    log.push(LogItem::failed("Logging in", err));
                    log.push(format!(
                        "If you need a new token, go to {} \
                         and set it with the 'token' command",
                        anilist::auth_url(crate::ANILIST_CLIENT_ID)
                    ));
                }
            }

            log.push("Continuing in offline mode");

            Box::new(Offline::new())
        }
    }
}

/// Items that are not tied to the UI and are commonly used together.
struct CommonState {
    config: Config,
    remote: Box<dyn RemoteService>,
}

fn init_ui_state<'a>(cstate: &CommonState, args: &ArgMatches) -> Result<UIState<'a>> {
    let mut saved_series = SavedSeries::load_or_default()?;
    let series = init_series_list(&cstate, args, &mut saved_series)?;

    let selected_series = match saved_series.last_watched_id {
        Some(id) => series
            .iter()
            .position(|series| series.info.id == id)
            .unwrap_or(0),
        None => 0,
    };

    let series = series.into_iter().map(SeriesState::new).collect();

    Ok(UIState {
        series,
        selected_series,
        saved_series,
        status_bar_state: StatusBarState::default(),
        last_used_command: None,
    })
}

fn init_series_list(
    cstate: &CommonState,
    args: &ArgMatches,
    saved_series: &mut SavedSeries,
) -> Result<Vec<Series>> {
    let mut series = saved_series.load_all_series_and_validate()?;

    // If the user specified a series, we'll need to check to see if we
    // already have it or fetch & save it otherwise.
    let desired_series = match args.value_of("series") {
        Some(desired_series) => desired_series,
        None => return Ok(series),
    };

    if saved_series.contains(desired_series) {
        return Ok(series);
    }

    let new_series = saved_series.insert_and_save_from_args_and_remote(
        args,
        desired_series,
        &cstate.config,
        cstate.remote.as_ref(),
    )?;

    series.push(new_series);
    Ok(series)
}

/// Current state of the UI.
pub struct UIState<'a> {
    series: Vec<SeriesState>,
    selected_series: usize,
    saved_series: SavedSeries,
    status_bar_state: StatusBarState<'a>,
    last_used_command: Option<Command>,
}

macro_rules! cur_series {
    ($struct:ident) => {
        match $struct.cur_series() {
            Some(value) => value,
            None => return Ok(()),
        }
    };
}

macro_rules! cur_series_mut {
    ($struct:ident) => {
        match $struct.cur_series_mut() {
            Some(value) => value,
            None => return Ok(()),
        }
    };
}

impl<'a> UIState<'a> {
    fn cur_series(&self) -> Option<&SeriesState> {
        self.series.get(self.selected_series)
    }

    fn cur_series_mut(&mut self) -> Option<&mut SeriesState> {
        self.series.get_mut(self.selected_series)
    }

    fn process_key(
        &mut self,
        state: &mut CommonState,
        log: &mut StatusLog,
        key: Key,
    ) -> Result<()> {
        if !self.is_idle() {
            return Ok(());
        }

        if self.status_bar_state.in_input_dialog() {
            return self.process_input_dialog_key(state, log, key);
        }

        match key {
            // Play next episode
            Key::Char(ch) if ch == state.config.tui.keys.play_next_episode => {
                let last_watched_changed = {
                    let id = cur_series!(self).inner.info.id;
                    self.saved_series.set_last_watched(id)
                };

                if last_watched_changed {
                    log.capture_status("Marking series as the last watched one", || {
                        self.saved_series.save()
                    });
                }

                log.capture_status("Playing next episode", || {
                    cur_series_mut!(self).play_next_episode_async(&state)
                });
            }
            // Command prompt
            Key::Char(':') => {
                self.status_bar_state.set_to_command_prompt();
            }
            // Run last used command
            Key::Char(ch) if ch == state.config.tui.keys.run_last_command => {
                let cmd = match &self.last_used_command {
                    Some(cmd) => cmd.clone(),
                    None => return Ok(()),
                };

                self.process_command(cmd, state, log)?;
            }
            // Select series
            Key::Up | Key::Down => {
                self.selected_series = match key {
                    Key::Up => self.selected_series.saturating_sub(1),
                    Key::Down if self.selected_series < self.series.len().saturating_sub(1) => {
                        self.selected_series + 1
                    }
                    _ => self.selected_series,
                };
            }
            _ => (),
        }

        Ok(())
    }

    fn process_input_dialog_key(
        &mut self,
        state: &mut CommonState,
        log: &mut StatusLog,
        key: Key,
    ) -> Result<()> {
        match &mut self.status_bar_state {
            StatusBarState::Log => Ok(()),
            StatusBarState::CommandPrompt(prompt) => {
                use component::command_prompt::PromptResult;

                match prompt.process_key(key) {
                    Ok(PromptResult::Command(command)) => {
                        self.status_bar_state.reset();
                        self.last_used_command = Some(command.clone());
                        self.process_command(command, state, log)
                    }
                    Ok(PromptResult::Done) => {
                        self.status_bar_state.reset();
                        Ok(())
                    }
                    Ok(PromptResult::NotDone) => Ok(()),
                    // We need to set the status bar state back before propagating errors,
                    // otherwise we'll be stuck in the prompt
                    Err(err) => {
                        self.status_bar_state.reset();
                        Err(err)
                    }
                }
            }
        }
    }

    fn process_command(
        &mut self,
        command: Command,
        cstate: &mut CommonState,
        log: &mut StatusLog,
    ) -> Result<()> {
        let series = &mut cur_series_mut!(self).inner;
        let remote = cstate.remote.as_ref();

        match command {
            Command::SyncFromRemote => {
                log.capture_status("Syncing entry from remote", || {
                    series.force_sync_changes_from_remote(remote)
                });

                Ok(())
            }
            Command::SyncToRemote => {
                log.capture_status("Syncing entry to remote", || {
                    series.force_sync_changes_to_remote(remote)
                });

                Ok(())
            }
            Command::Status(status) => {
                series.entry.set_status(status, &cstate.config);

                log.capture_status(format!("Setting series status to \"{}\"", status), || {
                    series.sync_changes_to_remote(remote)
                });

                Ok(())
            }
            Command::Progress(direction) => {
                use component::command_prompt::ProgressDirection;

                match direction {
                    ProgressDirection::Forwards => {
                        log.capture_status("Forcing forward watch progress", || {
                            series.episode_completed(remote, &cstate.config)
                        });
                    }
                    ProgressDirection::Backwards => {
                        log.capture_status("Forcing backwards watch progress", || {
                            series.episode_regressed(remote, &cstate.config)
                        });
                    }
                }

                Ok(())
            }
            Command::Score(raw_score) => {
                let score = match cstate.remote.parse_score(&raw_score) {
                    Some(score) if score == 0 => None,
                    Some(score) => Some(score),
                    None => {
                        log.push(LogItem::failed("Parsing score", None));
                        return Ok(());
                    }
                };

                series.entry.set_score(score);

                log.capture_status("Setting score", || {
                    series.sync_changes_to_remote(cstate.remote.as_ref())
                });

                Ok(())
            }
            Command::PlayerArgs(args) => {
                log.capture_status("Saving player args for series", || {
                    series.player_args = args;
                    series.save()
                });

                Ok(())
            }
            Command::LoginToken(token) => {
                use anime::remote::anilist::AniList;
                use anime::remote::AccessToken;

                log.capture_status("Setting user access token", || {
                    let token = AccessToken::encode(token);
                    token.save()?;
                    cstate.remote = Box::new(AniList::login(token)?);
                    Ok(())
                });

                Ok(())
            }
        }
    }

    fn process_tick(&mut self, state: &CommonState, log: &mut StatusLog) -> Result<()> {
        cur_series_mut!(self).process_tick(state, log)
    }

    fn is_idle(&self) -> bool {
        match self.cur_series() {
            Some(cur_series) => cur_series.watch_state == WatchState::Idle,
            None => true,
        }
    }
}

enum StatusBarState<'a> {
    Log,
    CommandPrompt(CommandPrompt<'a>),
}

impl<'a> StatusBarState<'a> {
    fn set_to_command_prompt(&mut self) {
        *self = StatusBarState::CommandPrompt(CommandPrompt::new());
    }

    fn reset(&mut self) {
        *self = StatusBarState::default();
    }

    fn in_input_dialog(&self) -> bool {
        match self {
            StatusBarState::Log => false,
            StatusBarState::CommandPrompt(_) => true,
        }
    }
}

impl<'a> Default for StatusBarState<'a> {
    fn default() -> StatusBarState<'a> {
        StatusBarState::Log
    }
}

#[derive(Debug)]
struct SeriesState {
    inner: Series,
    watch_state: WatchState,
}

impl SeriesState {
    fn new(inner: Series) -> SeriesState {
        SeriesState {
            inner,
            watch_state: WatchState::Idle,
        }
    }

    fn play_next_episode_async(&mut self, state: &CommonState) -> Result<()> {
        let remote = state.remote.as_ref();
        let config = &state.config;

        self.inner.begin_watching(remote, config)?;
        let next_ep = self.inner.entry.watched_eps() + 1;

        let child = self
            .inner
            .play_episode_cmd(next_ep, &state.config)?
            .spawn()
            .context(err::FailedToPlayEpisode { episode: next_ep })?;

        let progress_time = {
            let secs_must_watch =
                (self.inner.info.episode_length as f32 * config.episode.pcnt_must_watch) * 60.0;
            let time_must_watch = Duration::seconds(secs_must_watch as i64);

            Utc::now() + time_must_watch
        };

        self.watch_state = WatchState::Watching(progress_time, child);

        Ok(())
    }

    fn process_tick(&mut self, state: &CommonState, log: &mut StatusLog) -> Result<()> {
        match &mut self.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, child) => {
                let status = match child.try_wait().context(err::IO)? {
                    Some(status) => status,
                    None => return Ok(()),
                };

                // The watch state should be set to idle immediately to avoid a potential infinite loop.
                let progress_time = match mem::replace(&mut self.watch_state, WatchState::Idle) {
                    WatchState::Watching(progress_time, _) => progress_time,
                    WatchState::Idle => unreachable!(),
                };

                if !status.success() {
                    log.push("Player did not exit properly");
                    return Ok(());
                }

                if Utc::now() >= progress_time {
                    log.capture_status("Marking episode as completed", || {
                        self.inner
                            .episode_completed(state.remote.as_ref(), &state.config)
                    });
                } else {
                    log.push("Not marking episode as completed");
                }
            }
        }

        Ok(())
    }
}

type ProgressTime = DateTime<Utc>;

#[derive(Debug)]
enum WatchState {
    Idle,
    Watching(ProgressTime, process::Child),
}

impl PartialEq for WatchState {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (WatchState::Idle, WatchState::Idle) => true,
            (WatchState::Watching(_, _), WatchState::Watching(_, _)) => true,
            _ => false,
        }
    }
}
<commit_msg>anup: fetch the current series on-demand when processing commands<commit_after>mod component;
mod ui;

use crate::config::Config;
use crate::err::{self, Result};
use crate::file::SaveFile;
use crate::series::{SavedSeries, Series};
use anime::remote::RemoteService;
use chrono::{DateTime, Duration, Utc};
use clap::ArgMatches;
use component::command_prompt::{Command, CommandPrompt};
use component::log::{LogItem, StatusLog};
use snafu::ResultExt;
use std::mem;
use std::process;
use termion::event::Key;
use ui::{Event, Events, UI};

pub fn run(args: &ArgMatches) -> Result<()> {
    let mut ui = UI::init()?;

    let mut cstate = {
        let config = Config::load_or_create()?;
        let remote = init_remote(args, &mut ui.status_log);

        CommonState { config, remote }
    };

    let mut ui_state = init_ui_state(&cstate, args)?;
    let events = Events::new(Duration::seconds(1));

    loop {
        ui.draw(&ui_state, cstate.remote.as_ref())?;

        match events.next()? {
            Event::Input(key) => match key {
                // Exit
                Key::Char('q') if !ui_state.status_bar_state.in_input_dialog() => {
                    // Prevent ruining the user's terminal
                    ui.clear().ok();
                    break Ok(());
                }
                key => match ui_state.process_key(&mut cstate, &mut ui.status_log, key) {
                    Ok(_) => (),
                    Err(err) => {
                        ui.status_log.push(LogItem::failed("Processing key", err));
                    }
                },
            },
            Event::Tick => match ui_state.process_tick(&cstate, &mut ui.status_log) {
                Ok(_) => (),
                Err(err) => ui.status_log.push(LogItem::failed("Processing tick", err)),
            },
        }
    }
}

fn init_remote(args: &ArgMatches, log: &mut StatusLog) -> Box<dyn RemoteService> {
    use anime::remote::anilist;
    use anime::remote::offline::Offline;

    match crate::init_remote(args, true) {
        Ok(remote) => remote,
        Err(err) => {
            match err {
                err::Error::NeedAniListToken => {
                    log.push(format!(
                        "No access token found. Go to {} \
                         and set your token with the 'token' command",
                        anilist::auth_url(crate::ANILIST_CLIENT_ID)
                    ));
                }
                _ => {
                    log.push(LogItem::failed("Logging in", err));
                    log.push(format!(
                        "If you need a new token, go to {} \
                         and set it with the 'token' command",
                        anilist::auth_url(crate::ANILIST_CLIENT_ID)
                    ));
                }
            }

            log.push("Continuing in offline mode");

            Box::new(Offline::new())
        }
    }
}

/// Items that are not tied to the UI and are commonly used together.
struct CommonState {
    config: Config,
    remote: Box<dyn RemoteService>,
}

fn init_ui_state<'a>(cstate: &CommonState, args: &ArgMatches) -> Result<UIState<'a>> {
    let mut saved_series = SavedSeries::load_or_default()?;
    let series = init_series_list(&cstate, args, &mut saved_series)?;

    let selected_series = match saved_series.last_watched_id {
        Some(id) => series
            .iter()
            .position(|series| series.info.id == id)
            .unwrap_or(0),
        None => 0,
    };

    let series = series.into_iter().map(SeriesState::new).collect();

    Ok(UIState {
        series,
        selected_series,
        saved_series,
        status_bar_state: StatusBarState::default(),
        last_used_command: None,
    })
}

fn init_series_list(
    cstate: &CommonState,
    args: &ArgMatches,
    saved_series: &mut SavedSeries,
) -> Result<Vec<Series>> {
    let mut series = saved_series.load_all_series_and_validate()?;

    // If the user specified a series, we'll need to check to see if we
    // already have it or fetch & save it otherwise.
    let desired_series = match args.value_of("series") {
        Some(desired_series) => desired_series,
        None => return Ok(series),
    };

    if saved_series.contains(desired_series) {
        return Ok(series);
    }

    let new_series = saved_series.insert_and_save_from_args_and_remote(
        args,
        desired_series,
        &cstate.config,
        cstate.remote.as_ref(),
    )?;

    series.push(new_series);
    Ok(series)
}

/// Current state of the UI.
pub struct UIState<'a> {
    series: Vec<SeriesState>,
    selected_series: usize,
    saved_series: SavedSeries,
    status_bar_state: StatusBarState<'a>,
    last_used_command: Option<Command>,
}

macro_rules! cur_series {
    ($struct:ident) => {
        match $struct.cur_series() {
            Some(value) => value,
            None => return Ok(()),
        }
    };
}

macro_rules! cur_series_mut {
    ($struct:ident) => {
        match $struct.cur_series_mut() {
            Some(value) => value,
            None => return Ok(()),
        }
    };
}

impl<'a> UIState<'a> {
    fn cur_series(&self) -> Option<&SeriesState> {
        self.series.get(self.selected_series)
    }

    fn cur_series_mut(&mut self) -> Option<&mut SeriesState> {
        self.series.get_mut(self.selected_series)
    }

    fn process_key(
        &mut self,
        state: &mut CommonState,
        log: &mut StatusLog,
        key: Key,
    ) -> Result<()> {
        if !self.is_idle() {
            return Ok(());
        }

        if self.status_bar_state.in_input_dialog() {
            return self.process_input_dialog_key(state, log, key);
        }

        match key {
            // Play next episode
            Key::Char(ch) if ch == state.config.tui.keys.play_next_episode => {
                let last_watched_changed = {
                    let id = cur_series!(self).inner.info.id;
                    self.saved_series.set_last_watched(id)
                };

                if last_watched_changed {
                    log.capture_status("Marking series as the last watched one", || {
                        self.saved_series.save()
                    });
                }

                log.capture_status("Playing next episode", || {
                    cur_series_mut!(self).play_next_episode_async(&state)
                });
            }
            // Command prompt
            Key::Char(':') => {
                self.status_bar_state.set_to_command_prompt();
            }
            // Run last used command
            Key::Char(ch) if ch == state.config.tui.keys.run_last_command => {
                let cmd = match &self.last_used_command {
                    Some(cmd) => cmd.clone(),
                    None => return Ok(()),
                };

                self.process_command(cmd, state, log)?;
            }
            // Select series
            Key::Up | Key::Down => {
                self.selected_series = match key {
                    Key::Up => self.selected_series.saturating_sub(1),
                    Key::Down if self.selected_series < self.series.len().saturating_sub(1) => {
                        self.selected_series + 1
                    }
                    _ => self.selected_series,
                };
            }
            _ => (),
        }

        Ok(())
    }

    fn process_input_dialog_key(
        &mut self,
        state: &mut CommonState,
        log: &mut StatusLog,
        key: Key,
    ) -> Result<()> {
        match &mut self.status_bar_state {
            StatusBarState::Log => Ok(()),
            StatusBarState::CommandPrompt(prompt) => {
                use component::command_prompt::PromptResult;

                match prompt.process_key(key) {
                    Ok(PromptResult::Command(command)) => {
                        self.status_bar_state.reset();
                        self.last_used_command = Some(command.clone());
                        self.process_command(command, state, log)
                    }
                    Ok(PromptResult::Done) => {
                        self.status_bar_state.reset();
                        Ok(())
                    }
                    Ok(PromptResult::NotDone) => Ok(()),
                    // We need to set the status bar state back before propagating errors,
                    // otherwise we'll be stuck in the prompt
                    Err(err) => {
                        self.status_bar_state.reset();
                        Err(err)
                    }
                }
            }
        }
    }

    fn process_command(
        &mut self,
        command: Command,
        cstate: &mut CommonState,
        log: &mut StatusLog,
    ) -> Result<()> {
        match command {
            Command::SyncFromRemote => {
                let series = &mut cur_series_mut!(self).inner;
                let remote = cstate.remote.as_ref();

                log.capture_status("Syncing entry from remote", || {
                    series.force_sync_changes_from_remote(remote)
                });

                Ok(())
            }
            Command::SyncToRemote => {
                let series = &mut cur_series_mut!(self).inner;
                let remote = cstate.remote.as_ref();

                log.capture_status("Syncing entry to remote", || {
                    series.force_sync_changes_to_remote(remote)
                });

                Ok(())
            }
            Command::Status(status) => {
                let series = &mut cur_series_mut!(self).inner;
                let remote = cstate.remote.as_ref();

                series.entry.set_status(status, &cstate.config);

                log.capture_status(format!("Setting series status to \"{}\"", status), || {
                    series.sync_changes_to_remote(remote)
                });

                Ok(())
            }
            Command::Progress(direction) => {
                use component::command_prompt::ProgressDirection;

                let series = &mut cur_series_mut!(self).inner;
                let remote = cstate.remote.as_ref();

                match direction {
                    ProgressDirection::Forwards => {
                        log.capture_status("Forcing forward watch progress", || {
                            series.episode_completed(remote, &cstate.config)
                        });
                    }
                    ProgressDirection::Backwards => {
                        log.capture_status("Forcing backwards watch progress", || {
                            series.episode_regressed(remote, &cstate.config)
                        });
                    }
                }

                Ok(())
            }
            Command::Score(raw_score) => {
                let series = &mut cur_series_mut!(self).inner;
                let remote = cstate.remote.as_ref();

                let score = match cstate.remote.parse_score(&raw_score) {
                    Some(score) if score == 0 => None,
                    Some(score) => Some(score),
                    None => {
                        log.push(LogItem::failed("Parsing score", None));
                        return Ok(());
                    }
                };

                log.capture_status("Setting score", || {
                    series.entry.set_score(score);
                    series.sync_changes_to_remote(remote)
                });

                Ok(())
            }
            Command::PlayerArgs(args) => {
                let series = &mut cur_series_mut!(self).inner;

                log.capture_status("Saving player args for series", || {
                    series.player_args = args;
                    series.save()
                });

                Ok(())
            }
            Command::LoginToken(token) => {
                use anime::remote::anilist::AniList;
                use anime::remote::AccessToken;

                log.capture_status("Setting user access token", || {
                    let token = AccessToken::encode(token);
                    token.save()?;
                    cstate.remote = Box::new(AniList::login(token)?);
                    Ok(())
                });

                Ok(())
            }
        }
    }

    fn process_tick(&mut self, state: &CommonState, log: &mut StatusLog) -> Result<()> {
        cur_series_mut!(self).process_tick(state, log)
    }

    fn is_idle(&self) -> bool {
        match self.cur_series() {
            Some(cur_series) => cur_series.watch_state == WatchState::Idle,
            None => true,
        }
    }
}

enum StatusBarState<'a> {
    Log,
    CommandPrompt(CommandPrompt<'a>),
}

impl<'a> StatusBarState<'a> {
    fn set_to_command_prompt(&mut self) {
        *self = StatusBarState::CommandPrompt(CommandPrompt::new());
    }

    fn reset(&mut self) {
        *self = StatusBarState::default();
    }

    fn in_input_dialog(&self) -> bool {
        match self {
            StatusBarState::Log => false,
            StatusBarState::CommandPrompt(_) => true,
        }
    }
}

impl<'a> Default for StatusBarState<'a> {
    fn default() -> StatusBarState<'a> {
        StatusBarState::Log
    }
}

#[derive(Debug)]
struct SeriesState {
    inner: Series,
    watch_state: WatchState,
}

impl SeriesState {
    fn new(inner: Series) -> SeriesState {
        SeriesState {
            inner,
            watch_state: WatchState::Idle,
        }
    }

    fn play_next_episode_async(&mut self, state: &CommonState) -> Result<()> {
        let remote = state.remote.as_ref();
        let config = &state.config;

        self.inner.begin_watching(remote, config)?;
        let next_ep = self.inner.entry.watched_eps() + 1;

        let child = self
            .inner
            .play_episode_cmd(next_ep, &state.config)?
            .spawn()
            .context(err::FailedToPlayEpisode { episode: next_ep })?;

        let progress_time = {
            let secs_must_watch =
                (self.inner.info.episode_length as f32 * config.episode.pcnt_must_watch) * 60.0;
            let time_must_watch = Duration::seconds(secs_must_watch as i64);

            Utc::now() + time_must_watch
        };

        self.watch_state = WatchState::Watching(progress_time, child);

        Ok(())
    }

    fn process_tick(&mut self, state: &CommonState, log: &mut StatusLog) -> Result<()> {
        match &mut self.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, child) => {
                let status = match child.try_wait().context(err::IO)? {
                    Some(status) => status,
                    None => return Ok(()),
                };

                // The watch state should be set to idle immediately to avoid a potential infinite loop.
                let progress_time = match mem::replace(&mut self.watch_state, WatchState::Idle) {
                    WatchState::Watching(progress_time, _) => progress_time,
                    WatchState::Idle => unreachable!(),
                };

                if !status.success() {
                    log.push("Player did not exit properly");
                    return Ok(());
                }

                if Utc::now() >= progress_time {
                    log.capture_status("Marking episode as completed", || {
                        self.inner
                            .episode_completed(state.remote.as_ref(), &state.config)
                    });
                } else {
                    log.push("Not marking episode as completed");
                }
            }
        }

        Ok(())
    }
}

type ProgressTime = DateTime<Utc>;

#[derive(Debug)]
enum WatchState {
    Idle,
    Watching(ProgressTime, process::Child),
}

impl PartialEq for WatchState {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (WatchState::Idle, WatchState::Idle) => true,
            (WatchState::Watching(_, _), WatchState::Watching(_, _)) => true,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>use super::*;
use Allocator;

use core::ptr;

extern "C" {
    /// We need this to allocate aligned memory for our heap.
    fn memalign(alignment: usize, size: usize) -> *mut u8;

    // Release our memory.
    fn free(ptr: *mut u8);
}

const HEAP_ALIGN: usize = 4096;
const HEAP_SIZE: usize = 256;

#[test]
fn test_allocation_size_and_order() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let mut heap
            = BuddyHeapAllocator::new( mem
                                     , &mut free_lists
                                     , HEAP_SIZE );

        // TEST NEEDED: Can't align beyond MIN_HEAP_ALIGN.

        // Can't align beyond heap_size.
        assert_eq!(None, heap.alloc_size(256, 256*2));

        // Simple allocations just round up to next block size.
        assert_eq!(Some(16), heap.alloc_size(0, 1));
        assert_eq!(Some(16), heap.alloc_size(1, 1));
        assert_eq!(Some(16), heap.alloc_size(16, 1));
        assert_eq!(Some(32), heap.alloc_size(17, 1));
        assert_eq!(Some(32), heap.alloc_size(32, 32));
        assert_eq!(Some(256), heap.alloc_size(256, 256));

        // Aligned allocations use alignment as block size.
        assert_eq!(Some(64), heap.alloc_size(16, 64));

        // Block orders.
        assert_eq!(Some(0), heap.alloc_order(0, 1));
        assert_eq!(Some(0), heap.alloc_order(1, 1));
        assert_eq!(Some(0), heap.alloc_order(16, 16));
        assert_eq!(Some(1), heap.alloc_order(32, 32));
        assert_eq!(Some(2), heap.alloc_order(64, 64));
        assert_eq!(Some(3), heap.alloc_order(128, 128));
        assert_eq!(Some(4), heap.alloc_order(256, 256));
        assert_eq!(None, heap.alloc_order(512, 512));

        free(mem);
    }
}

#[test]
fn test_get_buddy() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let mut heap
            = BuddyHeapAllocator::new( mem
                                     , &mut free_lists
                                     , HEAP_SIZE );
        let block_16_0 = mem;
        let block_16_1 = mem.offset(16);
        assert_eq!(Some(block_16_1), heap.get_buddy(0, block_16_0));
        assert_eq!(Some(block_16_0), heap.get_buddy(0, block_16_1));

        let block_32_0 = mem;
        let block_32_1 = mem.offset(32);
        assert_eq!(Some(block_32_1), heap.get_buddy(1, block_32_0));
        assert_eq!(Some(block_32_0), heap.get_buddy(1, block_32_1));

        let block_32_2 = mem.offset(64);
        let block_32_3 = mem.offset(96);
        assert_eq!(Some(block_32_3), heap.get_buddy(1, block_32_2));
        assert_eq!(Some(block_32_2), heap.get_buddy(1, block_32_3));

        let block_256_0 = mem;
        assert_eq!(None, heap.get_buddy(4, block_256_0));

        free(mem);
    }
}

#[test]
fn test_alloc_and_dealloc() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let mut heap
            = BuddyHeapAllocator::new( mem
                                     , &mut free_lists
                                     , HEAP_SIZE );

        let block_128_0 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(0)), block_128_0);

        heap.deallocate(block_128_0.unwrap(), 128,128);

        let block_16_0 = heap.allocate(8, 8);
        assert_eq!(Some(mem), block_16_0);

        let bigger_than_heap = heap.allocate(4096, HEAP_SIZE);
        assert_eq!(None, bigger_than_heap);

        let bigger_than_free = heap.allocate(HEAP_SIZE, HEAP_SIZE);
        assert_eq!(None, bigger_than_free);

        let block_16_1 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(16)), block_16_1);

        let block_16_2 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(32)), block_16_2);

        let block_32_2 = heap.allocate(32, 32);
        assert_eq!(Some(mem.offset(64)), block_32_2);

        let block_16_3 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(48)), block_16_3);

        // let too_fragmented = heap.allocate(64, 64);
        // assert_eq!(None, too_fragmented);
        heap.deallocate(block_16_0.unwrap(), 8, 8);
        heap.deallocate(block_16_3.unwrap(), 8, 8);
        heap.deallocate(block_16_1.unwrap(), 8, 8);
        heap.deallocate(block_16_2.unwrap(), 8, 8);

        heap.deallocate(block_32_2.unwrap(), 32, 32);

        let block_128_0 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(0)), block_128_0);

        let block_128_1 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(128)), block_128_1);

        heap.deallocate(block_128_1.unwrap(), 128, 128);
        heap.deallocate(block_128_0.unwrap(), 128, 128);

        // And allocate the whole heap, just to make sure everything
        // got cleaned up correctly.
        let block_256_0 = heap.allocate(256, 256).unwrap();
        assert_eq!(mem.offset(0), block_256_0);

        free(mem);
    }
}
<commit_msg>[alloc] remove unused mut from tests<commit_after>use super::*;
use Allocator;

use core::ptr;

extern "C" {
    /// We need this to allocate aligned memory for our heap.
    fn memalign(alignment: usize, size: usize) -> *mut u8;

    // Release our memory.
    fn free(ptr: *mut u8);
}

const HEAP_ALIGN: usize = 4096;
const HEAP_SIZE: usize = 256;

#[test]
fn test_allocation_size_and_order() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let heap = BuddyHeapAllocator::new( mem
                                          , &mut free_lists
                                          , HEAP_SIZE );

        // TEST NEEDED: Can't align beyond MIN_HEAP_ALIGN.

        // Can't align beyond heap_size.
        assert_eq!(None, heap.alloc_size(256, 256*2));

        // Simple allocations just round up to next block size.
        assert_eq!(Some(16), heap.alloc_size(0, 1));
        assert_eq!(Some(16), heap.alloc_size(1, 1));
        assert_eq!(Some(16), heap.alloc_size(16, 1));
        assert_eq!(Some(32), heap.alloc_size(17, 1));
        assert_eq!(Some(32), heap.alloc_size(32, 32));
        assert_eq!(Some(256), heap.alloc_size(256, 256));

        // Aligned allocations use alignment as block size.
        assert_eq!(Some(64), heap.alloc_size(16, 64));

        // Block orders.
        assert_eq!(Some(0), heap.alloc_order(0, 1));
        assert_eq!(Some(0), heap.alloc_order(1, 1));
        assert_eq!(Some(0), heap.alloc_order(16, 16));
        assert_eq!(Some(1), heap.alloc_order(32, 32));
        assert_eq!(Some(2), heap.alloc_order(64, 64));
        assert_eq!(Some(3), heap.alloc_order(128, 128));
        assert_eq!(Some(4), heap.alloc_order(256, 256));
        assert_eq!(None, heap.alloc_order(512, 512));

        free(mem);
    }
}

#[test]
fn test_get_buddy() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let heap = BuddyHeapAllocator::new( mem
                                          , &mut free_lists
                                          , HEAP_SIZE );
        let block_16_0 = mem;
        let block_16_1 = mem.offset(16);
        assert_eq!(Some(block_16_1), heap.get_buddy(0, block_16_0));
        assert_eq!(Some(block_16_0), heap.get_buddy(0, block_16_1));

        let block_32_0 = mem;
        let block_32_1 = mem.offset(32);
        assert_eq!(Some(block_32_1), heap.get_buddy(1, block_32_0));
        assert_eq!(Some(block_32_0), heap.get_buddy(1, block_32_1));

        let block_32_2 = mem.offset(64);
        let block_32_3 = mem.offset(96);
        assert_eq!(Some(block_32_3), heap.get_buddy(1, block_32_2));
        assert_eq!(Some(block_32_2), heap.get_buddy(1, block_32_3));

        let block_256_0 = mem;
        assert_eq!(None, heap.get_buddy(4, block_256_0));

        free(mem);
    }
}

#[test]
fn test_alloc_and_dealloc() {
    unsafe {
        let mem = memalign(HEAP_ALIGN, HEAP_SIZE);
        let mut free_lists: [FreeList; 5]
            = [ FreeList::new(), FreeList::new()
              , FreeList::new(), FreeList::new()
              , FreeList::new()
              ];
        let heap = BuddyHeapAllocator::new( mem
                                          , &mut free_lists
                                          , HEAP_SIZE );

        let block_128_0 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(0)), block_128_0);

        heap.deallocate(block_128_0.unwrap(), 128,128);

        let block_16_0 = heap.allocate(8, 8);
        assert_eq!(Some(mem), block_16_0);

        let bigger_than_heap = heap.allocate(4096, HEAP_SIZE);
        assert_eq!(None, bigger_than_heap);

        let bigger_than_free = heap.allocate(HEAP_SIZE, HEAP_SIZE);
        assert_eq!(None, bigger_than_free);

        let block_16_1 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(16)), block_16_1);

        let block_16_2 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(32)), block_16_2);

        let block_32_2 = heap.allocate(32, 32);
        assert_eq!(Some(mem.offset(64)), block_32_2);

        let block_16_3 = heap.allocate(8, 8);
        assert_eq!(Some(mem.offset(48)), block_16_3);

        // let too_fragmented = heap.allocate(64, 64);
        // assert_eq!(None, too_fragmented);
        heap.deallocate(block_16_0.unwrap(), 8, 8);
        heap.deallocate(block_16_3.unwrap(), 8, 8);
        heap.deallocate(block_16_1.unwrap(), 8, 8);
        heap.deallocate(block_16_2.unwrap(), 8, 8);

        heap.deallocate(block_32_2.unwrap(), 32, 32);

        let block_128_0 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(0)), block_128_0);

        let block_128_1 = heap.allocate(128, 128);
        assert_eq!(Some(mem.offset(128)), block_128_1);

        heap.deallocate(block_128_1.unwrap(), 128, 128);
        heap.deallocate(block_128_0.unwrap(), 128, 128);

        // And allocate the whole heap, just to make sure everything
        // got cleaned up correctly.
        let block_256_0 = heap.allocate(256, 256).unwrap();
        assert_eq!(mem.offset(0), block_256_0);

        free(mem);
    }
}
<|endoftext|>


<commit_before>use std::sync::{Mutex, Arc};
use std::net::{SocketAddr, ToSocketAddrs};
use std::f64::consts::PI;
use std::collections::VecDeque;
use opengl_graphics::GlGraphics;
use piston::input::{RenderArgs, UpdateArgs};

use std::{thread, time};
use std::net::TcpStream;
use network::{Command, Message};

use bincode::{serialize_into, deserialize_from, Infinite};

use state::{WorldState, GameState};
use shapes::Unit;

pub struct NetworkClient {
    pub world_state: Arc<Mutex<WorldState>>,
    server_addr: SocketAddr,
    stream: Option<TcpStream>,
    commands: Arc<Mutex<VecDeque<Command>>>,
}

impl NetworkClient {
    pub fn new<T: ToSocketAddrs>(server_addrs: T, world_state:
                                 Arc<Mutex<WorldState>>,
                                 commands: Arc<Mutex<VecDeque<Command>>>) -> NetworkClient {
        let server_addr = server_addrs.to_socket_addrs().unwrap().next().unwrap();
        NetworkClient {
            world_state: world_state,
            server_addr: server_addr,
            stream: None,
            commands: commands,
        }
    }

    // todo: Maybe return client_id here? Would allow the application to reconnect...
    pub fn connect(&mut self) {
        let mut stream = TcpStream::connect(self.server_addr).unwrap();
        serialize_into(&mut stream, &Message::ClientHello, Infinite).unwrap();
        let server_hello = deserialize_from(&mut stream, Infinite);

        self.stream = Some(stream);
        if let Ok(Message::ServerHello(_, world_state)) = server_hello {
            let mut world_state_lock = self.world_state.lock().unwrap();
            *world_state_lock = world_state;
        } else {
            panic!("Could not connect to server");
        }
    }

    pub fn update(self) {
        let stream = self.stream.expect("Stream not here :(");
        let mut command_stream = stream.try_clone().unwrap();
        let commands = self.commands.clone();

        // Command sender loop
        thread::spawn(move || {
            loop {
                let command = {
                    let mut commands = commands.lock().unwrap();
                    commands.pop_front()
                };
                command.map(|cmd| {
                    println!("Got command: {:?}", cmd);
                    //let cmd = Message::Command(Command::Move(0.into(), [100f64, 100f64]));
                    serialize_into(&mut command_stream, &Message::Command(cmd), Infinite)
                        .map_err(|e|println!("Sending command failed: {}", e));
                });
                thread::sleep(time::Duration::from_millis(10));
            }
        });

        let mut game_state_stream = stream.try_clone().unwrap();
        let world_state = self.world_state.clone();
        thread::spawn(move || {
            loop {
                let game_state = deserialize_from(&mut game_state_stream,
                                                  Infinite);
                match game_state {
                    Ok(game) => {
                        //println!("{:?}", game);
                        let mut world_state_lock = world_state.lock().unwrap();
                        world_state_lock.game = game;

                    }
                    Err(e) => {
                        println!("{:?}", e);
                        thread::sleep(time::Duration::from_millis(200));
                    }
                }
            }
        });
    }
}

pub struct App {
    pub gl: GlGraphics, // OpenGL drawing backend.
    pub world_state: Arc<Mutex<WorldState>>,
    pub units: Vec<Unit>,
    pub commands: Arc<Mutex<VecDeque<Command>>>,
}

impl App {
    pub fn new(gl: GlGraphics) -> App {
        App {
            gl: gl,
            world_state: Arc::new(Mutex::new(WorldState::new(0, 0))),
            units: vec![],
            commands: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    pub fn select(&mut self, position: [f64;2]) {
        for u in &mut self.units {
            u.selected = u.is_hit(position);
        };
    }

    pub fn render(&mut self, args: &RenderArgs) {
        use graphics::{polygon, clear};
        use graphics::Transformed;
        use graphics::types::Polygon;

        const BLACK:  [f32; 4] = [0.0, 0.0,  0.0,  1.0];
        const YELLOW: [f32; 4] = [1.0, 1.0,  0.22, 1.0];
        const ORANGE: [f32; 4] = [1.0, 0.61, 0.22, 1.0];

        const FRONT_THICKNESS: f64 = 5.0;

        let units = &self.units;

        self.gl.draw(args.viewport(), |c, gl| {

            // Clear the screen.
            clear(BLACK, gl);

            for s in units.iter() {

                // Create a triangle polygon. The initial orientation is facing east.
                let triangle: Polygon = &s.get_shape();

                // Create a border on the front of the polygon. This is a trapezoid.
                // Because the angle of the trapezoid side is 22.5°, we know that `dx` is always `2 * dy`.
                let front: Polygon = &[
                    [s.size, s.size],                                           // Top right
                    [s.size, 0.0],                                                 // Bottom right
                    [s.size - FRONT_THICKNESS, FRONT_THICKNESS / 2.0],             // Bottom left
                    [s.size - FRONT_THICKNESS, s.size - FRONT_THICKNESS / 2.0], // Top left
                ];

                // Rotate the front to match the unit
                let transform_front = c.transform.trans(s.position[0], s.position[1])
                    .rot_rad(s.rotation)
                    .trans(-25.0, -25.0);

                // We don't need to apply any transformation to the units
                let transform_triangle = c.transform;

                // Draw the unit ORANGE if selected
                if s.selected {
                    polygon(ORANGE, triangle, transform_triangle, gl);
                    polygon(YELLOW, front, transform_front, gl);
                } else {
                    polygon(YELLOW, triangle, transform_triangle, gl);
                    polygon(ORANGE, front, transform_front, gl);
                }

            }
        });
    }

    pub fn update(&mut self, args: &UpdateArgs) {
        /*
        for s in &mut self.units {
            let diff = [s.target[0]-s.position[0], s.target[1]-s.position[1]];
            s.position[0] += diff[0]/2.0*args.dt;
            s.position[1] += diff[1]/2.0*args.dt;
        }
        */
    }

    pub fn move_selected(&mut self, position: [f64;2]) {
        for i in 0..self.units.len() {
            let s = &mut self.units[i];
            if s.selected {
                s.target = position;
                let dx = position[0] - s.position[0];
                let dy = position[1] - s.position[1];
                if dx.is_sign_negative() {
                    s.rotation = (dy / dx).atan() + PI;
                } else {
                    s.rotation = (dy / dx).atan();
                }
                let id = i as u32;
                let mut commands = self.commands.lock().unwrap();
                commands.push_back(Command::Move(id.into(), position));
                println!("dx: {}, dy: {}, new rotation: {}", dx, dy, s.rotation);
            }
        }
    }
}
<commit_msg>Remove unused use statement<commit_after>use std::sync::{Mutex, Arc};
use std::net::{SocketAddr, ToSocketAddrs};
use std::f64::consts::PI;
use std::collections::VecDeque;
use opengl_graphics::GlGraphics;
use piston::input::{RenderArgs, UpdateArgs};

use std::{thread, time};
use std::net::TcpStream;
use network::{Command, Message};

use bincode::{serialize_into, deserialize_from, Infinite};

use state::WorldState;
use shapes::Unit;

pub struct NetworkClient {
    pub world_state: Arc<Mutex<WorldState>>,
    server_addr: SocketAddr,
    stream: Option<TcpStream>,
    commands: Arc<Mutex<VecDeque<Command>>>,
}

impl NetworkClient {
    pub fn new<T: ToSocketAddrs>(server_addrs: T, world_state:
                                 Arc<Mutex<WorldState>>,
                                 commands: Arc<Mutex<VecDeque<Command>>>) -> NetworkClient {
        let server_addr = server_addrs.to_socket_addrs().unwrap().next().unwrap();
        NetworkClient {
            world_state: world_state,
            server_addr: server_addr,
            stream: None,
            commands: commands,
        }
    }

    // todo: Maybe return client_id here? Would allow the application to reconnect...
    pub fn connect(&mut self) {
        let mut stream = TcpStream::connect(self.server_addr).unwrap();
        serialize_into(&mut stream, &Message::ClientHello, Infinite).unwrap();
        let server_hello = deserialize_from(&mut stream, Infinite);

        self.stream = Some(stream);
        if let Ok(Message::ServerHello(_, world_state)) = server_hello {
            let mut world_state_lock = self.world_state.lock().unwrap();
            *world_state_lock = world_state;
        } else {
            panic!("Could not connect to server");
        }
    }

    pub fn update(self) {
        let stream = self.stream.expect("Stream not here :(");
        let mut command_stream = stream.try_clone().unwrap();
        let commands = self.commands.clone();

        // Command sender loop
        thread::spawn(move || {
            loop {
                let command = {
                    let mut commands = commands.lock().unwrap();
                    commands.pop_front()
                };
                command.map(|cmd| {
                    println!("Got command: {:?}", cmd);
                    //let cmd = Message::Command(Command::Move(0.into(), [100f64, 100f64]));
                    serialize_into(&mut command_stream, &Message::Command(cmd), Infinite)
                        .map_err(|e|println!("Sending command failed: {}", e));
                });
                thread::sleep(time::Duration::from_millis(10));
            }
        });

        let mut game_state_stream = stream.try_clone().unwrap();
        let world_state = self.world_state.clone();
        thread::spawn(move || {
            loop {
                let game_state = deserialize_from(&mut game_state_stream,
                                                  Infinite);
                match game_state {
                    Ok(game) => {
                        //println!("{:?}", game);
                        let mut world_state_lock = world_state.lock().unwrap();
                        world_state_lock.game = game;

                    }
                    Err(e) => {
                        println!("{:?}", e);
                        thread::sleep(time::Duration::from_millis(200));
                    }
                }
            }
        });
    }
}

pub struct App {
    pub gl: GlGraphics, // OpenGL drawing backend.
    pub world_state: Arc<Mutex<WorldState>>,
    pub units: Vec<Unit>,
    pub commands: Arc<Mutex<VecDeque<Command>>>,
}

impl App {
    pub fn new(gl: GlGraphics) -> App {
        App {
            gl: gl,
            world_state: Arc::new(Mutex::new(WorldState::new(0, 0))),
            units: vec![],
            commands: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    pub fn select(&mut self, position: [f64;2]) {
        for u in &mut self.units {
            u.selected = u.is_hit(position);
        };
    }

    pub fn render(&mut self, args: &RenderArgs) {
        use graphics::{polygon, clear};
        use graphics::Transformed;
        use graphics::types::Polygon;

        const BLACK:  [f32; 4] = [0.0, 0.0,  0.0,  1.0];
        const YELLOW: [f32; 4] = [1.0, 1.0,  0.22, 1.0];
        const ORANGE: [f32; 4] = [1.0, 0.61, 0.22, 1.0];

        const FRONT_THICKNESS: f64 = 5.0;

        let units = &self.units;

        self.gl.draw(args.viewport(), |c, gl| {

            // Clear the screen.
            clear(BLACK, gl);

            for s in units.iter() {

                // Create a triangle polygon. The initial orientation is facing east.
                let triangle: Polygon = &s.get_shape();

                // Create a border on the front of the polygon. This is a trapezoid.
                // Because the angle of the trapezoid side is 22.5°, we know that `dx` is always `2 * dy`.
                let front: Polygon = &[
                    [s.size, s.size],                                           // Top right
                    [s.size, 0.0],                                                 // Bottom right
                    [s.size - FRONT_THICKNESS, FRONT_THICKNESS / 2.0],             // Bottom left
                    [s.size - FRONT_THICKNESS, s.size - FRONT_THICKNESS / 2.0], // Top left
                ];

                // Rotate the front to match the unit
                let transform_front = c.transform.trans(s.position[0], s.position[1])
                    .rot_rad(s.rotation)
                    .trans(-25.0, -25.0);

                // We don't need to apply any transformation to the units
                let transform_triangle = c.transform;

                // Draw the unit ORANGE if selected
                if s.selected {
                    polygon(ORANGE, triangle, transform_triangle, gl);
                    polygon(YELLOW, front, transform_front, gl);
                } else {
                    polygon(YELLOW, triangle, transform_triangle, gl);
                    polygon(ORANGE, front, transform_front, gl);
                }

            }
        });
    }

    pub fn update(&mut self, args: &UpdateArgs) {
        /*
        for s in &mut self.units {
            let diff = [s.target[0]-s.position[0], s.target[1]-s.position[1]];
            s.position[0] += diff[0]/2.0*args.dt;
            s.position[1] += diff[1]/2.0*args.dt;
        }
        */
    }

    pub fn move_selected(&mut self, position: [f64;2]) {
        for i in 0..self.units.len() {
            let s = &mut self.units[i];
            if s.selected {
                s.target = position;
                let dx = position[0] - s.position[0];
                let dy = position[1] - s.position[1];
                if dx.is_sign_negative() {
                    s.rotation = (dy / dx).atan() + PI;
                } else {
                    s.rotation = (dy / dx).atan();
                }
                let id = i as u32;
                let mut commands = self.commands.lock().unwrap();
                commands.push_back(Command::Move(id.into(), position));
                println!("dx: {}, dy: {}, new rotation: {}", dx, dy, s.rotation);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Mul, Index, IndexMut};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Coord {
    pub y: i32,
    pub x: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LoopCoord {
    pub y: i32,
    pub x: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Dir {
    pub y: i32,
    pub x: i32,
}
impl Add<Dir> for Coord {
    type Output = Coord;
    fn add(self, rhs: Dir) -> Coord {
        Coord {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for Coord {
    type Output = Coord;
    fn sub(self, rhs: Dir) -> Coord {
        Coord {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Add<Dir> for LoopCoord {
    type Output = LoopCoord;
    fn add(self, rhs: Dir) -> LoopCoord {
        LoopCoord {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for LoopCoord {
    type Output = LoopCoord;
    fn sub(self, rhs: Dir) -> LoopCoord {
        LoopCoord {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Add<Dir> for Dir {
    type Output = Dir;
    fn add(self, rhs: Dir) -> Dir {
        Dir {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for Dir {
    type Output = Dir;
    fn sub(self, rhs: Dir) -> Dir {
        Dir {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Mul<i32> for Dir {
    type Output = Dir;
    fn mul(self, rhs: i32) -> Dir {
        Dir {
            y: self.y * rhs,
            x: self.x * rhs,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Grid<T: Clone> {
    height: i32,
    width: i32,
    data: Vec<T>,
}
impl<T: Clone> Grid<T> {
    pub fn new(height: i32, width: i32, default: T) -> Grid<T> {
        Grid {
            height: height,
            width: width,
            data: vec![default; (height * width) as usize],
        }
    }
    pub fn height(&self) -> i32 {
        self.height
    }
    pub fn width(&self) -> i32 {
        self.width
    }
    pub fn index(&self, cd: Coord) -> usize {
        (cd.y * self.width + cd.x) as usize
    }
    pub fn index_loop(&self, cd: LoopCoord) -> usize {
        (cd.y * self.width + cd.x) as usize
    }
}
impl<T: Clone> Index<Coord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: Coord) -> &'a T {
        let idx = self.index(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<Coord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: Coord) -> &'a mut T {
        let idx = self.index(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<LoopCoord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: LoopCoord) -> &'a T {
        let idx = self.index_loop(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<LoopCoord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: LoopCoord) -> &'a mut T {
        let idx = self.index_loop(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<usize> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: usize) -> &'a T {
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<usize> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut T {
        &mut self.data[idx]
    }
}
#[cfg(test)]
pub fn vec_to_grid<T>(v: &Vec<Vec<T>>) -> Grid<T>
where
    T: Copy,
{
    if v.len() == 0 {
        panic!("Attempted to convert empty Vec to Grid");
    }
    let ref_len = v[0].len();
    for r in v {
        if r.len() != ref_len {
            panic!("Each element in v must contain the same number of elements");
        }
    }
    let mut flat = vec![];
    for r in v {
        for &x in r {
            flat.push(x);
        }
    }
    Grid {
        height: v.len() as i32,
        width: ref_len as i32,
        data: flat,
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_common_types_operators() {
        assert_eq!(
            Coord { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            Coord { y: 4, x: 7 }
        );
        assert_eq!(
            Coord { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            Coord { y: -2, x: -3 }
        );
        assert_eq!(
            LoopCoord { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            LoopCoord { y: 4, x: 7 }
        );
        assert_eq!(
            LoopCoord { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            LoopCoord { y: -2, x: -3 }
        );
        assert_eq!(
            Dir { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            Dir { y: 4, x: 7 }
        );
        assert_eq!(
            Dir { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            Dir { y: -2, x: -3 }
        );
        assert_eq!(Dir { y: 1, x: 2 } * 3, Dir { y: 3, x: 6 });
    }

    #[test]
    fn test_grid() {
        let mut grid = Grid::new(3, 3, 0);
        assert_eq!(grid.height(), 3);
        assert_eq!(grid.width(), 3);
        assert_eq!(grid[LoopCoord { y: 1, x: 1 }], 0);
        grid[LoopCoord { y: 1, x: 1 }] = 4;
        assert_eq!(grid[LoopCoord { y: 1, x: 1 }], 4);
        assert_eq!(grid[LoopCoord { y: 1, x: 0 }], 0);
        assert_eq!(grid[LoopCoord { y: 2, x: 0 }], 0);
        assert_eq!(grid[4], 4);
    }
}
<commit_msg>Add FiniteSearchQueue<commit_after>use std::ops::{Add, Sub, Mul, Index, IndexMut};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Coord {
    pub y: i32,
    pub x: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LoopCoord {
    pub y: i32,
    pub x: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Dir {
    pub y: i32,
    pub x: i32,
}
impl Add<Dir> for Coord {
    type Output = Coord;
    fn add(self, rhs: Dir) -> Coord {
        Coord {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for Coord {
    type Output = Coord;
    fn sub(self, rhs: Dir) -> Coord {
        Coord {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Add<Dir> for LoopCoord {
    type Output = LoopCoord;
    fn add(self, rhs: Dir) -> LoopCoord {
        LoopCoord {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for LoopCoord {
    type Output = LoopCoord;
    fn sub(self, rhs: Dir) -> LoopCoord {
        LoopCoord {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Add<Dir> for Dir {
    type Output = Dir;
    fn add(self, rhs: Dir) -> Dir {
        Dir {
            y: self.y + rhs.y,
            x: self.x + rhs.x,
        }
    }
}
impl Sub<Dir> for Dir {
    type Output = Dir;
    fn sub(self, rhs: Dir) -> Dir {
        Dir {
            y: self.y - rhs.y,
            x: self.x - rhs.x,
        }
    }
}
impl Mul<i32> for Dir {
    type Output = Dir;
    fn mul(self, rhs: i32) -> Dir {
        Dir {
            y: self.y * rhs,
            x: self.x * rhs,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Grid<T: Clone> {
    height: i32,
    width: i32,
    data: Vec<T>,
}
impl<T: Clone> Grid<T> {
    pub fn new(height: i32, width: i32, default: T) -> Grid<T> {
        Grid {
            height: height,
            width: width,
            data: vec![default; (height * width) as usize],
        }
    }
    pub fn height(&self) -> i32 {
        self.height
    }
    pub fn width(&self) -> i32 {
        self.width
    }
    pub fn index(&self, cd: Coord) -> usize {
        (cd.y * self.width + cd.x) as usize
    }
    pub fn index_loop(&self, cd: LoopCoord) -> usize {
        (cd.y * self.width + cd.x) as usize
    }
}
impl<T: Clone> Index<Coord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: Coord) -> &'a T {
        let idx = self.index(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<Coord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: Coord) -> &'a mut T {
        let idx = self.index(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<LoopCoord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: LoopCoord) -> &'a T {
        let idx = self.index_loop(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<LoopCoord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: LoopCoord) -> &'a mut T {
        let idx = self.index_loop(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<usize> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: usize) -> &'a T {
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<usize> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut T {
        &mut self.data[idx]
    }
}

pub struct FiniteSearchQueue {
    top: i32,
    end: i32,
    size: i32,
    queue: Vec<i32>,
    stored: Vec<bool>,
    is_started: bool,
}
impl FiniteSearchQueue {
    pub fn new(max_elem: i32) -> FiniteSearchQueue {
        FiniteSearchQueue {
            top: 0,
            end: 0,
            size: max_elem + 1,
            queue: vec![0; max_elem as usize + 1],
            stored: vec![false; max_elem as usize],
            is_started: false,
        }
    }
    pub fn is_started(&self) -> bool {
        self.is_started
    }
    pub fn start(&mut self) {
        self.is_started = true;
    }
    pub fn finish(&mut self) {
        self.is_started = false;
    }
    pub fn push(&mut self, v: i32) {
        if !self.stored[v as usize] {
            self.stored[v as usize] = true;
            let loc = self.end;
            self.end += 1;
            if self.end == self.size {
                self.end = 0;
            }
            self.queue[loc as usize] = v;
        }
    }
    pub fn pop(&mut self) -> i32 {
        let ret = self.queue[self.top as usize];
        self.top += 1;
        if self.top == self.size {
            self.top = 0;
        }
        self.stored[ret as usize] = false;
        ret
    }
    pub fn empty(&mut self) -> bool {
        self.top == self.end
    }
}

#[cfg(test)]
pub fn vec_to_grid<T>(v: &Vec<Vec<T>>) -> Grid<T>
where
    T: Copy,
{
    if v.len() == 0 {
        panic!("Attempted to convert empty Vec to Grid");
    }
    let ref_len = v[0].len();
    for r in v {
        if r.len() != ref_len {
            panic!("Each element in v must contain the same number of elements");
        }
    }
    let mut flat = vec![];
    for r in v {
        for &x in r {
            flat.push(x);
        }
    }
    Grid {
        height: v.len() as i32,
        width: ref_len as i32,
        data: flat,
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_common_types_operators() {
        assert_eq!(
            Coord { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            Coord { y: 4, x: 7 }
        );
        assert_eq!(
            Coord { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            Coord { y: -2, x: -3 }
        );
        assert_eq!(
            LoopCoord { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            LoopCoord { y: 4, x: 7 }
        );
        assert_eq!(
            LoopCoord { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            LoopCoord { y: -2, x: -3 }
        );
        assert_eq!(
            Dir { y: 1, x: 2 } + Dir { y: 3, x: 5 },
            Dir { y: 4, x: 7 }
        );
        assert_eq!(
            Dir { y: 1, x: 2 } - Dir { y: 3, x: 5 },
            Dir { y: -2, x: -3 }
        );
        assert_eq!(Dir { y: 1, x: 2 } * 3, Dir { y: 3, x: 6 });
    }

    #[test]
    fn test_grid() {
        let mut grid = Grid::new(3, 3, 0);
        assert_eq!(grid.height(), 3);
        assert_eq!(grid.width(), 3);
        assert_eq!(grid[LoopCoord { y: 1, x: 1 }], 0);
        grid[LoopCoord { y: 1, x: 1 }] = 4;
        assert_eq!(grid[LoopCoord { y: 1, x: 1 }], 4);
        assert_eq!(grid[LoopCoord { y: 1, x: 0 }], 0);
        assert_eq!(grid[LoopCoord { y: 2, x: 0 }], 0);
        assert_eq!(grid[4], 4);
    }
}
<|endoftext|>


<commit_before>//! Completion API
use std::collections::BTreeSet;
use std::fs;
use std::path::{self, Path};

use super::Result;
use line_buffer::LineBuffer;

// TODO: let the implementers choose/find word boudaries ???
// (line, pos) is like (rl_line_buffer, rl_point) to make contextual completion ("select t.na| from tbl as t")
// TOOD: make &self &mut self ???

/// To be called for tab-completion.
pub trait Completer {
    /// Takes the currently edited `line` with the cursor `pos`ition and
    /// returns the start position and the completion candidates for the partial word to be completed.
    /// "ls /usr/loc" => Ok((3, vec!["/usr/local/"]))
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)>;
    /// Updates the edited `line` with the `elected` candidate.
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        let end = line.pos();
        line.replace(start, end, elected)
    }
}

impl Completer for () {
    fn complete(&self, _line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
        Ok((0, Vec::new()))
    }
    fn update(&self, _line: &mut LineBuffer, _start: usize, _elected: &str) {
        unreachable!()
    }
}

impl<'c, C: ?Sized + Completer> Completer for &'c C {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        (**self).complete(line, pos)
    }
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        (**self).update(line, start, elected)
    }
}
macro_rules! box_completer {
    ($($id: ident)*) => {
        $(
            impl<C: ?Sized + Completer> Completer for $id<C> {
                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
                    (**self).complete(line, pos)
                }
                fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
                    (**self).update(line, start, elected)
                }
            }
        )*
    }
}

use std::sync::Arc;
use std::rc::Rc;
box_completer! { Box Rc Arc }

pub struct FilenameCompleter {
    break_chars: BTreeSet<char>,
}

static DEFAULT_BREAK_CHARS: [char; 18] = [' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>',
                                          '<', '=', ';', '|', '&', '{', '(', '\0'];

impl FilenameCompleter {
    pub fn new() -> FilenameCompleter {
        FilenameCompleter { break_chars: DEFAULT_BREAK_CHARS.iter().cloned().collect() }
    }
}

impl Default for FilenameCompleter {
    fn default() -> FilenameCompleter {
        FilenameCompleter::new()
    }
}

impl Completer for FilenameCompleter {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        let (start, path) = extract_word(line, pos, &self.break_chars);
        let matches = try!(filename_complete(path));
        Ok((start, matches))
    }
}

fn filename_complete(path: &str) -> Result<Vec<String>> {
    use std::env::{current_dir, home_dir};

    let sep = path::MAIN_SEPARATOR;
    let (dir_name, file_name) = match path.rfind(sep) {
        Some(idx) => path.split_at(idx + sep.len_utf8()),
        None => ("", path),
    };

    let dir_path = Path::new(dir_name);
    let dir = if dir_path.starts_with("~") {
        // ~[/...]
        if let Some(home) = home_dir() {
            match dir_path.strip_prefix("~") {
                Ok(rel_path) => home.join(rel_path),
                _ => home,
            }
        } else {
            dir_path.to_path_buf()
        }
    } else if dir_path.is_relative() {
        // TODO ~user[/...] (https://crates.io/crates/users)
        if let Ok(cwd) = current_dir() {
            cwd.join(dir_path)
        } else {
            dir_path.to_path_buf()
        }
    } else {
        dir_path.to_path_buf()
    };

    let mut entries: Vec<String> = Vec::new();
    for entry in try!(fs::read_dir(dir)) {
        let entry = try!(entry);
        if let Some(s) = entry.file_name().to_str() {
            if s.starts_with(file_name) {
                let mut path = String::from(dir_name) + s;
                if try!(fs::metadata(entry.path())).is_dir() {
                    path.push(sep);
                }
                entries.push(path);
            }
        }
    }
    Ok(entries)
}

/// Given a `line` and a cursor `pos`ition,
/// try to find backward the start of a word.
/// Return (0, `line[..pos]`) if no break char has been found.
/// Return the word and its start position (idx, `line[idx..pos]`) otherwise.
pub fn extract_word<'l>(line: &'l str,
                        pos: usize,
                        break_chars: &BTreeSet<char>)
                        -> (usize, &'l str) {
    let line = &line[..pos];
    if line.is_empty() {
        return (0, line);
    }
    match line.char_indices().rev().find(|&(_, c)| break_chars.contains(&c)) {
        Some((i, c)) => {
            let start = i + c.len_utf8();
            (start, &line[start..])
        }
        None => (0, line),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;

    #[test]
    pub fn extract_word() {
        let break_chars: BTreeSet<char> = super::DEFAULT_BREAK_CHARS.iter().cloned().collect();
        let line = "ls '/usr/local/b";
        assert_eq!((4, "/usr/local/b"),
                   super::extract_word(line, line.len(), &break_chars));
    }
}
<commit_msg>Fix file completion on windows.<commit_after>//! Completion API
use std::collections::BTreeSet;
use std::fs;
use std::path::{self, Path};

use super::Result;
use line_buffer::LineBuffer;

// TODO: let the implementers choose/find word boudaries ???
// (line, pos) is like (rl_line_buffer, rl_point) to make contextual completion ("select t.na| from tbl as t")
// TOOD: make &self &mut self ???

/// To be called for tab-completion.
pub trait Completer {
    /// Takes the currently edited `line` with the cursor `pos`ition and
    /// returns the start position and the completion candidates for the partial word to be completed.
    /// "ls /usr/loc" => Ok((3, vec!["/usr/local/"]))
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)>;
    /// Updates the edited `line` with the `elected` candidate.
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        let end = line.pos();
        line.replace(start, end, elected)
    }
}

impl Completer for () {
    fn complete(&self, _line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
        Ok((0, Vec::new()))
    }
    fn update(&self, _line: &mut LineBuffer, _start: usize, _elected: &str) {
        unreachable!()
    }
}

impl<'c, C: ?Sized + Completer> Completer for &'c C {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        (**self).complete(line, pos)
    }
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        (**self).update(line, start, elected)
    }
}
macro_rules! box_completer {
    ($($id: ident)*) => {
        $(
            impl<C: ?Sized + Completer> Completer for $id<C> {
                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
                    (**self).complete(line, pos)
                }
                fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
                    (**self).update(line, start, elected)
                }
            }
        )*
    }
}

use std::sync::Arc;
use std::rc::Rc;
box_completer! { Box Rc Arc }

pub struct FilenameCompleter {
    break_chars: BTreeSet<char>,
}

#[cfg(unix)]
static DEFAULT_BREAK_CHARS: [char; 18] = [' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>',
                                          '<', '=', ';', '|', '&', '{', '(', '\0'];
// Remove \ to make file completion works on windows
#[cfg(windows)]
static DEFAULT_BREAK_CHARS: [char; 17] = [' ', '\t', '\n', '"', '\'', '`', '@', '$', '>',
                                          '<', '=', ';', '|', '&', '{', '(', '\0'];

impl FilenameCompleter {
    pub fn new() -> FilenameCompleter {
        FilenameCompleter { break_chars: DEFAULT_BREAK_CHARS.iter().cloned().collect() }
    }
}

impl Default for FilenameCompleter {
    fn default() -> FilenameCompleter {
        FilenameCompleter::new()
    }
}

impl Completer for FilenameCompleter {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        let (start, path) = extract_word(line, pos, &self.break_chars);
        let matches = try!(filename_complete(path));
        Ok((start, matches))
    }
}

fn filename_complete(path: &str) -> Result<Vec<String>> {
    use std::env::{current_dir, home_dir};

    let sep = path::MAIN_SEPARATOR;
    let (dir_name, file_name) = match path.rfind(sep) {
        Some(idx) => path.split_at(idx + sep.len_utf8()),
        None => ("", path),
    };

    let dir_path = Path::new(dir_name);
    let dir = if dir_path.starts_with("~") {
        // ~[/...]
        if let Some(home) = home_dir() {
            match dir_path.strip_prefix("~") {
                Ok(rel_path) => home.join(rel_path),
                _ => home,
            }
        } else {
            dir_path.to_path_buf()
        }
    } else if dir_path.is_relative() {
        // TODO ~user[/...] (https://crates.io/crates/users)
        if let Ok(cwd) = current_dir() {
            cwd.join(dir_path)
        } else {
            dir_path.to_path_buf()
        }
    } else {
        dir_path.to_path_buf()
    };

    let mut entries: Vec<String> = Vec::new();
    for entry in try!(fs::read_dir(dir)) {
        let entry = try!(entry);
        if let Some(s) = entry.file_name().to_str() {
            if s.starts_with(file_name) {
                let mut path = String::from(dir_name) + s;
                if try!(fs::metadata(entry.path())).is_dir() {
                    path.push(sep);
                }
                entries.push(path);
            }
        }
    }
    Ok(entries)
}

/// Given a `line` and a cursor `pos`ition,
/// try to find backward the start of a word.
/// Return (0, `line[..pos]`) if no break char has been found.
/// Return the word and its start position (idx, `line[idx..pos]`) otherwise.
pub fn extract_word<'l>(line: &'l str,
                        pos: usize,
                        break_chars: &BTreeSet<char>)
                        -> (usize, &'l str) {
    let line = &line[..pos];
    if line.is_empty() {
        return (0, line);
    }
    match line.char_indices().rev().find(|&(_, c)| break_chars.contains(&c)) {
        Some((i, c)) => {
            let start = i + c.len_utf8();
            (start, &line[start..])
        }
        None => (0, line),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;

    #[test]
    pub fn extract_word() {
        let break_chars: BTreeSet<char> = super::DEFAULT_BREAK_CHARS.iter().cloned().collect();
        let line = "ls '/usr/local/b";
        assert_eq!((4, "/usr/local/b"),
                   super::extract_word(line, line.len(), &break_chars));
    }
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub enum ArmInstruction {
    ADC(ArmInstrDProc),
    ADD(ArmInstrDProc),
    AND(ArmInstrDProc),
    B_BL(ArmInstrBBL),
    BLX(ArmInstrBranchExchange),
    BX(ArmInstrBranchExchange),
    CMN(ArmInstrDProc),
    CMP(ArmInstrDProc),
    BIC(ArmInstrDProc),
    EOR(ArmInstrDProc),
    LDM(ArmInstrLoadStoreMulti),
    LDR(ArmInstrLoadStore),
    LDRB(ArmInstrLoadStore),
    MCR(ArmInstrMoveCoproc),
    MRC(ArmInstrMoveCoproc),
    MRS(ArmInstrMoveStatusReg),
    MSR(ArmInstrMoveStatusReg),
    MOV(ArmInstrDProc),
    MVN(ArmInstrDProc),
    ORR(ArmInstrDProc),
    RSB(ArmInstrDProc),
    RSC(ArmInstrDProc),
    SBC(ArmInstrDProc),
    STM(ArmInstrLoadStoreMulti),
    STR(ArmInstrLoadStore),
    STRB(ArmInstrLoadStore),
    SUB(ArmInstrDProc),
    TEQ(ArmInstrDProc),
    TST(ArmInstrDProc),

    MOD_BLX(ArmInstrModBLX),

    UNKNOWN,
}

bitfield!(ArmInstrDProc: u32, {
    shifter_operand: 0 => 11,
    rd: 12 => 15,
    rn: 16 => 19,
    s_bit: 20 => 20,
    opcode: 21 => 24,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrBBL: u32, {
    signed_imm_24: 0 => 23,
    link_bit: 24 => 24,
    cond: 28 => 31
});

bitfield!(ArmInstrBranchExchange: u32, {
    rm: 0 => 3,
    cond: 28 => 31
});

bitfield!(ArmInstrLoadStore: u32, {
    addressing_mode_specific: 0 => 11,
    rd: 12 => 15,
    rn: 16 => 19,
    l_bit: 20 => 20,
    w_bit: 21 => 21,
    b_bit: 22 => 22,
    u_bit: 23 => 23,
    p_bit: 24 => 24,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrLoadStoreMulti: u32, {
    register_list: 0 => 15,
    rn: 16 => 19,
    l_bit: 20 => 20,
    w_bit: 21 => 21,
    s_bit: 22 => 22,
    u_bit: 23 => 23,
    p_bit: 24 => 24,
    cond: 28 => 31
});

bitfield!(ArmInstrMoveCoproc: u32, {
    crm: 0 => 3,
    opcode_2: 5 => 7,
    cp_num: 8 => 11,
    rd: 12 => 15,
    crn: 16 => 19,
    opcode_1: 21 => 23,
    cond: 28 => 31
});

bitfield!(ArmInstrMoveStatusReg: u32, {
    shifter_operand: 0 => 11,
    rd: 12 => 15,
    field_mask: 16 => 19,
    r_bit: 22 => 22,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrModBLX: u32, {
    signed_imm_24: 0 => 23,
    h_bit: 24 => 24
});

pub fn decode_arm_instruction(encoding: u32) -> ArmInstruction {
    macro_rules! handle {
        ($instr:ident: $data:ident, $mask:expr, $val:expr) => {
            if encoding & $mask == $val {
                return ArmInstruction::$instr($data::new(encoding));
            }
        };
    }

    // Special (0b1111) instructions
    if bits!(encoding, 28 => 31) == 0b1111 {
        handle!(MOD_BLX: ArmInstrModBLX, 0xFE000000, 0xFA000000);
        return ArmInstruction::UNKNOWN;
    }

    // Data Processing instructions
    handle!(AND: ArmInstrDProc, 0x0FE00090, 0x00000090);
    handle!(EOR: ArmInstrDProc, 0x0FE00090, 0x00200090);
    handle!(SUB: ArmInstrDProc, 0x0FE00090, 0x00400090);
    handle!(RSB: ArmInstrDProc, 0x0FE00090, 0x00600090);
    handle!(ADD: ArmInstrDProc, 0x0FE00090, 0x00800090);
    handle!(ADC: ArmInstrDProc, 0x0FE00090, 0x00A00090);
    handle!(SBC: ArmInstrDProc, 0x0FE00090, 0x00C00090);
    handle!(RSC: ArmInstrDProc, 0x0FE00090, 0x00E00090);
    handle!(TST: ArmInstrDProc, 0x0FF00090, 0x01100090);
    handle!(TEQ: ArmInstrDProc, 0x0FF00090, 0x01300090);
    handle!(CMP: ArmInstrDProc, 0x0FF00090, 0x01500090);
    handle!(CMN: ArmInstrDProc, 0x0FF00090, 0x01700090);
    handle!(ORR: ArmInstrDProc, 0x0FE00090, 0x01800090);
    handle!(MOV: ArmInstrDProc, 0x0FE00090, 0x01A00090);
    handle!(BIC: ArmInstrDProc, 0x0FE00090, 0x01C00090);
    handle!(MVN: ArmInstrDProc, 0x0FE00090, 0x01E00090);

    // Branch instructions
    handle!(B_BL: ArmInstrBBL, 0x0E000000, 0x0A000000);
    handle!(BLX: ArmInstrBranchExchange, 0x0FF000F0, 0x01200030);
    handle!(BX: ArmInstrBranchExchange, 0x0FF000F0, 0x01200010);

    // Load/store instructions
    handle!(LDR: ArmInstrLoadStore, 0x0C500000, 0x04100000);
    handle!(LDRB: ArmInstrLoadStore, 0x0C500000, 0x04500000);
    handle!(STR: ArmInstrLoadStore, 0x0C500000, 0x04000000);
    handle!(STRB: ArmInstrLoadStore, 0x0C500000, 0x04400000);

    // Load/store multiple instructions
    handle!(STM: ArmInstrLoadStoreMulti, 0x0E500000, 0x08000000);
    handle!(STM: ArmInstrLoadStoreMulti, 0x0E700000, 0x08400000);
    handle!(LDM: ArmInstrLoadStoreMulti, 0x0E500000, 0x08100000);
    handle!(LDM: ArmInstrLoadStoreMulti, 0x0E708000, 0x08500000);
    handle!(LDM: ArmInstrLoadStoreMulti, 0x02508000, 0x08508000);

    // Coprocessor instructions
    handle!(MRC: ArmInstrMoveCoproc, 0x0F100010, 0x0E100010);
    handle!(MCR: ArmInstrMoveCoproc, 0x0F100010, 0x0E000010);

    // Status register instructions
    handle!(MRS: ArmInstrMoveStatusReg, 0x0FB00000, 0x01000000);
    handle!(MSR: ArmInstrMoveStatusReg, 0x0FB00000, 0x03200000);
    handle!(MSR: ArmInstrMoveStatusReg, 0x0FB000F0, 0x01200000);

    ArmInstruction::UNKNOWN
}
<commit_msg>Revert "decoder_arm: Use handle! macro similar to decoder_thumb"<commit_after>#[derive(Debug)]
pub enum ArmInstruction {
    ADC(ArmInstrDProc),
    ADD(ArmInstrDProc),
    AND(ArmInstrDProc),
    B_BL(ArmInstrBBL),
    BLX(ArmInstrBranchExchange),
    BX(ArmInstrBranchExchange),
    CMN(ArmInstrDProc),
    CMP(ArmInstrDProc),
    BIC(ArmInstrDProc),
    EOR(ArmInstrDProc),
    LDM(ArmInstrLoadStoreMulti),
    LDR(ArmInstrLoadStore),
    LDRB(ArmInstrLoadStore),
    MCR(ArmInstrMoveCoproc),
    MRC(ArmInstrMoveCoproc),
    MRS(ArmInstrMoveStatusReg),
    MSR(ArmInstrMoveStatusReg),
    MOV(ArmInstrDProc),
    MVN(ArmInstrDProc),
    ORR(ArmInstrDProc),
    RSB(ArmInstrDProc),
    RSC(ArmInstrDProc),
    SBC(ArmInstrDProc),
    STM(ArmInstrLoadStoreMulti),
    STR(ArmInstrLoadStore),
    STRB(ArmInstrLoadStore),
    SUB(ArmInstrDProc),
    TEQ(ArmInstrDProc),
    TST(ArmInstrDProc),

    MOD_BLX(ArmInstrModBLX),

    UNKNOWN,
}

bitfield!(ArmInstrDProc: u32, {
    shifter_operand: 0 => 11,
    rd: 12 => 15,
    rn: 16 => 19,
    s_bit: 20 => 20,
    opcode: 21 => 24,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrBBL: u32, {
    signed_imm_24: 0 => 23,
    link_bit: 24 => 24,
    cond: 28 => 31
});

bitfield!(ArmInstrBranchExchange: u32, {
    rm: 0 => 3,
    cond: 28 => 31
});

bitfield!(ArmInstrLoadStore: u32, {
    addressing_mode_specific: 0 => 11,
    rd: 12 => 15,
    rn: 16 => 19,
    l_bit: 20 => 20,
    w_bit: 21 => 21,
    b_bit: 22 => 22,
    u_bit: 23 => 23,
    p_bit: 24 => 24,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrLoadStoreMulti: u32, {
    register_list: 0 => 15,
    rn: 16 => 19,
    l_bit: 20 => 20,
    w_bit: 21 => 21,
    s_bit: 22 => 22,
    u_bit: 23 => 23,
    p_bit: 24 => 24,
    cond: 28 => 31
});

bitfield!(ArmInstrMoveCoproc: u32, {
    crm: 0 => 3,
    opcode_2: 5 => 7,
    cp_num: 8 => 11,
    rd: 12 => 15,
    crn: 16 => 19,
    opcode_1: 21 => 23,
    cond: 28 => 31
});

bitfield!(ArmInstrMoveStatusReg: u32, {
    shifter_operand: 0 => 11,
    rd: 12 => 15,
    field_mask: 16 => 19,
    r_bit: 22 => 22,
    i_bit: 25 => 25,
    cond: 28 => 31
});

bitfield!(ArmInstrModBLX: u32, {
    signed_imm_24: 0 => 23,
    h_bit: 24 => 24
});

pub fn decode_arm_instruction(encoding: u32) -> ArmInstruction {
    macro_rules! constrain {
        ($data:expr, $([$low:expr => $high:expr, $val:expr, $boolean:expr]),*) => {{
            $((bits!($data, $low => $high) == $val) == $boolean)&&*
        }};
    }

    //
    // Special (0b1111) instructions
    //

    if bits!(encoding, 28 => 31) == 0b1111 {
        if constrain!(encoding, [25 => 27, 0b101, true]) {
            return ArmInstruction::MOD_BLX(ArmInstrModBLX::new(encoding));
        }

        return ArmInstruction::UNKNOWN;
    }

    //
    // Data Processing instructions
    //

    macro_rules! constrain_data_proc {
        ($encoding:expr, $opcode:expr, $condition:expr) => {{
            !constrain!($encoding, [25 => 25, 0b0, true], [7 => 7, 0b1, true], [4 => 4, 0b1, true])
        }};
    }

    macro_rules! constrain_move_proc {
        ($encoding:expr, $opcode:expr, $condition:expr) => {{
            constrain!($encoding, [26 => 27, 0b00, true], [21 => 24, $opcode, true]) &&
                constrain_data_proc!($encoding, $opcode, $condition)
        }};
    }

    macro_rules! constrain_compare_proc {
        ($encoding:expr, $opcode:expr, $condition:expr) => {{
            constrain!($encoding, [26 => 27, 0b00, true], [21 => 24, $opcode, true], [20 => 20, 0b1, true]) &&
                constrain_data_proc!($encoding, $opcode, $condition)
        }};
    }

    macro_rules! constrain_compute_proc {
        ($encoding:expr, $opcode:expr, $condition:expr) => {{
            constrain!($encoding, [26 => 27, 0b00, true], [21 => 24, $opcode, true]) &&
                constrain_data_proc!($encoding, $opcode, $condition)
        }};
    }

    if constrain_compute_proc!(encoding, 0b0000, condition) {
        return ArmInstruction::AND(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0001, condition) {
        return ArmInstruction::EOR(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0010, condition) {
        return ArmInstruction::SUB(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0011, condition) {
        return ArmInstruction::RSB(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0100, condition) {
        return ArmInstruction::ADD(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0101, condition) {
        return ArmInstruction::ADC(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0110, condition) {
        return ArmInstruction::SBC(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b0111, condition) {
        return ArmInstruction::RSC(ArmInstrDProc::new(encoding));
    }

    if constrain_compare_proc!(encoding, 0b1000, condition) {
        return ArmInstruction::TST(ArmInstrDProc::new(encoding));
    }

    if constrain_compare_proc!(encoding, 0b1001, condition) {
        return ArmInstruction::TEQ(ArmInstrDProc::new(encoding));
    }

    if constrain_compare_proc!(encoding, 0b1010, condition) {
        return ArmInstruction::CMP(ArmInstrDProc::new(encoding));
    }

    if constrain_compare_proc!(encoding, 0b1011, condition) {
        return ArmInstruction::CMN(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b1100, condition) {
        return ArmInstruction::ORR(ArmInstrDProc::new(encoding));
    }

    if constrain_move_proc!(encoding, 0b1101, condition) {
        return ArmInstruction::MOV(ArmInstrDProc::new(encoding));
    }

    if constrain_compute_proc!(encoding, 0b1110, condition) {
        return ArmInstruction::BIC(ArmInstrDProc::new(encoding));
    }

    if constrain_move_proc!(encoding, 0b1111, condition) {
        return ArmInstruction::MVN(ArmInstrDProc::new(encoding));
    }

    //
    // Branch instructions
    //

    if constrain!(encoding, [25 => 27, 0b101, true]) {
        return ArmInstruction::B_BL(ArmInstrBBL::new(encoding));
    }

    if constrain!(encoding, [20 => 27, 0b00010010, true], [4 => 7, 0b0011, true]) {
        return ArmInstruction::BLX(ArmInstrBranchExchange::new(encoding));
    }

    if constrain!(encoding, [20 => 27, 0b00010010, true], [4 => 7, 0b0001, true]) {
        return ArmInstruction::BX(ArmInstrBranchExchange::new(encoding));
    }

    //
    // Load/store instructions
    //

    if constrain!(encoding, [26 => 27, 0b01, true], [22 => 22, 0b0, true], [20 => 20, 0b1, true]) {
        return ArmInstruction::LDR(ArmInstrLoadStore::new(encoding));
    }

    if constrain!(encoding, [26 => 27, 0b01, true], [22 => 22, 0b1, true], [20 => 20, 0b1, true]) {
        return ArmInstruction::LDRB(ArmInstrLoadStore::new(encoding));
    }

    if constrain!(encoding, [26 => 27, 0b01, true], [22 => 22, 0b0, true], [20 => 20, 0b0, true]) {
        return ArmInstruction::STR(ArmInstrLoadStore::new(encoding));
    }

    if constrain!(encoding, [26 => 27, 0b01, true], [22 => 22, 0b1, true], [20 => 20, 0b0, true]) {
        return ArmInstruction::STRB(ArmInstrLoadStore::new(encoding));
    }

    //
    // Load/store multiple instructions
    //

    if constrain!(encoding, [25 => 27, 0b100, true], [22 => 22, 0b0, true], [20 => 20, 0b0, true]) ||
        constrain!(encoding, [25 => 27, 0b100, true], [20 => 22, 0b100, true]) {
        return ArmInstruction::STM(ArmInstrLoadStoreMulti::new(encoding));
    }

    if constrain!(encoding, [25 => 27, 0b100, true], [22 => 22, 0b0, true], [20 => 20, 0b1, true]) ||
        constrain!(encoding, [25 => 27, 0b100, true], [20 => 22, 0b101, true], [15 => 15, 0b0, true]) ||
        constrain!(encoding, [25 => 25, 0b100, true], [22 => 22, 0b1, true], [20 => 20, 0b1, true], [15 => 15, 0b1, true]) {
        return ArmInstruction::LDM(ArmInstrLoadStoreMulti::new(encoding));
    }

    //
    // Coprocessor instructions
    //

    if constrain!(encoding, [24 => 27, 0b1110, true], [20 => 20, 0b1, true], [4 => 4, 0b1, true]) {
        return ArmInstruction::MRC(ArmInstrMoveCoproc::new(encoding));
    }

    if constrain!(encoding, [24 => 27, 0b1110, true], [20 => 20, 0b0, true], [4 => 4, 0b1, true]) {
        return ArmInstruction::MCR(ArmInstrMoveCoproc::new(encoding));
    }

    //
    // Status register instructions
    //

    if constrain!(encoding, [23 => 27, 0b00010, true], [20 => 21, 0b00, true]) {
        return ArmInstruction::MRS(ArmInstrMoveStatusReg::new(encoding));
    }

    if constrain!(encoding, [23 => 27, 0b00110, true], [20 => 21, 0b10, true]) ||
        constrain!(encoding, [23 => 27, 0b00010, true], [20 => 21, 0b10, true], [4 => 7, 0b0000, true]) {
        return ArmInstruction::MSR(ArmInstrMoveStatusReg::new(encoding));
    }

    ArmInstruction::UNKNOWN
}
<|endoftext|>