<commit_before>use toml::Value;

use libimagerror::into::IntoError;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::result::Result;

use vcs::git::action::StoreAction;

pub fn commit_interactive(config: &Value) -> bool {
    unimplemented!()
}

pub fn commit_message(config: &Value, action: StoreAction) -> String {
    if commit_interactive(config) {
        unimplemented!()
    } else {
        unimplemented!()
    }
}

pub fn abort_on_repo_init_err(cfg: Option<&Value>) -> bool {
    get_bool_cfg(cfg, "abort_on_repo_init_failure", true, true)
}

pub fn ensure_branch(cfg: Option<&Value>) -> Result<Option<String>> {
    match cfg {
        Some(cfg) => {
            match cfg.lookup("ensure_branch") {
                Some(&Value::String(ref s)) => Ok(Some(s.clone())),
                Some(_) => {
                    warn!("Configuration error, 'ensure_branch' must be a String.");
                    Err(GHEK::ConfigTypeError.into_error())
                        .map_err_into(GHEK::ConfigTypeError)
                },
                None => {
                    debug!("No key `ensure_branch'");
                    Ok(None)
                },
            }
        },
        None => Ok(None),
    }
}

pub fn do_checkout_ensure_branch(cfg: Option<&Value>) -> bool {
    get_bool_cfg(cfg, "try_checkout_ensure_branch", true, true)
}

fn get_bool_cfg(cfg: Option<&Value>, name: &str, on_fail: bool, on_unavail: bool) -> bool {
    cfg.map(|cfg| {
        match cfg.lookup(name) {
            Some(&Value::Boolean(b)) => b,
            Some(_) => {
                warn!("Configuration error, '{}' must be a Boolean (true|false).", name);
                warn!("Assuming 'true' now.");
                on_fail
            },
            None => {
                debug!("No key '{}' - Assuming 'true'", name);
                on_fail
            },
        }
    })
    .unwrap_or(on_unavail)
}

<commit_msg>Insert dummy commit message<commit_after>use toml::Value;

use libimagerror::into::IntoError;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::result::Result;

use vcs::git::action::StoreAction;

pub fn commit_interactive(config: &Value) -> bool {
    warn!("Interactive committing not yet supported, using dummy commit message");
    false
}

pub fn commit_message(config: &Value, action: StoreAction) -> String {
    if commit_interactive(config) {
        unimplemented!()
    } else {
        String::from("Dummy commit")
    }
}

pub fn abort_on_repo_init_err(cfg: Option<&Value>) -> bool {
    get_bool_cfg(cfg, "abort_on_repo_init_failure", true, true)
}

pub fn ensure_branch(cfg: Option<&Value>) -> Result<Option<String>> {
    match cfg {
        Some(cfg) => {
            match cfg.lookup("ensure_branch") {
                Some(&Value::String(ref s)) => Ok(Some(s.clone())),
                Some(_) => {
                    warn!("Configuration error, 'ensure_branch' must be a String.");
                    Err(GHEK::ConfigTypeError.into_error())
                        .map_err_into(GHEK::ConfigTypeError)
                },
                None => {
                    debug!("No key `ensure_branch'");
                    Ok(None)
                },
            }
        },
        None => Ok(None),
    }
}

pub fn do_checkout_ensure_branch(cfg: Option<&Value>) -> bool {
    get_bool_cfg(cfg, "try_checkout_ensure_branch", true, true)
}

fn get_bool_cfg(cfg: Option<&Value>, name: &str, on_fail: bool, on_unavail: bool) -> bool {
    cfg.map(|cfg| {
        match cfg.lookup(name) {
            Some(&Value::Boolean(b)) => b,
            Some(_) => {
                warn!("Configuration error, '{}' must be a Boolean (true|false).", name);
                warn!("Assuming 'true' now.");
                on_fail
            },
            None => {
                debug!("No key '{}' - Assuming 'true'", name);
                on_fail
            },
        }
    })
    .unwrap_or(on_unavail)
}

<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::trace::trace_error;
use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::runtime::Runtime as GRuntime;

pub struct DeleteHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl DeleteHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook {
        DeleteHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for DeleteHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "DeleteHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for DeleteHook {

    fn name(&self) -> &'static str {
        "stdhook_git_delete"
    }

    /// Set the configuration of the hook. See
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`.
    ///
    /// This function traces the error (using `trace_error()`) that
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`
    /// returns, if any.
    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for DeleteHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for DeleteHook {

    fn access(&self, id: &StoreId) -> HookResult<()> {
        use libimagerror::into::IntoError;
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use vcs::git::config::abort_on_repo_init_err;
        use git2::{ADD_DEFAULT, STATUS_WT_DELETED, IndexMatchedPath};

        debug!("[GIT DELETE HOOK]: {:?}", id);

        let action = StoreAction::Delete;

        if !self.runtime.has_repository() {
            debug!("[GIT DELETE HOOK]: Runtime has no repository...");
            if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) {
                // Abort on repo init failure
                debug!("[GIT DELETE HOOK]: Config says we should abort if we have no repository");
                debug!("[GIT DELETE HOOK]: Returing Err(_)");
                return Err(GHEK::RepositoryInitError.into_error())
                    .map_err_into(GHEK::RepositoryError)
                    .map_into_hook_error()
            } else {
                debug!("[GIT DELETE HOOK]: Config says it is okay to not have a repository");
                debug!("[GIT DELETE HOOK]: Returing Ok(())");
                return Ok(())
            }
        }

        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched signature object")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_dbg_str("[GIT DELETE HOOK]: Fetched file status")
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            debug!("[GIT DELETE HOOK]: Checking file status for: {}", path.display());
            if file_status.contains(STATUS_WT_DELETED) {
                debug!("[GIT DELETE HOOK]: File is deleted: {}", path.display());
                0
            } else {
                debug!("[GIT DELETE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched index")
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_dbg_str("[GIT DELETE HOOK]: Wrote index tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_dbg_str("[GIT DELETE HOOK]: Found commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_dbg_str("[GIT DELETE HOOK]: Found tree for index")
                .map_into_hook_error()
        );

        let message = try!(commit_message(&repo, cfg, action, &id)
                .map_dbg_err_str("Failed to get commit message")
                .map_dbg_str("[GIT DELETE HOOK]: Got commit message"));

        try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_dbg_str("[GIT DELETE HOOK]: Committed")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
        );

        index.write()
            .map_err_into(GHEK::RepositoryIndexWritingError)
            .map_dbg_err_str("Failed to write tree")
            .map_dbg_str("[GIT DELETE HOOK]: Wrote index")
            .map_into_hook_error()
            .map(|_| ())
    }

}

<commit_msg>DeleteHook: Ensure the right branch is checked out<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::trace::trace_error;
use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::runtime::Runtime as GRuntime;

