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

// compile-flags:--test
// ignore-emscripten

// NB: These tests kill child processes. Valgrind sees these children as leaking
// memory, which makes for some *confusing* logs. That's why these are here
// instead of in std.

#![reexport_test_harness_main = "test_main"]
#![feature(libc, std_misc, duration)]

extern crate libc;

use std::process::{self, Command, Child, Output, Stdio};
use std::str;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;

macro_rules! t {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("error: {}", e) })
}

#[test]
fn test_destroy_once() {
    let mut p = sleeper();
    t!(p.kill());
}

#[cfg(unix)]
pub fn sleeper() -> Child {
    t!(Command::new("sleep").arg("1000").spawn())
}
#[cfg(windows)]
pub fn sleeper() -> Child {
    // There's a `timeout` command on windows, but it doesn't like having
    // its output piped, so instead just ping ourselves a few times with
    // gaps in between so we're sure this process is alive for awhile
    t!(Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn())
}

#[test]
fn test_destroy_twice() {
    let mut p = sleeper();
    t!(p.kill()); // this shouldn't crash...
    let _ = p.kill(); // ...and nor should this (and nor should the destructor)
}

#[test]
fn test_destroy_actually_kills() {
    let cmd = if cfg!(windows) {
        "cmd"
    } else if cfg!(target_os = "android") {
        "/system/bin/cat"
    } else {
        "cat"
    };

    // this process will stay alive indefinitely trying to read from stdin
    let mut p = t!(Command::new(cmd)
                           .stdin(Stdio::piped())
                           .spawn());

    t!(p.kill());

    // Don't let this test time out, this should be quick
    let (tx, rx) = channel();
    thread::spawn(move|| {
        thread::sleep_ms(1000);
        if rx.try_recv().is_err() {
            process::exit(1);
        }
    });
    let code = t!(p.wait()).code();
    if cfg!(windows) {
        assert!(code.is_some());
    } else {
        assert!(code.is_none());
    }
    tx.send(());
}
<commit_msg>Auto merge of #43607 - marioidival:master, r=alexcrichton<commit_after>// Copyright 2012-2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags:--test
// ignore-emscripten

// NB: These tests kill child processes. Valgrind sees these children as leaking
// memory, which makes for some *confusing* logs. That's why these are here
// instead of in std.

#![feature(libc, std_misc, duration)]

extern crate libc;

use std::process::{self, Command, Child, Output, Stdio};
use std::str;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;

macro_rules! t {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("error: {}", e) })
}

#[test]
fn test_destroy_once() {
    let mut p = sleeper();
    t!(p.kill());
}

#[cfg(unix)]
pub fn sleeper() -> Child {
    t!(Command::new("sleep").arg("1000").spawn())
}
#[cfg(windows)]
pub fn sleeper() -> Child {
    // There's a `timeout` command on windows, but it doesn't like having
    // its output piped, so instead just ping ourselves a few times with
    // gaps in between so we're sure this process is alive for awhile
    t!(Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn())
}

#[test]
fn test_destroy_twice() {
    let mut p = sleeper();
    t!(p.kill()); // this shouldn't crash...
    let _ = p.kill(); // ...and nor should this (and nor should the destructor)
}

#[test]
fn test_destroy_actually_kills() {
    let cmd = if cfg!(windows) {
        "cmd"
    } else if cfg!(target_os = "android") {
        "/system/bin/cat"
    } else {
        "cat"
    };

    // this process will stay alive indefinitely trying to read from stdin
    let mut p = t!(Command::new(cmd)
                           .stdin(Stdio::piped())
                           .spawn());

    t!(p.kill());

    // Don't let this test time out, this should be quick
    let (tx, rx) = channel();
    thread::spawn(move|| {
        thread::sleep_ms(1000);
        if rx.try_recv().is_err() {
            process::exit(1);
        }
    });
    let code = t!(p.wait()).code();
    if cfg!(windows) {
        assert!(code.is_some());
    } else {
        assert!(code.is_none());
    }
    tx.send(());
}
<|endoftext|>


<commit_before>use crate::rpc::{RPCEvent, RPCMessage, Rpc};
use crate::NetworkConfig;
use futures::prelude::*;
use libp2p::{
    core::{
        swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
        PublicKey,
    },
    gossipsub::{Gossipsub, GossipsubEvent},
    identify::{protocol::IdentifyInfo, Identify, IdentifyEvent},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use slog::{debug, o};
use types::Topic;

/// Builds the network behaviour for the libp2p Swarm.
/// Implements gossipsub message routing.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    // TODO: Add Kademlia for peer discovery
    /// The events generated by this behaviour to be consumed in the swarm poll.
    serenity_rpc: Rpc<TSubstream>,
    /// Allows discovery of IP addresses for peers on the network.
    identify: Identify<TSubstream>,
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    #[behaviour(ignore)]
    /// Logger for behaviour actions.
    log: slog::Logger,
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(message) => {
                let gs_message = String::from_utf8_lossy(&message.data);
                // TODO: Remove this type - debug only
                self.events
                    .push(BehaviourEvent::Message(gs_message.to_string()))
            }
            _ => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Identified {
                peer_id, mut info, ..
            } => {
                if info.listen_addrs.len() > 20 {
                    debug!(
                        self.log,
                        "More than 20 peers have been identified, truncating"
                    );
                    info.listen_addrs.truncate(20);
                }
                self.events.push(BehaviourEvent::Identified(peer_id, info));
            }
            IdentifyEvent::Error { .. } => {}
            IdentifyEvent::SendBack { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self {
        let local_peer_id = local_public_key.clone().into_peer_id();
        let identify_config = net_conf.identify_config.clone();
        let behaviour_log = log.new(o!());

        Behaviour {
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            serenity_rpc: Rpc::new(log),
            identify: Identify::new(
                identify_config.version,
                identify_config.user_agent,
                local_public_key,
            ),
            events: Vec::new(),
            log: behaviour_log,
        }
    }

    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.serenity_rpc.send_rpc(peer_id, rpc_event);
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    RPC(PeerId, RPCEvent),
    PeerDialed(PeerId),
    Identified(PeerId, IdentifyInfo),
    // TODO: This is a stub at the moment
    Message(String),
}
<commit_msg>Add Ping protocol to lighthouse<commit_after>use crate::rpc::{RPCEvent, RPCMessage, Rpc};
use crate::NetworkConfig;
use futures::prelude::*;
use libp2p::{
    core::{
        swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
        PublicKey,
    },
    gossipsub::{Gossipsub, GossipsubEvent},
    identify::{protocol::IdentifyInfo, Identify, IdentifyEvent},
    ping::{Ping, PingEvent},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use slog::{debug, o};
use types::Topic;

/// Builds the network behaviour for the libp2p Swarm.
/// Implements gossipsub message routing.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    // TODO: Add Kademlia for peer discovery
    /// The events generated by this behaviour to be consumed in the swarm poll.
    serenity_rpc: Rpc<TSubstream>,
    /// Allows discovery of IP addresses for peers on the network.
    identify: Identify<TSubstream>,
    /// Keep regular connection to peers and disconnect if absent.
    // TODO: Keepalive, likely remove this later.
    // TODO: Make the ping time customizeable.
    ping: Ping<TSubstream>,
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    /// Logger for behaviour actions.
    #[behaviour(ignore)]
    log: slog::Logger,
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(message) => {
                let gs_message = String::from_utf8_lossy(&message.data);
                // TODO: Remove this type - debug only
                self.events
                    .push(BehaviourEvent::Message(gs_message.to_string()))
            }
            _ => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Identified {
                peer_id, mut info, ..
            } => {
                if info.listen_addrs.len() > 20 {
                    debug!(
                        self.log,
                        "More than 20 peers have been identified, truncating"
                    );
                    info.listen_addrs.truncate(20);
                }
                self.events.push(BehaviourEvent::Identified(peer_id, info));
            }
            IdentifyEvent::Error { .. } => {}
            IdentifyEvent::SendBack { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: PingEvent) {
        // not interested in ping responses at the moment.
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self {
        let local_peer_id = local_public_key.clone().into_peer_id();
        let identify_config = net_conf.identify_config.clone();
        let behaviour_log = log.new(o!());

        Behaviour {
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            serenity_rpc: Rpc::new(log),
            identify: Identify::new(
                identify_config.version,
                identify_config.user_agent,
                local_public_key,
            ),
            ping: Ping::new(),
            events: Vec::new(),
            log: behaviour_log,
        }
    }

    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.serenity_rpc.send_rpc(peer_id, rpc_event);
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    RPC(PeerId, RPCEvent),
    PeerDialed(PeerId),
    Identified(PeerId, IdentifyInfo),
    // TODO: This is a stub at the moment
    Message(String),
}
<|endoftext|>


<commit_before>/*!
The ignore module is responsible for managing the state required to determine
whether a *single* file path should be searched or not.

In general, there are two ways to ignore a particular file:

1. Specify an ignore rule in some "global" configuration, such as a
   $HOME/.rgignore or on the command line.
2. A specific ignore file (like .gitignore) found during directory traversal.

The `IgnoreDir` type handles ignore patterns for any one particular directory
(including "global" ignore patterns), while the `Ignore` type handles a stack
of `IgnoreDir`s for use during directory traversal.
*/

use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use gitignore::{self, Gitignore, GitignoreBuilder, Match, Pattern};
use types::Types;

const IGNORE_NAMES: &'static [&'static str] = &[
    ".gitignore",
    ".agignore",
    ".rgignore",
];

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Gitignore(gitignore::Error),
    Io {
        path: PathBuf,
        err: io::Error,
    },
}