pub struct DeleteHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl DeleteHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook {
        DeleteHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for DeleteHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "DeleteHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for DeleteHook {

    fn name(&self) -> &'static str {
        "stdhook_git_delete"
    }

    /// Set the configuration of the hook. See
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`.
    ///
    /// This function traces the error (using `trace_error()`) that
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`
    /// returns, if any.
    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for DeleteHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for DeleteHook {

    fn access(&self, id: &StoreId) -> HookResult<()> {
        use libimagerror::into::IntoError;
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use vcs::git::config::abort_on_repo_init_err;
        use git2::{ADD_DEFAULT, STATUS_WT_DELETED, IndexMatchedPath};

        debug!("[GIT DELETE HOOK]: {:?}", id);

        let action = StoreAction::Delete;

        if !self.runtime.has_repository() {
            debug!("[GIT DELETE HOOK]: Runtime has no repository...");
            if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) {
                // Abort on repo init failure
                debug!("[GIT DELETE HOOK]: Config says we should abort if we have no repository");
                debug!("[GIT DELETE HOOK]: Returing Err(_)");
                return Err(GHEK::RepositoryInitError.into_error())
                    .map_err_into(GHEK::RepositoryError)
                    .map_into_hook_error()
            } else {
                debug!("[GIT DELETE HOOK]: Config says it is okay to not have a repository");
                debug!("[GIT DELETE HOOK]: Returing Ok(())");
                return Ok(())
            }
        }

        let _         = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action));
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched signature object")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_dbg_str("[GIT DELETE HOOK]: Fetched file status")
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            debug!("[GIT DELETE HOOK]: Checking file status for: {}", path.display());
            if file_status.contains(STATUS_WT_DELETED) {
                debug!("[GIT DELETE HOOK]: File is deleted: {}", path.display());
                0
            } else {
                debug!("[GIT DELETE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_dbg_str("[GIT DELETE HOOK]: Fetched index")
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_dbg_str("[GIT DELETE HOOK]: Wrote index tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_dbg_str("[GIT DELETE HOOK]: Found commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_dbg_str("[GIT DELETE HOOK]: Found tree for index")
                .map_into_hook_error()
        );

        let message = try!(commit_message(&repo, cfg, action, &id)
                .map_dbg_err_str("Failed to get commit message")
                .map_dbg_str("[GIT DELETE HOOK]: Got commit message"));

        try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_dbg_str("[GIT DELETE HOOK]: Committed")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
        );

        index.write()
            .map_err_into(GHEK::RepositoryIndexWritingError)
            .map_dbg_err_str("Failed to write tree")
            .map_dbg_str("[GIT DELETE HOOK]: Wrote index")
            .map_into_hook_error()
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::hashmap::HashMap;
use cgmath::vector::Vec2;

pub type PlayerId = i32;
pub type UnitId = i32;
pub type MapPos = Vec2<i32>;

pub enum Command {
  CommandMove(UnitId, MapPos),
  CommandEndTurn,
}

pub enum EventView {
  EventViewMove(UnitId, ~[MapPos]),
  EventViewEndTurn(PlayerId, PlayerId), // old_id, new_id
}

pub struct Player {
  id: PlayerId,
}

pub struct Unit {
  id: UnitId,
  pos: MapPos,
}

pub struct Core<'a> {
  units: ~[Unit],
  players: ~[Player],
  current_player_id: PlayerId,
  event_list: ~[~Event],
  event_view_lists: HashMap<PlayerId, ~[EventView]>,
  map_size: Vec2<i32>,
}

fn get_event_view_lists() -> HashMap<PlayerId, ~[EventView]> {
  let mut map = HashMap::new();
  map.insert(0 as PlayerId, ~[]);
  map.insert(1 as PlayerId, ~[]);
  map
}

impl<'a> Core<'a> {
  pub fn new() -> Core {
    Core {
      units: ~[],
      players: ~[Player{id: 0}, Player{id: 1}],
      current_player_id: 0,
      event_list: ~[],
      event_view_lists: get_event_view_lists(),
      map_size: Vec2{x: 4, y: 8},
    }
  }