impl Error {
    fn from_io<P: AsRef<Path>>(path: P, err: io::Error) -> Error {
        Error::Io { path: path.as_ref().to_path_buf(), err: err }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Gitignore(ref err) => err.description(),
            Error::Io { ref err, .. } => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Gitignore(ref err) => err.fmt(f),
            Error::Io { ref path, ref err } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

impl From<gitignore::Error> for Error {
    fn from(err: gitignore::Error) -> Error {
        Error::Gitignore(err)
    }
}

/// Ignore represents a collection of ignore patterns organized by directory.
/// In particular, a stack is maintained, where the top of the stack
/// corresponds to the current directory being searched and the bottom of the
/// stack represents the root of a search. Ignore patterns at the top of the
/// stack take precedence over ignore patterns at the bottom of the stack.
pub struct Ignore {
    /// A stack of ignore patterns at each directory level of traversal.
    /// A directory that contributes no ignore patterns is `None`.
    stack: Vec<Option<IgnoreDir>>,
    /// A set of override globs that are always checked first. A match (whether
    /// it's whitelist or blacklist) trumps anything in stack.
    overrides: Overrides,
    /// A file type matcher.
    types: Types,
    ignore_hidden: bool,
    no_ignore: bool,
}

impl Ignore {
    /// Create an empty set of ignore patterns.
    pub fn new() -> Ignore {
        Ignore {
            stack: vec![],
            overrides: Overrides::new(None),
            types: Types::empty(),
            ignore_hidden: true,
            no_ignore: false,
        }
    }

    /// Set whether hidden files/folders should be ignored (defaults to true).
    pub fn ignore_hidden(&mut self, yes: bool) -> &mut Ignore {
        self.ignore_hidden = yes;
        self
    }

    /// When set, ignore files are ignored.
    pub fn no_ignore(&mut self, yes: bool) -> &mut Ignore {
        self.no_ignore = yes;
        self
    }

    /// Add a set of globs that overrides all other match logic.
    pub fn add_override(&mut self, gi: Gitignore) -> &mut Ignore {
        self.overrides = Overrides::new(Some(gi));
        self
    }

    /// Add a file type matcher. The file type matcher has the lowest
    /// precedence.
    pub fn add_types(&mut self, types: Types) -> &mut Ignore {
        self.types = types;
        self
    }

    /// Push parent directories of `path` on to the stack.
    pub fn push_parents<P: AsRef<Path>>(
        &mut self,
        path: P,
    ) -> Result<(), Error> {
        let path = try!(path.as_ref().canonicalize().map_err(|err| {
            Error::from_io(path.as_ref(), err)
        }));
        let mut path = &*path;
        let mut saw_git = path.join(".git").is_dir();
        let mut ignore_names = IGNORE_NAMES.to_vec();
        let mut ignore_dir_results = vec![];
        while let Some(parent) = path.parent() {
            if self.no_ignore {
                ignore_dir_results.push(Ok(None));
            } else {
                if saw_git {
                    ignore_names.retain(|&name| name != ".gitignore");
                } else {
                    saw_git = parent.join(".git").is_dir();
                }
                let ignore_dir_result =
                    IgnoreDir::with_ignore_names(parent, ignore_names.iter());
                ignore_dir_results.push(ignore_dir_result);
            }
            path = parent;
        }

        for ignore_dir_result in ignore_dir_results.into_iter().rev() {
            try!(self.push_ignore_dir(ignore_dir_result));
        }
        Ok(())
    }

    /// Add a directory to the stack.
    ///
    /// Note that even if this returns an error, the directory is added to the
    /// stack (and therefore should be popped).
    pub fn push<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        if self.no_ignore {
            self.stack.push(None);
            return Ok(());
        }
        self.push_ignore_dir(IgnoreDir::new(path))
    }

    /// Pushes the result of building a directory matcher on to the stack.
    ///
    /// If the result given contains an error, then it is returned.
    pub fn push_ignore_dir(
        &mut self,
        result: Result<Option<IgnoreDir>, Error>,
    ) -> Result<(), Error> {
        match result {
            Ok(id) => {
                self.stack.push(id);
                Ok(())
            }
            Err(err) => {
                // Don't leave the stack in an inconsistent state.
                self.stack.push(None);
                Err(err)
            }
        }
    }

    /// Pop a directory from the stack.
    ///
    /// This panics if the stack is empty.
    pub fn pop(&mut self) {
        self.stack.pop().expect("non-empty stack");
    }

    /// Returns true if and only if the given file path should be ignored.
    pub fn ignored<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> bool {
        let path = path.as_ref();
        let mat = self.overrides.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        if self.ignore_hidden && is_hidden(&path) {
            debug!("{} ignored because it is hidden", path.display());
            return true;
        }
        for id in self.stack.iter().rev().filter_map(|id| id.as_ref()) {
            let mat = id.matched(path, is_dir);
            if let Some(is_ignored) = self.ignore_match(path, mat) {
                if is_ignored {
                    return true;
                }
                // If this path is whitelisted by an ignore, then fallthrough
                // and let the file type matcher have a say.
                break;
            }
        }
        let mat = self.types.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        false
    }

    /// Returns true if the given match says the given pattern should be
    /// ignored or false if the given pattern should be explicitly whitelisted.
    /// Returns None otherwise.
    pub fn ignore_match<P: AsRef<Path>>(
        &self,
        path: P,
        mat: Match,
    ) -> Option<bool> {
        let path = path.as_ref();
        match mat {
            Match::Whitelist(ref pat) => {
                debug!("{} whitelisted by {:?}", path.display(), pat);
                Some(false)
            }
            Match::Ignored(ref pat) => {
                debug!("{} ignored by {:?}", path.display(), pat);
                Some(true)
            }
            Match::None => None,
        }
    }
}

/// IgnoreDir represents a set of ignore patterns retrieved from a single
/// directory.
pub struct IgnoreDir {
    /// The path to this directory as given.
    path: PathBuf,
    /// A single accumulation of glob patterns for this directory, matched
    /// using gitignore semantics.
    ///
    /// This will include patterns from rgignore as well. The patterns are
    /// ordered so that precedence applies automatically (e.g., rgignore
    /// patterns procede gitignore patterns).
    gi: Option<Gitignore>,
    // TODO(burntsushi): Matching other types of glob patterns that don't
    // conform to gitignore will probably require refactoring this approach.
}

impl IgnoreDir {
    /// Create a new matcher for the given directory.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Option<IgnoreDir>, Error> {
        IgnoreDir::with_ignore_names(path, IGNORE_NAMES.iter())
    }