  pub fn id_to_unit_mut_opt(&'a mut self, id: UnitId) -> Option<&'a mut Unit> {
    self.units.mut_iter().find(|u| u.id == id)
  }

  pub fn id_to_unit_mut(&'a mut self, id: UnitId) -> &'a mut Unit {
    match self.id_to_unit_mut_opt(id) {
      Some(unit) => unit,
      None => fail!("Bad unit id: {}", id),
    }
  }

  pub fn id_to_unit_opt(&'a self, id: UnitId) -> Option<&'a Unit> {
    self.units.iter().find(|u| u.id == id)
  }

  pub fn id_to_unit(&'a self, id: UnitId) -> &'a Unit {
    match self.id_to_unit_opt(id) {
      Some(unit) => unit,
      None => fail!("Bad unit id: {}", id),
    }
  }

  fn command_to_event(&self, command: Command) -> ~Event {
    match command {
      CommandEndTurn => {
        EventEndTurn::new(self) as ~Event
      },
      CommandMove(unit_id, destination) => {
        EventMove::new(self, unit_id, destination) as ~Event
      },
    }
  }

  pub fn do_command(&mut self, command: Command) {
    let event = self.command_to_event(command);
    self.event_list.push(event);
    self.make_event_views();
  }

  pub fn make_event_views(&mut self) {
    while self.event_list.len() != 0 {
      let event = self.event_list.pop().unwrap();
      event.apply(self);
      for player in self.players.iter() {
        let event_view_list = self.event_view_lists.get_mut(&player.id);
        event_view_list.push(event.get_view());
      }
    }
  }
}

trait Event {
  fn apply(&self, core: &mut Core);
  fn get_view(&self) -> EventView;
  // TODO: fn is_visible(&self) -> bool;
}

struct EventMove {
  unit_id: UnitId,
  path: ~[MapPos],
}

impl EventMove {
  fn new(core: &Core, unit_id: UnitId, destination: MapPos) -> ~EventMove {
    let start_pos = core.id_to_unit(unit_id).pos;
    ~EventMove {
      path: ~[start_pos, destination],
      unit_id: unit_id,
    }
  }
}

impl Event for EventMove {
  fn get_view(&self) -> EventView {
    EventViewMove(self.unit_id, self.path.clone())
  }

  fn apply(&self, core: &mut Core) {
    let unit = core.id_to_unit_mut(self.unit_id);
    unit.pos = *self.path.last().unwrap();
  }
}

struct EventEndTurn {
  old_id: PlayerId,
  new_id: PlayerId,
}

impl EventEndTurn {
  fn new(core: &Core) -> ~EventEndTurn {
    let old_id = core.current_player_id;
    let max_id = core.players.len() as i32;
    let new_id = if old_id + 1 == max_id { 0 } else { old_id + 1 };
    ~EventEndTurn{old_id: old_id, new_id: new_id}
  }
}

impl Event for EventEndTurn {
  fn get_view(&self) -> EventView {
    EventViewEndTurn(self.old_id, self.new_id)
  }

  fn apply(&self, core: &mut Core) {
    // core.deselected_any_units();
    for player in core.players.iter() {
      if player.id == self.new_id {
        if core.current_player_id == self.old_id {
          core.current_player_id = player.id;
        }
        return;
      }
    }
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>Core: make_event_views(): priv<commit_after>// See LICENSE file for copyright and license details.

use std::hashmap::HashMap;
use cgmath::vector::Vec2;

pub type PlayerId = i32;
pub type UnitId = i32;
pub type MapPos = Vec2<i32>;

pub enum Command {
  CommandMove(UnitId, MapPos),
  CommandEndTurn,
}

pub enum EventView {
  EventViewMove(UnitId, ~[MapPos]),
  EventViewEndTurn(PlayerId, PlayerId), // old_id, new_id
}

pub struct Player {
  id: PlayerId,
}

pub struct Unit {
  id: UnitId,
  pos: MapPos,
}

pub struct Core<'a> {
  units: ~[Unit],
  players: ~[Player],
  current_player_id: PlayerId,
  event_list: ~[~Event],
  event_view_lists: HashMap<PlayerId, ~[EventView]>,
  map_size: Vec2<i32>,
}

fn get_event_view_lists() -> HashMap<PlayerId, ~[EventView]> {
  let mut map = HashMap::new();
  map.insert(0 as PlayerId, ~[]);
  map.insert(1 as PlayerId, ~[]);
  map
}

impl<'a> Core<'a> {
  pub fn new() -> Core {
    Core {
      units: ~[],
      players: ~[Player{id: 0}, Player{id: 1}],
      current_player_id: 0,
      event_list: ~[],
      event_view_lists: get_event_view_lists(),
      map_size: Vec2{x: 4, y: 8},
    }
  }

  pub fn id_to_unit_mut_opt(&'a mut self, id: UnitId) -> Option<&'a mut Unit> {
    self.units.mut_iter().find(|u| u.id == id)
  }

  pub fn id_to_unit_mut(&'a mut self, id: UnitId) -> &'a mut Unit {
    match self.id_to_unit_mut_opt(id) {
      Some(unit) => unit,
      None => fail!("Bad unit id: {}", id),
    }
  }

  pub fn id_to_unit_opt(&'a self, id: UnitId) -> Option<&'a Unit> {
    self.units.iter().find(|u| u.id == id)
  }

  pub fn id_to_unit(&'a self, id: UnitId) -> &'a Unit {
    match self.id_to_unit_opt(id) {
      Some(unit) => unit,
      None => fail!("Bad unit id: {}", id),
    }
  }

  fn command_to_event(&self, command: Command) -> ~Event {
    match command {
      CommandEndTurn => {
        EventEndTurn::new(self) as ~Event
      },
      CommandMove(unit_id, destination) => {
        EventMove::new(self, unit_id, destination) as ~Event
      },
    }
  }

  pub fn do_command(&mut self, command: Command) {
    let event = self.command_to_event(command);
    self.event_list.push(event);
    self.make_event_views();
  }

  fn make_event_views(&mut self) {
    while self.event_list.len() != 0 {
      let event = self.event_list.pop().unwrap();
      event.apply(self);
      for player in self.players.iter() {
        let event_view_list = self.event_view_lists.get_mut(&player.id);
        event_view_list.push(event.get_view());
      }
    }
  }
}

trait Event {
  fn apply(&self, core: &mut Core);
  fn get_view(&self) -> EventView;
  // TODO: fn is_visible(&self) -> bool;
}

struct EventMove {
  unit_id: UnitId,
  path: ~[MapPos],
}

impl EventMove {
  fn new(core: &Core, unit_id: UnitId, destination: MapPos) -> ~EventMove {
    let start_pos = core.id_to_unit(unit_id).pos;
    ~EventMove {
      path: ~[start_pos, destination],
      unit_id: unit_id,
    }
  }
}

impl Event for EventMove {
  fn get_view(&self) -> EventView {
    EventViewMove(self.unit_id, self.path.clone())
  }

  fn apply(&self, core: &mut Core) {
    let unit = core.id_to_unit_mut(self.unit_id);
    unit.pos = *self.path.last().unwrap();
  }
}

struct EventEndTurn {
  old_id: PlayerId,
  new_id: PlayerId,
}

impl EventEndTurn {
  fn new(core: &Core) -> ~EventEndTurn {
    let old_id = core.current_player_id;
    let max_id = core.players.len() as i32;
    let new_id = if old_id + 1 == max_id { 0 } else { old_id + 1 };
    ~EventEndTurn{old_id: old_id, new_id: new_id}
  }
}

impl Event for EventEndTurn {
  fn get_view(&self) -> EventView {
    EventViewEndTurn(self.old_id, self.new_id)
  }

  fn apply(&self, core: &mut Core) {
    // core.deselected_any_units();
    for player in core.players.iter() {
      if player.id == self.new_id {
        if core.current_player_id == self.old_id {
          core.current_player_id = player.id;
        }
        return;
      }
    }
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>


<commit_before>// run-pass

use std::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        let zero: Wrapping<$type> = Wrapping(0);
        let one: Wrapping<$type> = Wrapping(1);
        let min: Wrapping<$type> = Wrapping($min);
        let max: Wrapping<$type> = Wrapping($max);

        wrapping_operation!(min, max + one);
        wrapping_assignment!(min, max += one);
        wrapping_operation!(max, min - one);
        wrapping_assignment!(max, min -= one);
        wrapping_operation!(max, max * one);
        wrapping_assignment!(max, max *= one);
        wrapping_operation!(max, max / one);
        wrapping_assignment!(max, max /= one);
        wrapping_operation!(zero, max % one);
        wrapping_assignment!(zero, max %= one);
        wrapping_operation!(zero, zero & max);
        wrapping_assignment!(zero, zero &= max);
        wrapping_operation!(max, zero | max);
        wrapping_assignment!(max, zero |= max);
        wrapping_operation!(zero, max ^ max);
        wrapping_assignment!(zero, max ^= max);
        wrapping_operation!(zero, zero << 1usize);
        wrapping_assignment!(zero, zero <<= 1usize);
        wrapping_operation!(zero, zero >> 1usize);
        wrapping_assignment!(zero, zero >>= 1usize);
        wrapping_operation!(zero, -zero);
        wrapping_operation!(max, !min);
    };
}

fn main() {
    wrapping_test!(i8, std::i8::MIN, std::i8::MAX);
    wrapping_test!(i16, std::i16::MIN, std::i16::MAX);
    wrapping_test!(i32, std::i32::MIN, std::i32::MAX);
    wrapping_test!(i64, std::i64::MIN, std::i64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(i128, std::i128::MIN, std::i128::MAX);
    wrapping_test!(isize, std::isize::MIN, std::isize::MAX);
    wrapping_test!(u8, std::u8::MIN, std::u8::MAX);
    wrapping_test!(u16, std::u16::MIN, std::u16::MAX);
    wrapping_test!(u32, std::u32::MIN, std::u32::MAX);
    wrapping_test!(u64, std::u64::MIN, std::u64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(u128, std::u128::MIN, std::u128::MAX);
    wrapping_test!(usize, std::usize::MIN, std::usize::MAX);
}
<commit_msg>Fix to libstd test<commit_after>use core::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        #[test]
        fn wrapping_$type() {
            let zero: Wrapping<$type> = Wrapping(0);
            let one: Wrapping<$type> = Wrapping(1);
            let min: Wrapping<$type> = Wrapping($min);
            let max: Wrapping<$type> = Wrapping($max);

            wrapping_operation!(min, max + one);
            wrapping_assignment!(min, max += one);
            wrapping_operation!(max, min - one);
            wrapping_assignment!(max, min -= one);
            wrapping_operation!(max, max * one);
            wrapping_assignment!(max, max *= one);
            wrapping_operation!(max, max / one);
            wrapping_assignment!(max, max /= one);
            wrapping_operation!(zero, max % one);
            wrapping_assignment!(zero, max %= one);
            wrapping_operation!(zero, zero & max);
            wrapping_assignment!(zero, zero &= max);
            wrapping_operation!(max, zero | max);
            wrapping_assignment!(max, zero |= max);
            wrapping_operation!(zero, max ^ max);
            wrapping_assignment!(zero, max ^= max);
            wrapping_operation!(zero, zero << 1usize);
            wrapping_assignment!(zero, zero <<= 1usize);
            wrapping_operation!(zero, zero >> 1usize);
            wrapping_assignment!(zero, zero >>= 1usize);
            wrapping_operation!(zero, -zero);
            wrapping_operation!(max, !min);
        }
    };
}

#[cfg(tests)]
mod tests {
    wrapping_test!(i8, i8::MIN, i8::MAX);
    wrapping_test!(i16, i16::MIN, i16::MAX);
    wrapping_test!(i32, i32::MIN, i32::MAX);
    wrapping_test!(i64, i64::MIN, i64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(i128, i128::MIN, i128::MAX);
    wrapping_test!(isize, isize::MIN, isize::MAX);
    wrapping_test!(u8, u8::MIN, u8::MAX);
    wrapping_test!(u16, u16::MIN, u16::MAX);
    wrapping_test!(u32, u32::MIN, u32::MAX);
    wrapping_test!(u64, u64::MIN, u64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(u128, u128::MIN, u128::MAX);
    wrapping_test!(usize, usize::MIN, usize::MAX);
}
<|endoftext|>


<commit_before>//! This file contains the string defaults values as well as environment variable strings
//! for use in the clap layer of the application. This is not the final form for defaults.
//! Eventually this will be composed of fully typed default values. But as a first step we
//! need a spot to consolidate those values and help simplify some of the logic around them.

use clap::{value_t,
           ArgMatches};

use habitat_core::{self,
                   os::process::{ShutdownSignal,
                                 ShutdownTimeout},
                   package::PackageIdent};
use std::{ffi::OsStr,
          path::{Path,
                 PathBuf},
          str::FromStr};

pub const RING_ENVVAR: &str = "HAB_RING";
pub const RING_KEY_ENVVAR: &str = "HAB_RING_KEY";

pub const LISTEN_HTTP_DEFAULT_PORT: u16 = 9631;
pub const LISTEN_HTTP_DEFAULT_IP: &str = "0.0.0.0";
lazy_static! {
    pub static ref LISTEN_HTTP_DEFAULT_ADDR: String =
        { format!("{}:{}", LISTEN_HTTP_DEFAULT_IP, LISTEN_HTTP_DEFAULT_PORT) };
}

pub const PACKAGE_TARGET_ENVVAR: &str = "HAB_PACKAGE_TARGET";
lazy_static! {
    pub static ref SHUTDOWN_TIMEOUT_DEFAULT: String = ShutdownTimeout::default().to_string();
}

// We allow this on Windows as well as Unix, even though we don't use
// ShutdownSignal on Windows, because we want to allow Windows CLI
// users to interact with Unix Supervisors.
lazy_static! {
    pub static ref SHUTDOWN_SIGNAL_DEFAULT: String = ShutdownSignal::default().to_string();
}

pub const BINLINK_DIR_ENVVAR: &str = "HAB_BINLINK_DIR";

/// Default Binlink Dir
#[cfg(target_os = "windows")]
pub const DEFAULT_BINLINK_DIR: &str = "/hab/bin";
#[cfg(target_os = "linux")]
pub const DEFAULT_BINLINK_DIR: &str = "/bin";
#[cfg(target_os = "macos")]
pub const DEFAULT_BINLINK_DIR: &str = "/usr/local/bin";

pub fn cache_key_path_from_matches(matches: &ArgMatches<'_>) -> PathBuf {
    let path = clap::value_t!(matches, "CACHE_KEY_PATH", PathBuf).expect("CACHE_KEY_PATH required");
    PathBuf::from(path)
}

pub fn is_toml_file(val: &str) -> bool {
    let extension = Path::new(&val).extension().and_then(OsStr::to_str);
    match extension {
        // We could do some more validation (parse the whole toml file and check it) but that seems
        // excessive.
        Some("toml") => true,
        _ => false,
    }
}

pub fn file_into_idents(path: &str) -> Result<Vec<PackageIdent>, habitat_core::error::Error> {
    let s = std::fs::read_to_string(&path).map_err(|_| {
                habitat_core::error::Error::FileNotFound(format!("Could not open file {}", path))
            })?;

    s.lines().filter_map(line_to_ident).collect()
}

fn line_to_ident(line: &str) -> Option<Result<PackageIdent, habitat_core::error::Error>> {
    let trimmed = line.split('#').nth(0).unwrap_or("").trim();
    match trimmed.len() {
        0 => None,
        _ => Some(PackageIdent::from_str(trimmed)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    // These tests aren't super pretty because we can't do PartialEq for the Option<Error<X>> stuff,
    // so lots of unwrap
    fn test_line_to_ident() {
        assert!(line_to_ident("").is_none());
        assert!(line_to_ident("# foo").is_none());
        assert!(line_to_ident("   # foo").is_none());
        assert!(line_to_ident("\n\r# foo").is_none());

        assert_eq!(line_to_ident("core/gzip").unwrap().unwrap(),
                   PackageIdent::from_str("core/gzip").unwrap());
        assert_eq!(line_to_ident("core/gzip #foo").unwrap().unwrap(),
                   PackageIdent::from_str("core/gzip").unwrap());

        assert!(line_to_ident("core").unwrap().is_err());

        assert!(line_to_ident("core # not").unwrap().is_err());
    }
}
<commit_msg>Fix clippy<commit_after>//! This file contains the string defaults values as well as environment variable strings
//! for use in the clap layer of the application. This is not the final form for defaults.
//! Eventually this will be composed of fully typed default values. But as a first step we
//! need a spot to consolidate those values and help simplify some of the logic around them.

use clap::{value_t,
           ArgMatches};

use habitat_core::{self,
                   os::process::{ShutdownSignal,
                                 ShutdownTimeout},
                   package::PackageIdent};
use std::{ffi::OsStr,
          path::{Path,
                 PathBuf},
          str::FromStr};

pub const RING_ENVVAR: &str = "HAB_RING";
pub const RING_KEY_ENVVAR: &str = "HAB_RING_KEY";

pub const LISTEN_HTTP_DEFAULT_PORT: u16 = 9631;
pub const LISTEN_HTTP_DEFAULT_IP: &str = "0.0.0.0";
lazy_static! {
    pub static ref LISTEN_HTTP_DEFAULT_ADDR: String =
        { format!("{}:{}", LISTEN_HTTP_DEFAULT_IP, LISTEN_HTTP_DEFAULT_PORT) };
}

pub const PACKAGE_TARGET_ENVVAR: &str = "HAB_PACKAGE_TARGET";
lazy_static! {
    pub static ref SHUTDOWN_TIMEOUT_DEFAULT: String = ShutdownTimeout::default().to_string();
}

// We allow this on Windows as well as Unix, even though we don't use
// ShutdownSignal on Windows, because we want to allow Windows CLI
// users to interact with Unix Supervisors.
lazy_static! {
    pub static ref SHUTDOWN_SIGNAL_DEFAULT: String = ShutdownSignal::default().to_string();
}

pub const BINLINK_DIR_ENVVAR: &str = "HAB_BINLINK_DIR";

/// Default Binlink Dir
#[cfg(target_os = "windows")]
pub const DEFAULT_BINLINK_DIR: &str = "/hab/bin";
#[cfg(target_os = "linux")]
pub const DEFAULT_BINLINK_DIR: &str = "/bin";
#[cfg(target_os = "macos")]
pub const DEFAULT_BINLINK_DIR: &str = "/usr/local/bin";

pub fn cache_key_path_from_matches(matches: &ArgMatches<'_>) -> PathBuf {
    clap::value_t!(matches, "CACHE_KEY_PATH", PathBuf).expect("CACHE_KEY_PATH required")
}

pub fn is_toml_file(val: &str) -> bool {
    let extension = Path::new(&val).extension().and_then(OsStr::to_str);
    match extension {
        // We could do some more validation (parse the whole toml file and check it) but that seems
        // excessive.
        Some("toml") => true,
        _ => false,
    }
}

pub fn file_into_idents(path: &str) -> Result<Vec<PackageIdent>, habitat_core::error::Error> {
    let s = std::fs::read_to_string(&path).map_err(|_| {
                habitat_core::error::Error::FileNotFound(format!("Could not open file {}", path))
            })?;

    s.lines().filter_map(line_to_ident).collect()
}

fn line_to_ident(line: &str) -> Option<Result<PackageIdent, habitat_core::error::Error>> {
    let trimmed = line.split('#').nth(0).unwrap_or("").trim();
    match trimmed.len() {
        0 => None,
        _ => Some(PackageIdent::from_str(trimmed)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    // These tests aren't super pretty because we can't do PartialEq for the Option<Error<X>> stuff,
    // so lots of unwrap
    fn test_line_to_ident() {
        assert!(line_to_ident("").is_none());
        assert!(line_to_ident("# foo").is_none());
        assert!(line_to_ident("   # foo").is_none());
        assert!(line_to_ident("\n\r# foo").is_none());

        assert_eq!(line_to_ident("core/gzip").unwrap().unwrap(),
                   PackageIdent::from_str("core/gzip").unwrap());
        assert_eq!(line_to_ident("core/gzip #foo").unwrap().unwrap(),
                   PackageIdent::from_str("core/gzip").unwrap());

        assert!(line_to_ident("core").unwrap().is_err());

        assert!(line_to_ident("core # not").unwrap().is_err());
    }
}
<|endoftext|>


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

//! Generic implementations of some DOM APIs so they can be shared between Servo
//! and Gecko.

use context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{Component, LocalName};
use smallvec::SmallVec;
use std::borrow::Borrow;

/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
    element: &E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> bool
where
    E: Element,
{
    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        None,
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());
    matching::matches_selector_list(selector_list, element, &mut context)
}

/// <https://dom.spec.whatwg.org/#dom-element-closest>
pub fn element_closest<E>(
    element: E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> Option<E>
where
    E: Element,
{
    let mut nth_index_cache = NthIndexCache::default();

    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());

    let mut current = Some(element);
    while let Some(element) = current.take() {
        if matching::matches_selector_list(selector_list, &element, &mut context) {
            return Some(element);
        }
        current = element.parent_element();
    }

    return None;
}

/// A selector query abstraction, in order to be generic over QuerySelector and
/// QuerySelectorAll.
pub trait SelectorQuery<E: TElement> {
    /// The output of the query.
    type Output;

    /// Whether the query should stop after the first element has been matched.
    fn should_stop_after_first_match() -> bool;

    /// Append an element matching after the first query.
    fn append_element(output: &mut Self::Output, element: E);

    /// Returns true if the output is empty.
    fn is_empty(output: &Self::Output) -> bool;
}

/// The result of a querySelectorAll call.
pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;

/// A query for all the elements in a subtree.
pub struct QueryAll;

impl<E: TElement> SelectorQuery<E> for QueryAll {
    type Output = QuerySelectorAllResult<E>;

    fn should_stop_after_first_match() -> bool { false }

    fn append_element(output: &mut Self::Output, element: E) {
        output.push(element);
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_empty()
    }
}

/// A query for the first in-tree match of all the elements in a subtree.
pub struct QueryFirst;

impl<E: TElement> SelectorQuery<E> for QueryFirst {
    type Output = Option<E>;

    fn should_stop_after_first_match() -> bool { true }

    fn append_element(output: &mut Self::Output, element: E) {
        if output.is_none() {
            *output = Some(element)
        }
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_none()
    }
}

struct QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    results: &'a mut Q::Output,
    matching_context: MatchingContext<'a, E::Impl>,
    selector_list: &'a SelectorList<E::Impl>,
}

impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    fn light_tree_only(&self) -> bool { true }

    fn collect_invalidations(
        &mut self,
        element: E,
        self_invalidations: &mut InvalidationVector<'a>,
        descendant_invalidations: &mut InvalidationVector<'a>,
        _sibling_invalidations: &mut InvalidationVector<'a>,
    ) -> bool {
        // TODO(emilio): If the element is not a root element, and
        // selector_list has any descendant combinator, we need to do extra work
        // in order to handle properly things like:
        //
        //

        //

        //

        //

        //

        //
        // b.querySelector('#a div'); // Should return "c".
        //
        // For now, assert it's a root element.
        debug_assert!(element.parent_element().is_none());

        let target_vector =
            if self.matching_context.scope_element.is_some() {
                descendant_invalidations
            } else {
                self_invalidations
            };

        for selector in self.selector_list.0.iter() {
            target_vector.push(Invalidation::new(selector, 0))
        }

        false
    }

    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
        &mut self.matching_context
    }

    fn should_process_descendants(&mut self, _: E) -> bool {
        if Q::should_stop_after_first_match() {
            return Q::is_empty(&self.results)
        }

        true
    }

    fn invalidated_self(&mut self, e: E) {
        Q::append_element(self.results, e);
    }

    fn recursion_limit_exceeded(&mut self, _e: E) {}
    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
}

fn collect_all_elements<E, Q, F>(
    root: E::ConcreteNode,
    results: &mut Q::Output,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    for node in root.dom_descendants() {
        let element = match node.as_element() {
            Some(e) => e,
            None => continue,
        };

        if !filter(element) {
            continue;
        }

        Q::append_element(results, element);
        if Q::should_stop_after_first_match() {
            return;
        }
    }
}

/// Returns whether a given element is descendant of a given `root` node.
///
/// NOTE(emilio): if root == element, this returns false.
fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
where
    E: TElement,
{
    let mut current = element.as_node().parent_node();
    while let Some(n) = current.take() {
        if n == root {
            return true;
        }

        current = n.parent_node();
    }
    false
}

/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
    root: E::ConcreteNode,
    component: &Component<E::Impl>,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        }
        Component::ID(ref id) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();

            if case_sensitivity == CaseSensitivity::CaseSensitive &&
               root.is_in_document()
            {
                let doc = root.owner_doc();
                if let Ok(elements) = doc.elements_with_id(id) {
                    if root == doc.as_node() {
                        for element in elements {
                            Q::append_element(results, *element);
                            if Q::should_stop_after_first_match() {
                                break;
                            }
                        }
                    } else {
                        for element in elements {
                            if !element_is_descendant_of(*element, root) {
                                continue;
                            }

                            Q::append_element(results, *element);
                            if Q::should_stop_after_first_match() {
                                break;
                            }
                        }
                    }
                    return Ok(())
                }
            }

            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_id(id, case_sensitivity)
            })
        }
        Component::Class(ref class) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_class(class, case_sensitivity)
            })
        }
        Component::LocalName(LocalName { ref name, ref lower_name }) => {
            collect_all_elements::<E, Q, _>(root, results, |element| {
                if element.is_html_element_in_html_document() {
                    element.get_local_name() == lower_name.borrow()
                } else {
                    element.get_local_name() == name.borrow()
                }
            })
        }
        // TODO(emilio): More fast paths?
        _ => {
            return Err(())
        }
    }

    Ok(())
}

/// Fast paths for a given selector query.
///
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
/// replaced by HolyJit :)
fn query_selector_fast<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    // We need to return elements in document order, and reordering them
    // afterwards is kinda silly.
    if selector_list.0.len() > 1 {
        return Err(());
    }

    let selector = &selector_list.0[0];

    // Let's just care about the easy cases for now.
    if selector.len() == 1 {
        return query_selector_single_query::<E, Q>(
            root,
            selector.iter().next().unwrap(),
            results,
            quirks_mode,
        );
    }

    // FIXME(emilio): Implement better optimizations for compound selectors and
    // such.
    Err(())
}

// Slow path for a given selector query.
fn query_selector_slow<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    collect_all_elements::<E, Q, _>(root, results, |element| {
        matching::matches_selector_list(selector_list, &element, matching_context)
    });
}

/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
pub fn query_selector<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    use invalidation::element::invalidator::TreeStyleInvalidator;

    let quirks_mode = root.owner_doc().quirks_mode();
    let fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        quirks_mode,
    );

    if fast_result.is_ok() {
        return;
    }

    // Slow path: Use the invalidation machinery if we're a root, and tree
    // traversal otherwise.
    //
    // See the comment in collect_invalidations to see why only if we're a root.
    let mut nth_index_cache = NthIndexCache::default();
    let mut matching_context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );

    let root_element = root.as_element();
    matching_context.scope_element = root_element.map(|e| e.opaque());

    // The invalidation mechanism is only useful in presence of combinators.
    //
    // We could do that check properly here, though checking the length of the
    // selectors is a good heuristic.
    let invalidation_may_be_useful =
        selector_list.0.iter().any(|s| s.len() > 1);

    if root_element.is_some() || !invalidation_may_be_useful {
        query_selector_slow::<E, Q>(
            root,
            selector_list,
            results,
            &mut matching_context,
        );
    } else {
        let mut processor = QuerySelectorProcessor::<E, Q> {
            results,
            matching_context,
            selector_list,
        };

        for node in root.dom_children() {
            if let Some(e) = node.as_element() {
                TreeStyleInvalidator::new(
                    e,
                    /* stack_limit_checker = */ None,
                    &mut processor,
                ).invalidate();
            }
        }
    }
}
<commit_msg>style: Optimize all ids in the rightmost compound selector.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Generic implementations of some DOM APIs so they can be shared between Servo
//! and Gecko.