    /// Create a new matcher for the given directory using only the ignore
    /// patterns found in the file names given.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    ///
    /// Note that the order of the names given is meaningful. Names appearing
    /// later in the list have precedence over names appearing earlier in the
    /// list.
    pub fn with_ignore_names<P: AsRef<Path>, S, I>(
        path: P,
        names: I,
    ) -> Result<Option<IgnoreDir>, Error>
    where P: AsRef<Path>, S: AsRef<str>, I: Iterator<Item=S> {
        let mut id = IgnoreDir {
            path: path.as_ref().to_path_buf(),
            gi: None,
        };
        let mut ok = false;
        let mut builder = GitignoreBuilder::new(&id.path);
        // The ordering here is important. Later globs have higher precedence.
        for name in names {
            ok = builder.add_path(id.path.join(name.as_ref())).is_ok() || ok;
        }
        if !ok {
            Ok(None)
        } else {
            id.gi = Some(try!(builder.build()));
            Ok(Some(id))
        }
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this directory. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of this directory) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside
    /// directly in this directory.
    ///
    /// If the given path has a `./` prefix then it is stripped before
    /// matching.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        self.gi.as_ref()
            .map(|gi| gi.matched(path, is_dir))
            .unwrap_or(Match::None)
    }
}

/// Manages a set of overrides provided explicitly by the end user.
struct Overrides {
    gi: Option<Gitignore>,
    unmatched_pat: Pattern,
}

impl Overrides {
    /// Creates a new set of overrides from the gitignore matcher provided.
    /// If no matcher is provided, then the resulting overrides have no effect.
    fn new(gi: Option<Gitignore>) -> Overrides {
        Overrides {
            gi: gi,
            unmatched_pat: Pattern {
                from: Path::new("<argv>").to_path_buf(),
                original: "<none>".to_string(),
                pat: "<none>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Returns a match for the given path against this set of overrides.
    ///
    /// If there are no overrides, then this always returns Match::None.
    ///
    /// If there is at least one positive override, then this never returns
    /// Match::None (and interpreting non-matches as ignored) unless is_dir
    /// is true.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        self.gi.as_ref()
            .map(|gi| {
                let path = &*path.to_string_lossy();
                let mat = gi.matched_utf8(path, is_dir).invert();
                if mat.is_none() && !is_dir {
                    if gi.num_ignores() > 0 {
                        return Match::Ignored(&self.unmatched_pat);
                    }
                }
                mat
            })
            .unwrap_or(Match::None)
    }
}

fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    if let Some(name) = path.as_ref().file_name() {
        name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
    } else {
        false
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use gitignore::GitignoreBuilder;
    use super::IgnoreDir;

    macro_rules! ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(id.matched($path, false).is_ignored());
            }
        };
    }

    macro_rules! not_ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(!id.matched($path, false).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs");
    ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs");
    ignored_dir!(id3, ROOT, "!src/main.rs", "*.rs", "src/main.rs");

    not_ignored_dir!(idnot1, ROOT, "*.rs", "!src/main.rs", "src/main.rs");
}
<commit_msg>Remove .agignore from ignore file list.<commit_after>/*!
The ignore module is responsible for managing the state required to determine
whether a *single* file path should be searched or not.

In general, there are two ways to ignore a particular file:

1. Specify an ignore rule in some "global" configuration, such as a
   $HOME/.rgignore or on the command line.
2. A specific ignore file (like .gitignore) found during directory traversal.

The `IgnoreDir` type handles ignore patterns for any one particular directory
(including "global" ignore patterns), while the `Ignore` type handles a stack
of `IgnoreDir`s for use during directory traversal.
*/

use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use gitignore::{self, Gitignore, GitignoreBuilder, Match, Pattern};
use types::Types;

const IGNORE_NAMES: &'static [&'static str] = &[
    ".gitignore",
    ".rgignore",
];

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Gitignore(gitignore::Error),
    Io {
        path: PathBuf,
        err: io::Error,
    },
}

impl Error {
    fn from_io<P: AsRef<Path>>(path: P, err: io::Error) -> Error {
        Error::Io { path: path.as_ref().to_path_buf(), err: err }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Gitignore(ref err) => err.description(),
            Error::Io { ref err, .. } => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Gitignore(ref err) => err.fmt(f),
            Error::Io { ref path, ref err } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

impl From<gitignore::Error> for Error {
    fn from(err: gitignore::Error) -> Error {
        Error::Gitignore(err)
    }
}

/// Ignore represents a collection of ignore patterns organized by directory.
/// In particular, a stack is maintained, where the top of the stack
/// corresponds to the current directory being searched and the bottom of the
/// stack represents the root of a search. Ignore patterns at the top of the
/// stack take precedence over ignore patterns at the bottom of the stack.
pub struct Ignore {
    /// A stack of ignore patterns at each directory level of traversal.
    /// A directory that contributes no ignore patterns is `None`.
    stack: Vec<Option<IgnoreDir>>,
    /// A set of override globs that are always checked first. A match (whether
    /// it's whitelist or blacklist) trumps anything in stack.
    overrides: Overrides,
    /// A file type matcher.
    types: Types,
    ignore_hidden: bool,
    no_ignore: bool,
}

impl Ignore {
    /// Create an empty set of ignore patterns.
    pub fn new() -> Ignore {
        Ignore {
            stack: vec![],
            overrides: Overrides::new(None),
            types: Types::empty(),
            ignore_hidden: true,
            no_ignore: false,
        }
    }

    /// Set whether hidden files/folders should be ignored (defaults to true).
    pub fn ignore_hidden(&mut self, yes: bool) -> &mut Ignore {
        self.ignore_hidden = yes;
        self
    }

    /// When set, ignore files are ignored.
    pub fn no_ignore(&mut self, yes: bool) -> &mut Ignore {
        self.no_ignore = yes;
        self
    }

    /// Add a set of globs that overrides all other match logic.
    pub fn add_override(&mut self, gi: Gitignore) -> &mut Ignore {
        self.overrides = Overrides::new(Some(gi));
        self
    }

    /// Add a file type matcher. The file type matcher has the lowest
    /// precedence.
    pub fn add_types(&mut self, types: Types) -> &mut Ignore {
        self.types = types;
        self
    }

    /// Push parent directories of `path` on to the stack.
    pub fn push_parents<P: AsRef<Path>>(
        &mut self,
        path: P,
    ) -> Result<(), Error> {
        let path = try!(path.as_ref().canonicalize().map_err(|err| {
            Error::from_io(path.as_ref(), err)
        }));
        let mut path = &*path;
        let mut saw_git = path.join(".git").is_dir();
        let mut ignore_names = IGNORE_NAMES.to_vec();
        let mut ignore_dir_results = vec![];
        while let Some(parent) = path.parent() {
            if self.no_ignore {
                ignore_dir_results.push(Ok(None));
            } else {
                if saw_git {
                    ignore_names.retain(|&name| name != ".gitignore");
                } else {
                    saw_git = parent.join(".git").is_dir();
                }
                let ignore_dir_result =
                    IgnoreDir::with_ignore_names(parent, ignore_names.iter());
                ignore_dir_results.push(ignore_dir_result);
            }
            path = parent;
        }

        for ignore_dir_result in ignore_dir_results.into_iter().rev() {
            try!(self.push_ignore_dir(ignore_dir_result));
        }
        Ok(())
    }

    /// Add a directory to the stack.
    ///
    /// Note that even if this returns an error, the directory is added to the
    /// stack (and therefore should be popped).
    pub fn push<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        if self.no_ignore {
            self.stack.push(None);
            return Ok(());
        }
        self.push_ignore_dir(IgnoreDir::new(path))
    }

    /// Pushes the result of building a directory matcher on to the stack.
    ///
    /// If the result given contains an error, then it is returned.
    pub fn push_ignore_dir(
        &mut self,
        result: Result<Option<IgnoreDir>, Error>,
    ) -> Result<(), Error> {
        match result {
            Ok(id) => {
                self.stack.push(id);
                Ok(())
            }
            Err(err) => {
                // Don't leave the stack in an inconsistent state.
                self.stack.push(None);
                Err(err)
            }
        }
    }

    /// Pop a directory from the stack.
    ///
    /// This panics if the stack is empty.
    pub fn pop(&mut self) {
        self.stack.pop().expect("non-empty stack");
    }

    /// Returns true if and only if the given file path should be ignored.
    pub fn ignored<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> bool {
        let path = path.as_ref();
        let mat = self.overrides.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        if self.ignore_hidden && is_hidden(&path) {
            debug!("{} ignored because it is hidden", path.display());
            return true;
        }
        for id in self.stack.iter().rev().filter_map(|id| id.as_ref()) {
            let mat = id.matched(path, is_dir);
            if let Some(is_ignored) = self.ignore_match(path, mat) {
                if is_ignored {
                    return true;
                }
                // If this path is whitelisted by an ignore, then fallthrough
                // and let the file type matcher have a say.
                break;
            }
        }
        let mat = self.types.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        false
    }

    /// Returns true if the given match says the given pattern should be
    /// ignored or false if the given pattern should be explicitly whitelisted.
    /// Returns None otherwise.
    pub fn ignore_match<P: AsRef<Path>>(
        &self,
        path: P,
        mat: Match,
    ) -> Option<bool> {
        let path = path.as_ref();
        match mat {
            Match::Whitelist(ref pat) => {
                debug!("{} whitelisted by {:?}", path.display(), pat);
                Some(false)
            }
            Match::Ignored(ref pat) => {
                debug!("{} ignored by {:?}", path.display(), pat);
                Some(true)
            }
            Match::None => None,
        }
    }
}

/// IgnoreDir represents a set of ignore patterns retrieved from a single
/// directory.
pub struct IgnoreDir {
    /// The path to this directory as given.
    path: PathBuf,
    /// A single accumulation of glob patterns for this directory, matched
    /// using gitignore semantics.
    ///
    /// This will include patterns from rgignore as well. The patterns are
    /// ordered so that precedence applies automatically (e.g., rgignore
    /// patterns procede gitignore patterns).
    gi: Option<Gitignore>,
    // TODO(burntsushi): Matching other types of glob patterns that don't
    // conform to gitignore will probably require refactoring this approach.
}

impl IgnoreDir {
    /// Create a new matcher for the given directory.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Option<IgnoreDir>, Error> {
        IgnoreDir::with_ignore_names(path, IGNORE_NAMES.iter())
    }

    /// Create a new matcher for the given directory using only the ignore
    /// patterns found in the file names given.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    ///
    /// Note that the order of the names given is meaningful. Names appearing
    /// later in the list have precedence over names appearing earlier in the
    /// list.
    pub fn with_ignore_names<P: AsRef<Path>, S, I>(
        path: P,
        names: I,
    ) -> Result<Option<IgnoreDir>, Error>
    where P: AsRef<Path>, S: AsRef<str>, I: Iterator<Item=S> {
        let mut id = IgnoreDir {
            path: path.as_ref().to_path_buf(),
            gi: None,
        };
        let mut ok = false;
        let mut builder = GitignoreBuilder::new(&id.path);
        // The ordering here is important. Later globs have higher precedence.
        for name in names {
            ok = builder.add_path(id.path.join(name.as_ref())).is_ok() || ok;
        }
        if !ok {
            Ok(None)
        } else {
            id.gi = Some(try!(builder.build()));
            Ok(Some(id))
        }
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this directory. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of this directory) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside
    /// directly in this directory.
    ///
    /// If the given path has a `./` prefix then it is stripped before
    /// matching.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        self.gi.as_ref()
            .map(|gi| gi.matched(path, is_dir))
            .unwrap_or(Match::None)
    }
}

/// Manages a set of overrides provided explicitly by the end user.
struct Overrides {
    gi: Option<Gitignore>,
    unmatched_pat: Pattern,
}

impl Overrides {
    /// Creates a new set of overrides from the gitignore matcher provided.
    /// If no matcher is provided, then the resulting overrides have no effect.
    fn new(gi: Option<Gitignore>) -> Overrides {
        Overrides {
            gi: gi,
            unmatched_pat: Pattern {
                from: Path::new("<argv>").to_path_buf(),
                original: "<none>".to_string(),
                pat: "<none>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Returns a match for the given path against this set of overrides.
    ///
    /// If there are no overrides, then this always returns Match::None.
    ///
    /// If there is at least one positive override, then this never returns
    /// Match::None (and interpreting non-matches as ignored) unless is_dir
    /// is true.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        self.gi.as_ref()
            .map(|gi| {
                let path = &*path.to_string_lossy();
                let mat = gi.matched_utf8(path, is_dir).invert();
                if mat.is_none() && !is_dir {
                    if gi.num_ignores() > 0 {
                        return Match::Ignored(&self.unmatched_pat);
                    }
                }
                mat
            })
            .unwrap_or(Match::None)
    }
}

fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    if let Some(name) = path.as_ref().file_name() {
        name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
    } else {
        false
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use gitignore::GitignoreBuilder;
    use super::IgnoreDir;

    macro_rules! ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(id.matched($path, false).is_ignored());
            }
        };
    }

    macro_rules! not_ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(!id.matched($path, false).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs");
    ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs");
    ignored_dir!(id3, ROOT, "!src/main.rs", "*.rs", "src/main.rs");

    not_ignored_dir!(idnot1, ROOT, "*.rs", "!src/main.rs", "src/main.rs");
}
<|endoftext|>


<commit_before>use num::Integer;

use program::Program;
use command::Command;
use stack::Stack;

#[derive(Debug, Copy, Clone)]
enum DirectionPointer {
    Up,
    Right,
    Down,
    Left,
}

impl DirectionPointer {
    pub fn rotate_clockwise(&mut self) {
        use self::DirectionPointer::*;

        *self = match *self {
            Up => Right,
            Right => Down,
            Down => Left,
            Left => Up,
        }
    }

    pub fn rotate_counterclockwise(&mut self) {
        use self::DirectionPointer::*;

        *self = match *self {
            Up => Left,
            Left => Down,
            Down => Right,
            Right => Up,
        }
    }
}

#[derive(Debug, Copy, Clone)]
enum CodelChooser {
    Left,
    Right,
}

impl CodelChooser {
    pub fn switch(&mut self) {
        *self = match *self {
            CodelChooser::Left => CodelChooser::Right,
            CodelChooser::Right => CodelChooser::Left,
        }
    }
}

pub struct Interpreter {
    program: Program,
    dp: DirectionPointer,
    cc: CodelChooser,
    stack: Stack,
    position: (usize, usize),
}

impl Interpreter {
    pub fn new(program: Program) -> Self {
        Interpreter {
            program: program,
            dp: DirectionPointer::Right,
            cc: CodelChooser::Left,
            stack: Stack::new(),
            position: (0, 0),
        }
    }