use Atom;
use context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{Combinator, Component, LocalName};
use smallvec::SmallVec;
use std::borrow::Borrow;

/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
    element: &E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> bool
where
    E: Element,
{
    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        None,
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());
    matching::matches_selector_list(selector_list, element, &mut context)
}

/// <https://dom.spec.whatwg.org/#dom-element-closest>
pub fn element_closest<E>(
    element: E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> Option<E>
where
    E: Element,
{
    let mut nth_index_cache = NthIndexCache::default();

    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());

    let mut current = Some(element);
    while let Some(element) = current.take() {
        if matching::matches_selector_list(selector_list, &element, &mut context) {
            return Some(element);
        }
        current = element.parent_element();
    }

    return None;
}

/// A selector query abstraction, in order to be generic over QuerySelector and
/// QuerySelectorAll.
pub trait SelectorQuery<E: TElement> {
    /// The output of the query.
    type Output;

    /// Whether the query should stop after the first element has been matched.
    fn should_stop_after_first_match() -> bool;

    /// Append an element matching after the first query.
    fn append_element(output: &mut Self::Output, element: E);

    /// Returns true if the output is empty.
    fn is_empty(output: &Self::Output) -> bool;
}

/// The result of a querySelectorAll call.
pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;

/// A query for all the elements in a subtree.
pub struct QueryAll;