    pub fn step(&mut self) {
        unimplemented!();
    }

    fn calculate_offset(&self) -> (usize, usize) {
        unimplemented!()
    }

    fn execute_command(&mut self, command: &Command) {
        match *command {
            Command::Push => {
                unimplemented!();
            }

            Command::Pop => {
                self.stack.pop();
            }

            Command::Add => {
                self.stack.fold_top(|b, a| a + b);
            }

            Command::Subtract => {
                self.stack.fold_top(|b, a| a * b);
            }

            Command::Multiply => {
                self.stack.fold_top(|b, a| a - b);
            }

            Command::Divide => {
                self.stack.fold_top(|b, a| a / b);
            }

            Command::Mod => {
                self.stack.fold_top(|b, a| a.mod_floor(&b));
            }

            Command::Not => {
                if let Some(value) = self.stack.pop() {
                    self.stack.push(if value != 0 {
                        0
                    } else {
                        1
                    });
                }
            }

            Command::Greater => {
                self.stack.fold_top(|b, a| {
                    if a > b {
                        1
                    } else {
                        0
                    }
                });
            }

            Command::Pointer => {
                if let Some(value) = self.stack.pop() {
                    self.pointer(value);
                }
            }

            Command::Switch => {
                if let Some(value) = self.stack.pop() {
                    self.switch(value);
                }
            }

            Command::Duplicate => {
                if let Some(&value) = self.stack.last() {
                    self.stack.push(value);
                }
            }

            Command::Roll => {
                unimplemented!();
            }

            Command::IntIn => {
                unimplemented!();
            }

            Command::CharIn => {
                unimplemented!();
            }

            Command::IntOut => {
                unimplemented!();
            }

            Command::CharOut => {
                unimplemented!();
            }

            Command::NoOp => return,
        }
    }

    fn pointer(&mut self, steps: i64) {
        if steps >= 0 {
            for _ in 0..steps {
                self.dp.rotate_clockwise();
            }
        } else {
            for _ in 0..steps.abs() {
                self.dp.rotate_counterclockwise();
            }
        }
    }

    fn switch(&mut self, times: i64) {
        for _ in 0..times {
            self.cc.switch();
        }
    }
}
<commit_msg>Implement the roll instruction<commit_after>use num::Integer;

use program::Program;
use command::Command;
use stack::Stack;

#[derive(Debug, Copy, Clone)]
enum DirectionPointer {
    Up,
    Right,
    Down,
    Left,
}

impl DirectionPointer {
    pub fn rotate_clockwise(&mut self) {
        use self::DirectionPointer::*;

        *self = match *self {
            Up => Right,
            Right => Down,
            Down => Left,
            Left => Up,
        }
    }

    pub fn rotate_counterclockwise(&mut self) {
        use self::DirectionPointer::*;

        *self = match *self {
            Up => Left,
            Left => Down,
            Down => Right,
            Right => Up,
        }
    }
}

#[derive(Debug, Copy, Clone)]
enum CodelChooser {
    Left,
    Right,
}

impl CodelChooser {
    pub fn switch(&mut self) {
        *self = match *self {
            CodelChooser::Left => CodelChooser::Right,
            CodelChooser::Right => CodelChooser::Left,
        }
    }
}

pub struct Interpreter {
    program: Program,
    dp: DirectionPointer,
    cc: CodelChooser,
    stack: Stack,
    position: (usize, usize),
}

impl Interpreter {
    pub fn new(program: Program) -> Self {
        Interpreter {
            program: program,
            dp: DirectionPointer::Right,
            cc: CodelChooser::Left,
            stack: Stack::new(),
            position: (0, 0),
        }
    }

    pub fn step(&mut self) {
        unimplemented!();
    }

    fn calculate_offset(&self) -> (usize, usize) {
        unimplemented!()
    }

    fn execute_command(&mut self, command: &Command) {
        match *command {
            Command::Push => {
                unimplemented!();
            }

            Command::Pop => {
                self.stack.pop();
            }

            Command::Add => {
                self.stack.fold_top(|b, a| a + b);
            }

            Command::Subtract => {
                self.stack.fold_top(|b, a| a * b);
            }

            Command::Multiply => {
                self.stack.fold_top(|b, a| a - b);
            }

            Command::Divide => {
                self.stack.fold_top(|b, a| a / b);
            }

            Command::Mod => {
                self.stack.fold_top(|b, a| a.mod_floor(&b));
            }

            Command::Not => {
                if let Some(value) = self.stack.pop() {
                    self.stack.push(if value != 0 {
                        0
                    } else {
                        1
                    });
                }
            }

            Command::Greater => {
                self.stack.fold_top(|b, a| {
                    if a > b {
                        1
                    } else {
                        0
                    }
                });
            }

            Command::Pointer => {
                if let Some(value) = self.stack.pop() {
                    self.pointer(value);
                }
            }

            Command::Switch => {
                if let Some(value) = self.stack.pop() {
                    self.switch(value);
                }
            }

            Command::Duplicate => {
                if let Some(&value) = self.stack.last() {
                    self.stack.push(value);
                }
            }

            Command::Roll => {
                if let (Some(times), Some(depth)) = self.stack.pop2() {
                    self.stack.roll(depth as usize, times);
                }
            }

            Command::IntIn => {
                unimplemented!();
            }

            Command::CharIn => {
                unimplemented!();
            }

            Command::IntOut => {
                unimplemented!();
            }

            Command::CharOut => {
                unimplemented!();
            }

            Command::NoOp => return,
        }
    }

    fn pointer(&mut self, steps: i64) {
        if steps >= 0 {
            for _ in 0..steps {
                self.dp.rotate_clockwise();
            }
        } else {
            for _ in 0..steps.abs() {
                self.dp.rotate_counterclockwise();
            }
        }
    }

    fn switch(&mut self, times: i64) {
        for _ in 0..times {
            self.cc.switch();
        }
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Defines memory-related functions, such as allocate/deallocate/reallocate memory
//! regions, cache and allocation alignments.

use std::alloc::Layout;
use std::mem::align_of;
use std::ptr::NonNull;

// NOTE: Below code is written for spatial/temporal prefetcher optimizations. Memory allocation
// should align well with usage pattern of cache access and block sizes on layers of storage levels from
// registers to non-volatile memory. These alignments are all cache aware alignments incorporated
// from [cuneiform](https://crates.io/crates/cuneiform) crate. This approach mimicks Intel TBB's
// cache_aligned_allocator which exploits cache locality and minimizes prefetch signals
// resulting in less round trip time between the layers of storage.
// For further info: https://software.intel.com/en-us/node/506094

// 32-bit architecture and things other than netburst microarchitecture are using 64 bytes.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "x86")]
pub const ALIGNMENT: usize = 1 << 6;

// Intel x86_64:
// L2D streamer from L1:
// Loads data or instructions from memory to the second-level cache. To use the streamer,
// organize the data or instructions in blocks of 128 bytes, aligned on 128 bytes.
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "x86_64")]
pub const ALIGNMENT: usize = 1 << 7;

// 24Kc:
// Data Line Size
// - https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00346-2B-24K-DTS-04.00.pdf
// - https://gitlab.e.foundation/e/devices/samsung/n7100/stable_android_kernel_samsung_smdk4412/commit/2dbac10263b2f3c561de68b4c369bc679352ccee
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "mips")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "mips64")]
pub const ALIGNMENT: usize = 1 << 5;

// Defaults for powerpc
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "powerpc")]
pub const ALIGNMENT: usize = 1 << 5;

// Defaults for the ppc 64
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "powerpc64")]
pub const ALIGNMENT: usize = 1 << 6;