impl<E: TElement> SelectorQuery<E> for QueryAll {
    type Output = QuerySelectorAllResult<E>;

    fn should_stop_after_first_match() -> bool { false }

    fn append_element(output: &mut Self::Output, element: E) {
        output.push(element);
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_empty()
    }
}

/// A query for the first in-tree match of all the elements in a subtree.
pub struct QueryFirst;

impl<E: TElement> SelectorQuery<E> for QueryFirst {
    type Output = Option<E>;

    fn should_stop_after_first_match() -> bool { true }

    fn append_element(output: &mut Self::Output, element: E) {
        if output.is_none() {
            *output = Some(element)
        }
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_none()
    }
}

struct QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    results: &'a mut Q::Output,
    matching_context: MatchingContext<'a, E::Impl>,
    selector_list: &'a SelectorList<E::Impl>,
}

impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    fn light_tree_only(&self) -> bool { true }

    fn collect_invalidations(
        &mut self,
        element: E,
        self_invalidations: &mut InvalidationVector<'a>,
        descendant_invalidations: &mut InvalidationVector<'a>,
        _sibling_invalidations: &mut InvalidationVector<'a>,
    ) -> bool {
        // TODO(emilio): If the element is not a root element, and
        // selector_list has any descendant combinator, we need to do extra work
        // in order to handle properly things like:
        //
        //

        //

        //

        //

        //

        //
        // b.querySelector('#a div'); // Should return "c".
        //
        // For now, assert it's a root element.
        debug_assert!(element.parent_element().is_none());

        let target_vector =
            if self.matching_context.scope_element.is_some() {
                descendant_invalidations
            } else {
                self_invalidations
            };

        for selector in self.selector_list.0.iter() {
            target_vector.push(Invalidation::new(selector, 0))
        }

        false
    }

    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
        &mut self.matching_context
    }

    fn should_process_descendants(&mut self, _: E) -> bool {
        if Q::should_stop_after_first_match() {
            return Q::is_empty(&self.results)
        }

        true
    }

    fn invalidated_self(&mut self, e: E) {
        Q::append_element(self.results, e);
    }

    fn recursion_limit_exceeded(&mut self, _e: E) {}
    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
}