// e.g.: sifive
// - https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/riscv/sifive-l2-cache.txt#L41
// in general all of them are the same.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "riscv")]
pub const ALIGNMENT: usize = 1 << 6;

// This size is same across all hardware for this architecture.
// - https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2s390_2include_2asm_2cache_8h.html
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "s390x")]
pub const ALIGNMENT: usize = 1 << 8;

// This size is same across all hardware for this architecture.
// - https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2sparc_2include_2asm_2cache_8h.html#a9400cc2ba37e33279bdbc510a6311fb4
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "sparc")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "sparc64")]
pub const ALIGNMENT: usize = 1 << 6;

// On ARM cache line sizes are fixed. both v6 and v7.
// Need to add board specific or platform specific things later.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "thumbv6")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "thumbv7")]
pub const ALIGNMENT: usize = 1 << 5;

// Operating Systems cache size determines this.
// Currently no way to determine this without runtime inference.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "wasm32")]
pub const ALIGNMENT: usize = FALLBACK_ALIGNMENT;

// Same as v6 and v7.
// List goes like that:
// Cortex A, M, R, ARM v7, v7-M, Krait and NeoverseN uses this size.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "arm")]
pub const ALIGNMENT: usize = 1 << 5;

// Combined from 4 sectors. Volta says 128.
// Prevent chunk optimizations better to go to the default size.
// If you have smaller data with less padded functionality then use 32 with force option.
// - https://devtalk.nvidia.com/default/topic/803600/variable-cache-line-width-/
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "nvptx")]
pub const ALIGNMENT: usize = 1 << 7;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "nvptx64")]
pub const ALIGNMENT: usize = 1 << 7;

// This size is same across all hardware for this architecture.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "aarch64")]
pub const ALIGNMENT: usize = 1 << 6;

#[doc(hidden)]
/// Fallback cache and allocation multiple alignment size
const FALLBACK_ALIGNMENT: usize = 1 << 6;

///
/// As you can see this is global and lives as long as the program lives.
/// Be careful to not write anything to this pointer in any scenario.
/// If you use allocation methods shown here you won't have any problems.
const BYPASS_PTR: NonNull<u8> = unsafe { NonNull::new_unchecked(ALIGNMENT as *mut u8) };

pub fn allocate_aligned(size: usize) -> *mut u8 {
    unsafe {
        if size == 0 {
            // In a perfect world, there is no need to request zero size allocation.
            // Currently, passing zero sized layout to alloc is UB.
            // This will dodge allocator api for any type.
            BYPASS_PTR.as_ptr()
        } else {
            let layout = Layout::from_size_align_unchecked(size, ALIGNMENT);
            std::alloc::alloc_zeroed(layout)
        }
    }
}

pub unsafe fn free_aligned(ptr: *mut u8, size: usize) {
    if size != 0x00 && ptr != BYPASS_PTR.as_ptr() {
        std::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, ALIGNMENT));
    }
}

pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 {
    if ptr == BYPASS_PTR.as_ptr() {
        allocate_aligned(new_size)
    } else {
        let new_ptr = std::alloc::realloc(
            ptr,
            Layout::from_size_align_unchecked(old_size, ALIGNMENT),
            new_size,
        );
        if !new_ptr.is_null() && new_size > old_size {
            new_ptr.add(old_size).write_bytes(0, new_size - old_size);
        }
        new_ptr
    }
}

pub unsafe fn memcpy(dst: *mut u8, src: *const u8, len: usize) {
    if len != 0x00 && src != BYPASS_PTR.as_ptr() {
        std::ptr::copy_nonoverlapping(src, dst, len)
    }
}

extern "C" {
    pub fn memcmp(p1: *const u8, p2: *const u8, len: usize) -> i32;
}

/// Check if the pointer `p` is aligned to offset `a`.
pub fn is_aligned<T>(p: *const T, a: usize) -> bool {
    let a_minus_one = a.wrapping_sub(1);
    let pmoda = p as usize & a_minus_one;
    pmoda == 0
}

pub fn is_ptr_aligned<T>(p: *const T) -> bool {
    p.align_offset(align_of::<T>()) == 0
}

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

    #[test]
    fn test_allocate() {
        for _ in 0..10 {
            let p = allocate_aligned(1024);
            // make sure this is 64-byte aligned
            assert_eq!(0, (p as usize) % 64);
        }
    }

    #[test]
    fn test_is_aligned() {
        // allocate memory aligned to 64-byte
        let mut ptr = allocate_aligned(10);
        assert_eq!(true, is_aligned::<u8>(ptr, 1));
        assert_eq!(true, is_aligned::<u8>(ptr, 2));
        assert_eq!(true, is_aligned::<u8>(ptr, 4));

        // now make the memory aligned to 63-byte
        ptr = unsafe { ptr.offset(1) };
        assert_eq!(true, is_aligned::<u8>(ptr, 1));
        assert_eq!(false, is_aligned::<u8>(ptr, 2));
        assert_eq!(false, is_aligned::<u8>(ptr, 4));
    }
}
<commit_msg>ARROW-9176: [Rust] Fix for memory leaks in Arrow allocator<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Defines memory-related functions, such as allocate/deallocate/reallocate memory
//! regions, cache and allocation alignments.

use std::alloc::Layout;
use std::mem::align_of;
use std::ptr::NonNull;

// NOTE: Below code is written for spatial/temporal prefetcher optimizations. Memory allocation
// should align well with usage pattern of cache access and block sizes on layers of storage levels from
// registers to non-volatile memory. These alignments are all cache aware alignments incorporated
// from [cuneiform](https://crates.io/crates/cuneiform) crate. This approach mimicks Intel TBB's
// cache_aligned_allocator which exploits cache locality and minimizes prefetch signals
// resulting in less round trip time between the layers of storage.
// For further info: https://software.intel.com/en-us/node/506094

// 32-bit architecture and things other than netburst microarchitecture are using 64 bytes.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "x86")]
pub const ALIGNMENT: usize = 1 << 6;

// Intel x86_64:
// L2D streamer from L1:
// Loads data or instructions from memory to the second-level cache. To use the streamer,
// organize the data or instructions in blocks of 128 bytes, aligned on 128 bytes.
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "x86_64")]
pub const ALIGNMENT: usize = 1 << 7;

// 24Kc:
// Data Line Size
// - https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00346-2B-24K-DTS-04.00.pdf
// - https://gitlab.e.foundation/e/devices/samsung/n7100/stable_android_kernel_samsung_smdk4412/commit/2dbac10263b2f3c561de68b4c369bc679352ccee
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "mips")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "mips64")]
pub const ALIGNMENT: usize = 1 << 5;

// Defaults for powerpc
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "powerpc")]
pub const ALIGNMENT: usize = 1 << 5;

// Defaults for the ppc 64
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "powerpc64")]
pub const ALIGNMENT: usize = 1 << 6;

// e.g.: sifive
// - https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/riscv/sifive-l2-cache.txt#L41
// in general all of them are the same.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "riscv")]
pub const ALIGNMENT: usize = 1 << 6;

// This size is same across all hardware for this architecture.
// - https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2s390_2include_2asm_2cache_8h.html
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "s390x")]
pub const ALIGNMENT: usize = 1 << 8;

// This size is same across all hardware for this architecture.
// - https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2sparc_2include_2asm_2cache_8h.html#a9400cc2ba37e33279bdbc510a6311fb4
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "sparc")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "sparc64")]
pub const ALIGNMENT: usize = 1 << 6;

// On ARM cache line sizes are fixed. both v6 and v7.
// Need to add board specific or platform specific things later.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "thumbv6")]
pub const ALIGNMENT: usize = 1 << 5;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "thumbv7")]
pub const ALIGNMENT: usize = 1 << 5;