fn collect_all_elements<E, Q, F>(
    root: E::ConcreteNode,
    results: &mut Q::Output,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    for node in root.dom_descendants() {
        let element = match node.as_element() {
            Some(e) => e,
            None => continue,
        };

        if !filter(element) {
            continue;
        }

        Q::append_element(results, element);
        if Q::should_stop_after_first_match() {
            return;
        }
    }
}

/// Returns whether a given element is descendant of a given `root` node.
///
/// NOTE(emilio): if root == element, this returns false.
fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
where
    E: TElement,
{
    let mut current = element.as_node().parent_node();
    while let Some(n) = current.take() {
        if n == root {
            return true;
        }

        current = n.parent_node();
    }
    false
}

fn find_elements_with_id<E, Q, F>(
    root: E::ConcreteNode,
    id: &Atom,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
    if case_sensitivity == CaseSensitivity::CaseSensitive &&
       root.is_in_document()
    {
        let doc = root.owner_doc();
        if let Ok(elements) = doc.elements_with_id(id) {
            if root == doc.as_node() {
                for element in elements {
                    if !filter(*element) {
                        continue;
                    }

                    Q::append_element(results, *element);
                    if Q::should_stop_after_first_match() {
                        break;
                    }
                }
            } else {
                for element in elements {
                    if !element_is_descendant_of(*element, root) {
                        continue;
                    }

                    if !filter(*element) {
                        continue;
                    }

                    Q::append_element(results, *element);
                    if Q::should_stop_after_first_match() {
                        break;
                    }
                }
            }
        }
        return;
    }

    collect_all_elements::<E, Q, _>(root, results, |element| {
        element.has_id(id, case_sensitivity) && filter(element)
    });
}

/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
    root: E::ConcreteNode,
    component: &Component<E::Impl>,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        }
        Component::ID(ref id) => {
            find_elements_with_id::<E, Q, _>(
                root,
                id,
                results,
                quirks_mode,
                |_| true,
            )
        }
        Component::Class(ref class) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_class(class, case_sensitivity)
            })
        }
        Component::LocalName(LocalName { ref name, ref lower_name }) => {
            collect_all_elements::<E, Q, _>(root, results, |element| {
                if element.is_html_element_in_html_document() {
                    element.get_local_name() == lower_name.borrow()
                } else {
                    element.get_local_name() == name.borrow()
                }
            })
        }
        // TODO(emilio): More fast paths?
        _ => {
            return Err(())
        }
    }

    Ok(())
}

/// Fast paths for a given selector query.
///
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
/// replaced by HolyJit :)
fn query_selector_fast<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    // We need to return elements in document order, and reordering them
    // afterwards is kinda silly.
    if selector_list.0.len() > 1 {
        return Err(());
    }

    let selector = &selector_list.0[0];
    let quirks_mode = matching_context.quirks_mode();

    // Let's just care about the easy cases for now.
    if selector.len() == 1 {
        return query_selector_single_query::<E, Q>(
            root,
            selector.iter().next().unwrap(),
            results,
            quirks_mode,
        );
    }

    let mut iter = selector.iter();
    let mut combinator: Option<Combinator> = None;

    loop {
        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));

        for component in &mut iter {
            match *component {
                Component::ID(ref id) => {
                    if combinator.is_none() {
                        // In the rightmost compound, just find descendants of
                        // root that match the selector list with that id.
                        find_elements_with_id::<E, Q, _>(
                            root,
                            id,
                            results,
                            quirks_mode,
                            |e| {
                                matching::matches_selector_list(
                                    selector_list,
                                    &e,
                                    matching_context,
                                )
                            }
                        );

                        return Ok(());
                    }

                    // TODO(emilio): Find descendants of `root` that are
                    // descendants of an element with a given `id`.
                }
                _ => {},
            }
        }

        loop {
            let next_combinator = match iter.next_sequence() {
                None => return Err(()),
                Some(c) => c,
            };

            // We don't want to scan stuff affected by sibling combinators,
            // given we scan the subtree of elements with a given id (and we
            // don't want to care about scanning the siblings' subtrees).
            if next_combinator.is_sibling() {
                // Advance to the next combinator.
                for _ in &mut iter {}
                continue;
            }

            combinator = Some(next_combinator);
            break;
        }
    }
}

// Slow path for a given selector query.
fn query_selector_slow<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    collect_all_elements::<E, Q, _>(root, results, |element| {
        matching::matches_selector_list(selector_list, &element, matching_context)
    });
}