// Operating Systems cache size determines this.
// Currently no way to determine this without runtime inference.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "wasm32")]
pub const ALIGNMENT: usize = FALLBACK_ALIGNMENT;

// Same as v6 and v7.
// List goes like that:
// Cortex A, M, R, ARM v7, v7-M, Krait and NeoverseN uses this size.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "arm")]
pub const ALIGNMENT: usize = 1 << 5;

// Combined from 4 sectors. Volta says 128.
// Prevent chunk optimizations better to go to the default size.
// If you have smaller data with less padded functionality then use 32 with force option.
// - https://devtalk.nvidia.com/default/topic/803600/variable-cache-line-width-/
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "nvptx")]
pub const ALIGNMENT: usize = 1 << 7;
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "nvptx64")]
pub const ALIGNMENT: usize = 1 << 7;

// This size is same across all hardware for this architecture.
/// Cache and allocation multiple alignment size
#[cfg(target_arch = "aarch64")]
pub const ALIGNMENT: usize = 1 << 6;

#[doc(hidden)]
/// Fallback cache and allocation multiple alignment size
const FALLBACK_ALIGNMENT: usize = 1 << 6;

///
/// As you can see this is global and lives as long as the program lives.
/// Be careful to not write anything to this pointer in any scenario.
/// If you use allocation methods shown here you won't have any problems.
const BYPASS_PTR: NonNull<u8> = unsafe { NonNull::new_unchecked(ALIGNMENT as *mut u8) };

pub fn allocate_aligned(size: usize) -> *mut u8 {
    unsafe {
        if size == 0 {
            // In a perfect world, there is no need to request zero size allocation.
            // Currently, passing zero sized layout to alloc is UB.
            // This will dodge allocator api for any type.
            BYPASS_PTR.as_ptr()
        } else {
            let layout = Layout::from_size_align_unchecked(size, ALIGNMENT);
            std::alloc::alloc_zeroed(layout)
        }
    }
}

pub unsafe fn free_aligned(ptr: *mut u8, size: usize) {
    if ptr != BYPASS_PTR.as_ptr() {
        std::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, ALIGNMENT));
    }
}

pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 {
    if ptr == BYPASS_PTR.as_ptr() {
        return allocate_aligned(new_size);
    }

    if new_size == 0 {
        free_aligned(ptr, old_size);
        return BYPASS_PTR.as_ptr();
    }

    let new_ptr = std::alloc::realloc(
        ptr,
        Layout::from_size_align_unchecked(old_size, ALIGNMENT),
        new_size,
    );

    if !new_ptr.is_null() && new_size > old_size {
        new_ptr.add(old_size).write_bytes(0, new_size - old_size);
    }

    new_ptr
}

pub unsafe fn memcpy(dst: *mut u8, src: *const u8, len: usize) {
    if len != 0x00 && src != BYPASS_PTR.as_ptr() {
        std::ptr::copy_nonoverlapping(src, dst, len)
    }
}

extern "C" {
    pub fn memcmp(p1: *const u8, p2: *const u8, len: usize) -> i32;
}

/// Check if the pointer `p` is aligned to offset `a`.
pub fn is_aligned<T>(p: *const T, a: usize) -> bool {
    let a_minus_one = a.wrapping_sub(1);
    let pmoda = p as usize & a_minus_one;
    pmoda == 0
}

pub fn is_ptr_aligned<T>(p: *const T) -> bool {
    p.align_offset(align_of::<T>()) == 0
}

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

    #[test]
    fn test_allocate() {
        for _ in 0..10 {
            let p = allocate_aligned(1024);
            // make sure this is 64-byte aligned
            assert_eq!(0, (p as usize) % 64);
        }
    }

    #[test]
    fn test_is_aligned() {
        // allocate memory aligned to 64-byte
        let mut ptr = allocate_aligned(10);
        assert_eq!(true, is_aligned::<u8>(ptr, 1));
        assert_eq!(true, is_aligned::<u8>(ptr, 2));
        assert_eq!(true, is_aligned::<u8>(ptr, 4));

        // now make the memory aligned to 63-byte
        ptr = unsafe { ptr.offset(1) };
        assert_eq!(true, is_aligned::<u8>(ptr, 1));
        assert_eq!(false, is_aligned::<u8>(ptr, 2));
        assert_eq!(false, is_aligned::<u8>(ptr, 4));
    }
}
<|endoftext|>


<commit_before>use super::Value;

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::Number(ref a), &Value::Number(ref b)) => a == b,
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl PartialEq<str> for Value {
    /// Compare `str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == *"lorem");
    /// ```
    fn eq(&self, other: &str) -> bool {
        self.as_str().map_or(false, |s| s == other)
    }
}

impl<'a> PartialEq<&'a str> for Value {
    /// Compare `&str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == "lorem");
    /// ```
    fn eq(&self, other: &&str) -> bool {
        self.as_str().map_or(false, |s| s == *other)
    }
}

impl PartialEq<Value> for str {
    /// Compare YAML value with `str`
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(*"lorem" == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == self)
    }
}

impl<'a> PartialEq<Value> for &'a str {
    /// Compare `&str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!("lorem" == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == *self)
    }
}

impl PartialEq<String> for Value {
    /// Compare YAML value with String
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == "lorem".to_string());
    /// ```
    fn eq(&self, other: &String) -> bool {
        self.as_str().map_or(false, |s| s == other)
    }
}

impl PartialEq<Value> for String {
    /// Compare `String` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!("lorem".to_string() == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == self)
    }
}

macro_rules! partialeq_numeric {
    ($([$($ty:ty)*], $conversion:ident, $base:ty)*) => {
        $($(
            impl PartialEq<$ty> for Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }

            impl PartialEq<Value> for $ty {
                fn eq(&self, other: &Value) -> bool {
                    other.$conversion().map_or(false, |i| i == (*self as $base))
                }
            }

            impl<'a> PartialEq<$ty> for &'a Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }

            impl<'a> PartialEq<$ty> for &'a mut Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }
        )*)*
    }
}

partialeq_numeric! {
    [i8 i16 i32 i64 isize], as_i64, i64
    [u8 u16 u32 usize], as_i64, i64
    [f32 f64], as_f64, f64
}
<commit_msg>Value == u64<commit_after>use super::Value;

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::Number(ref a), &Value::Number(ref b)) => a == b,
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl PartialEq<str> for Value {
    /// Compare `str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == *"lorem");
    /// ```
    fn eq(&self, other: &str) -> bool {
        self.as_str().map_or(false, |s| s == other)
    }
}

impl<'a> PartialEq<&'a str> for Value {
    /// Compare `&str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == "lorem");
    /// ```
    fn eq(&self, other: &&str) -> bool {
        self.as_str().map_or(false, |s| s == *other)
    }
}

impl PartialEq<Value> for str {
    /// Compare YAML value with `str`
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(*"lorem" == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == self)
    }
}

impl<'a> PartialEq<Value> for &'a str {
    /// Compare `&str` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!("lorem" == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == *self)
    }
}

impl PartialEq<String> for Value {
    /// Compare YAML value with String
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!(Value::String("lorem".into()) == "lorem".to_string());
    /// ```
    fn eq(&self, other: &String) -> bool {
        self.as_str().map_or(false, |s| s == other)
    }
}

impl PartialEq<Value> for String {
    /// Compare `String` with YAML value
    ///
    /// # Examples
    ///
    /// ```edition2018
    /// # use serde_yaml::Value;
    /// assert!("lorem".to_string() == Value::String("lorem".into()));
    /// ```
    fn eq(&self, other: &Value) -> bool {
        other.as_str().map_or(false, |s| s == self)
    }
}