/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
pub fn query_selector<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    use invalidation::element::invalidator::TreeStyleInvalidator;

    let quirks_mode = root.owner_doc().quirks_mode();

    let mut nth_index_cache = NthIndexCache::default();
    let mut matching_context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );

    let root_element = root.as_element();
    matching_context.scope_element = root_element.map(|e| e.opaque());

    let fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        &mut matching_context,
    );

    if fast_result.is_ok() {
        return;
    }

    // Slow path: Use the invalidation machinery if we're a root, and tree
    // traversal otherwise.
    //
    // See the comment in collect_invalidations to see why only if we're a root.
    //
    // The invalidation mechanism is only useful in presence of combinators.
    //
    // We could do that check properly here, though checking the length of the
    // selectors is a good heuristic.
    let invalidation_may_be_useful =
        selector_list.0.iter().any(|s| s.len() > 1);

    if root_element.is_some() || !invalidation_may_be_useful {
        query_selector_slow::<E, Q>(
            root,
            selector_list,
            results,
            &mut matching_context,
        );
    } else {
        let mut processor = QuerySelectorProcessor::<E, Q> {
            results,
            matching_context,
            selector_list,
        };

        for node in root.dom_children() {
            if let Some(e) = node.as_element() {
                TreeStyleInvalidator::new(
                    e,
                    /* stack_limit_checker = */ None,
                    &mut processor,
                ).invalidate();
            }
        }
    }
}
<|endoftext|>


<commit_before>use fuse::{FileType, Request, ReplyDirectory};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::inode;
use gd;
use super::shared;

pub fn readdir(fs: &mut GoodDataFS,
               _req: &Request,
               ino: u64,
               _fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {
    println!("GoodDataFS::readdir() - Reading inode {} - {:?}",
             ino,
             inode::Inode::deserialize(ino));
    if ino == constants::INODE_ROOT {
        if offset == 0 {
            reply.add(constants::INODE_ROOT,
                      2,
                      FileType::RegularFile,
                      constants::USER_JSON_FILENAME);
            reply.add(constants::INODE_ROOT,
                      3,
                      FileType::Directory,
                      constants::PROJECTS_DIRNAME);
        }
        reply.ok();
    } else if ino == constants::INODE_PROJECTS {
        if offset == 0 {
            fs.client.projects_fetch();

            let mut i: u64 = 0;
            let client: &gd::GoodDataClient = fs.client();
            let projects = client.projects().as_ref();
            for project in projects.unwrap().into_iter() {
                let title: &String = project.project()
                    .meta()
                    .title()
                    .as_ref()
                    .unwrap();

                let inode = (i + 1) << 48;
                println!("GoodDataFS::readdir() - Adding path {:?}, inode {}",
                         title,
                         inode);
                // let sanitized = re.replace_all(&title[..], "_");
                reply.add(constants::INODE_PROJECTS, i + 2, FileType::Directory, title);
                i += 1;
            }

            reply.add(constants::INODE_PROJECTS_JSON,
                      i + 3,
                      FileType::RegularFile,
                      constants::PROJECTS_JSON_FILENAME);
        }
        reply.ok();
    } else {
        let inode = inode::Inode::deserialize(ino);
        if inode.project > 0 {
            if offset == 0 {
                readdir_project(inode.project as u16, &mut reply);
            }
            reply.ok();
        } else {
            println!("GoodDataFS::readdir() - Unknown inode {}", ino);
            reply.error(ENOENT);
        }
    }
}

fn readdir_project(projectid: u16, reply: &mut ReplyDirectory) {
    let mut offset = 2;
    for &(category, reserved, obj_type, path) in shared::ITEMS.into_iter() {
        let inode = inode::Inode {
            project: projectid,
            category: category,
            item: 0,
            reserved: reserved,
        };

        let fileinode: u64 = inode.into();
        println!("GoodDataFS::readdir() - Adding inode {} - {:?}, project {}, path {}",
                 fileinode,
                 &inode,
                 projectid - 1,
                 path);
        reply.add(fileinode, offset, obj_type, path);

        offset += 1;
    }
}
<commit_msg>Use matching pattern<commit_after>use fuse::{FileType, Request, ReplyDirectory};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::inode;
use gd;
use super::shared;

pub fn readdir(fs: &mut GoodDataFS,
               _req: &Request,
               ino: u64,
               _fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {
    let inode = inode::Inode::deserialize(ino);
    println!("GoodDataFS::readdir() - Reading inode {} - {:?}",
             ino,
             inode);
    match ino {
        constants::INODE_ROOT => {
            if offset == 0 {
                root(&mut reply);
            }
            reply.ok();
        }
        constants::INODE_PROJECTS => {
            if offset == 0 {
                projects(fs, &mut reply);
            }
            reply.ok();
        }
        _ => {
            if inode.project > 0 {
                if offset == 0 {
                    project(inode.project as u16, &mut reply);
                }
                reply.ok();
            } else {
                println!("GoodDataFS::readdir() - Unknown inode {}", ino);
                reply.error(ENOENT);
            }
        }
    }
}

fn project(projectid: u16, reply: &mut ReplyDirectory) {
    let mut offset = 2;
    for &(category, reserved, obj_type, path) in shared::ITEMS.into_iter() {
        let inode = inode::Inode {
            project: projectid,
            category: category,
            item: 0,
            reserved: reserved,
        };

        let fileinode: u64 = inode.into();
        println!("GoodDataFS::readdir() - Adding inode {} - {:?}, project {}, path {}",
                 fileinode,
                 &inode,
                 projectid - 1,
                 path);
        reply.add(fileinode, offset, obj_type, path);

        offset += 1;
    }
}

fn projects(fs: &mut GoodDataFS, reply: &mut ReplyDirectory) {
    fs.client.projects_fetch();

    let mut i: u64 = 0;
    let client: &gd::GoodDataClient = fs.client();
    let projects = client.projects().as_ref();
    for project in projects.unwrap().into_iter() {
        let title: &String = project.project()
            .meta()
            .title()
            .as_ref()
            .unwrap();

        let inode = (i + 1) << 48;
        println!("GoodDataFS::readdir() - Adding path {:?}, inode {}",
                 title,
                 inode);
        // let sanitized = re.replace_all(&title[..], "_");
        reply.add(constants::INODE_PROJECTS, i + 2, FileType::Directory, title);
        i += 1;
    }

    reply.add(constants::INODE_PROJECTS_JSON,
              i + 3,
              FileType::RegularFile,
              constants::PROJECTS_JSON_FILENAME);
}

fn root(reply: &mut ReplyDirectory) {
    reply.add(constants::INODE_ROOT,
              2,
              FileType::RegularFile,
              constants::USER_JSON_FILENAME);
    reply.add(constants::INODE_ROOT,
              3,
              FileType::Directory,
              constants::PROJECTS_DIRNAME);
}
<|endoftext|>