macro_rules! partialeq_numeric {
    ($([$($ty:ty)*], $conversion:ident, $base:ty)*) => {
        $($(
            impl PartialEq<$ty> for Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }

            impl PartialEq<Value> for $ty {
                fn eq(&self, other: &Value) -> bool {
                    other.$conversion().map_or(false, |i| i == (*self as $base))
                }
            }

            impl<'a> PartialEq<$ty> for &'a Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }

            impl<'a> PartialEq<$ty> for &'a mut Value {
                fn eq(&self, other: &$ty) -> bool {
                    self.$conversion().map_or(false, |i| i == (*other as $base))
                }
            }
        )*)*
    }
}

partialeq_numeric! {
    [i8 i16 i32 i64 isize], as_i64, i64
    [u8 u16 u32 u64 usize], as_u64, u64
    [f32 f64], as_f64, f64
}
<|endoftext|>


<commit_before>use std::cmp::min;
use std::rc::Rc;

use Cursive;
use direction::Direction;
use view::{IdView, View};
use align::{Align, HAlign, VAlign};
use view::scroll::ScrollBase;
use event::{Event, EventResult, Key};
use vec::Vec2;
use Printer;

use unicode_width::UnicodeWidthStr;

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    focus: usize,
    scrollbase: ScrollBase,
    // This is a custom callback to include a &T
    select_cb: Option<Rc<Fn(&mut Cursive, &T)>>,
    align: Align,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            focus: 0,
            scrollbase: ScrollBase::new(),
            select_cb: None,
            align: Align::top_left(),
        }
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.select_cb = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    ///
    /// Chainable variant.
    pub fn on_select<F>(mut self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.set_on_select(cb);

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the view is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item.
    ///
    /// Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }
}

impl SelectView<String> {
    /// Convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&self, printer: &Printer) {

        let h = self.items.len();
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer =
            &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

        self.scrollbase.draw(printer, |printer, i| {
            printer.with_selection(i == self.focus, |printer| {
                let l = self.items[i].label.width();
                let x = self.align.h.get_offset(l, printer.size.x);
                printer.print_hline((0, 0), x, " ");
                printer.print((x, 0), &self.items[i].label);
                printer.print_hline((x + l, 0), printer.size.x - l - x, " ");
            });
        });
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        // Items here are not compressible.
        // So no matter what the horizontal requirements are,
        // we'll still return our longest item.
        let w = self.items
            .iter()
            .map(|item| item.label.width())
            .max()
            .unwrap_or(1);
        let h = self.items.len();

        let scrolling = req.y < h;

        // Add 2 spaces for the scrollbar if we need
        let w = if scrolling {
            w + 2
        } else {
            w
        };

        Vec2::new(w, h)
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Key(Key::Up) if self.focus > 0 => self.focus -= 1,
            Event::Key(Key::Down) if self.focus + 1 < self.items.len() => {
                self.focus += 1
            }
            Event::Key(Key::PageUp) => self.focus -= min(self.focus, 10),
            Event::Key(Key::PageDown) => {
                self.focus = min(self.focus + 10, self.items.len() - 1)
            }
            Event::Key(Key::Home) => self.focus = 0,
            Event::Key(Key::End) => self.focus = self.items.len() - 1,
            Event::Key(Key::Enter) if self.select_cb.is_some() => {
                let cb = self.select_cb.as_ref().unwrap().clone();
                let v = self.selection();
                // We return a Callback Rc<|s| cb(s, &*v)>
                return EventResult::Consumed(Some(Rc::new(move |s| {
                    cb(s, &*v)
                })));
            }
            Event::Char(c) => {
                // Starting from the current focus,
                // find the first item that match the char.
                // Cycle back to the beginning of
                // the list when we reach the end.
                // This is achieved by chaining twice the iterator
                let iter = self.items.iter().chain(self.items.iter());
                if let Some((i, _)) = iter.enumerate()
                    .skip(self.focus + 1)
                    .find(|&(_, item)| item.label.starts_with(c)) {
                    // Apply modulo in case we have a hit
                    // from the chained iterator
                    self.focus = i % self.items.len();
                }
            }
            _ => return EventResult::Ignored,
        }

        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        true
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.items.len());
    }
}
<commit_msg>Add enabled state to SelectView<commit_after>use std::cmp::min;
use std::rc::Rc;

use Cursive;
use With;
use direction::Direction;
use view::{IdView, View};
use align::{Align, HAlign, VAlign};
use view::scroll::ScrollBase;
use event::{Event, EventResult, Key};
use theme::ColorStyle;
use vec::Vec2;
use Printer;

use unicode_width::UnicodeWidthStr;

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    enabled: bool,
    focus: usize,
    scrollbase: ScrollBase,
    // This is a custom callback to include a &T
    select_cb: Option<Rc<Fn(&mut Cursive, &T)>>,
    align: Align,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            enabled: true,
            focus: 0,
            scrollbase: ScrollBase::new(),
            select_cb: None,
            align: Align::top_left(),
        }
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.select_cb = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    ///
    /// Chainable variant.
    pub fn on_select<F>(mut self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.set_on_select(cb);

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the view is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item.
    ///
    /// Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }

    fn draw_item(&self, printer: &Printer, i: usize) {
        let l = self.items[i].label.width();
        let x = self.align.h.get_offset(l, printer.size.x);
        printer.print_hline((0, 0), x, " ");
        printer.print((x, 0), &self.items[i].label);
        printer.print_hline((x + l, 0), printer.size.x - l - x, " ");
    }
}

impl SelectView<String> {
    /// Convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&self, printer: &Printer) {

        let h = self.items.len();
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer =
            &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

        self.scrollbase.draw(printer, |printer, i| {
            printer.with_selection(i == self.focus, |printer| {
                if i != self.focus && !self.enabled {
                    printer.with_color(ColorStyle::Secondary,
                                       |printer| self.draw_item(printer, i));
                } else {
                    self.draw_item(printer, i);
                }
            });
        });
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        // Items here are not compressible.
        // So no matter what the horizontal requirements are,
        // we'll still return our longest item.
        let w = self.items
            .iter()
            .map(|item| item.label.width())
            .max()
            .unwrap_or(1);
        let h = self.items.len();

        let scrolling = req.y < h;

        // Add 2 spaces for the scrollbar if we need
        let w = if scrolling {
            w + 2
        } else {
            w
        };

        Vec2::new(w, h)
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Key(Key::Up) if self.focus > 0 => self.focus -= 1,
            Event::Key(Key::Down) if self.focus + 1 < self.items.len() => {
                self.focus += 1
            }
            Event::Key(Key::PageUp) => self.focus -= min(self.focus, 10),
            Event::Key(Key::PageDown) => {
                self.focus = min(self.focus + 10, self.items.len() - 1)
            }
            Event::Key(Key::Home) => self.focus = 0,
            Event::Key(Key::End) => self.focus = self.items.len() - 1,
            Event::Key(Key::Enter) if self.select_cb.is_some() => {
                let cb = self.select_cb.as_ref().unwrap().clone();
                let v = self.selection();
                // We return a Callback Rc<|s| cb(s, &*v)>
                return EventResult::Consumed(Some(Rc::new(move |s| {
                    cb(s, &*v)
                })));
            }
            Event::Char(c) => {
                // Starting from the current focus,
                // find the first item that match the char.
                // Cycle back to the beginning of
                // the list when we reach the end.
                // This is achieved by chaining twice the iterator
                let iter = self.items.iter().chain(self.items.iter());
                if let Some((i, _)) = iter.enumerate()
                    .skip(self.focus + 1)
                    .find(|&(_, item)| item.label.starts_with(c)) {
                    // Apply modulo in case we have a hit
                    // from the chained iterator
                    self.focus = i % self.items.len();
                }
            }
            _ => return EventResult::Ignored,
        }

        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled && !self.items.is_empty()
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.items.len());
    }
}
<|endoftext|>