<commit_before>// Copyright (c) 2017 fd developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate, &'static str>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err("Only one placeholder allowed for batch commands");
        }
        if cmd.args[0].has_tokens() {
            return Err("First argument of exec-batch is expected to be a fixed executable");
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm);
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths = paths.map(|p| Self::prepare_path(&p));
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                // A single `Tokens` is expected
                // So we can directy consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

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

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<commit_msg>Sort search results when using -X option<commit_after>// Copyright (c) 2017 fd developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate, &'static str>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err("Only one placeholder allowed for batch commands");
        }
        if cmd.args[0].has_tokens() {
            return Err("First argument of exec-batch is expected to be a fixed executable");
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm);
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths: Vec<String> = paths.map(|p| Self::prepare_path(&p)).collect();
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                paths.sort();

                // A single `Tokens` is expected
                // So we can directly consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

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

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Amos Onn.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

use std::fs::File;
use std::os::unix::fs::FileExt;
use std::ffi::OsStr;
use std::path::Path;
use libc;

use super::fusefl::*;
use fuse_mt::*;
use super::Result;

pub trait ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize>;
}

pub trait WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize>;

    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

impl ReadFileLike for File {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        FileExt::read_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }
}

impl WriteFileLike for File {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        FileExt::write_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }

    // NOTE: we can't use the flush method from Write, because that wants a &mut. However, for now
    // File's impl of flush is the same as ours, Ok(()), so we can stick with that.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

pub enum NoFile {}

#[allow(unused_variables)]
impl ReadFileLike for NoFile {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }
}

#[allow(unused_variables)]
impl WriteFileLike for NoFile {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }

    fn flush(&self) -> Result<()> {
        Err(libc::ENOSYS)
    }
}

pub struct ReadWriteAdaptor<R, W> {
    reader: R,
    writer: W,
}

impl<R, W> ReadFileLike for ReadWriteAdaptor<R, W> where R: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        self.reader.read_at(buf, offset)
    }
}

impl<R, W> WriteFileLike for ReadWriteAdaptor<R, W> where W: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        self.writer.write_at(buf, offset)
    }

    fn flush(&self) -> Result<()> {
        self.writer.flush()
    }
}

pub enum ModalFileLike<R, W, RW> {
    ReadOnly(R),
    WriteOnly(W),
    ReadWrite(RW),
}

use self::ModalFileLike::*;

impl<R, W, RW> ReadFileLike for ModalFileLike<R, W, RW> where
    R: ReadFileLike, RW: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(ref r) => r.read_at(buf, offset),
            WriteOnly(_) => Err(libc::EBADF),
            ReadWrite(ref rw) => rw.read_at(buf, offset),
        }
    }
}

impl<R, W, RW> WriteFileLike for ModalFileLike<R, W, RW> where
    W: WriteFileLike, RW: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.write_at(buf, offset),
            ReadWrite(ref rw) => rw.write_at(buf, offset),
        }
    }

    fn flush(&self) -> Result<()> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.flush(),
            ReadWrite(ref rw) => rw.flush(),
        }
    }
}

pub trait FilesystemFLRwOpen {
    type ReadLike: ReadFileLike; // = NoFile;
    type WriteLike: WriteFileLike; // = NoFile;
    type ReadWriteLike: ReadFileLike+WriteFileLike; // = ReadWriteAdaptor<Self::ReadLike, Self::WriteLike>;

    fn open_read(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn open_write(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn open_readwrite(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_read(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn create_write(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_readwrite(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
pub trait FilesystemFLOpen {
    type FileLike: ReadFileLike+WriteFileLike;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    fn read(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _size: u32) -> ResultData {
        let _size = _size as usize;
        let mut vec = Vec::<u8>::with_capacity(_size);
        unsafe { vec.set_len(_size) };
        let num_read = _fl.read_at(vec.as_mut_slice(), _offset)?;
        assert!(num_read <= _size);
        unsafe { vec.set_len(num_read) };
        Ok(vec)
    }

    fn write(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _data: Vec<u8>, _flags: u32) -> ResultWrite {
        assert!(_data.len() <= u32::max_value() as usize);
        _fl.write_at(_data.as_slice(), _offset).map(|x| x as u32)
    }

    fn fsync(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _datasync: bool) -> ResultEmpty {
        _fl.flush()
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
impl<T> FilesystemFLOpen for T where T: FilesystemFLRwOpen {
    type FileLike = ModalFileLike<
        <Self as FilesystemFLRwOpen>::ReadLike,
        <Self as FilesystemFLRwOpen>::WriteLike,
        <Self as FilesystemFLRwOpen>::ReadWriteLike,
    >;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_open(self.open_read(_req, _path, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_open(self.open_write(_req, _path, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_open(self.open_readwrite(_req, _path, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_create(self.create_read(_req, _parent, _name, _mode, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_create(self.create_write(_req, _parent, _name, _mode, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_create(self.create_readwrite(_req, _parent, _name, _mode, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }
}

<commit_msg>Added support for fsync-ing metadata outside of the FileLikes.<commit_after>// Copyright 2017 Amos Onn.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

use std::fs::File;
use std::os::unix::fs::FileExt;
use std::ffi::OsStr;
use std::path::Path;
use libc;

use super::fusefl::*;
use fuse_mt::*;
use super::Result;

pub trait ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize>;
}

pub trait WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize>;

    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

impl ReadFileLike for File {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        FileExt::read_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }
}

impl WriteFileLike for File {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        FileExt::write_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }

    // NOTE: we can't use the flush method from Write, because that wants a &mut. However, for now
    // File's impl of flush is the same as ours, Ok(()), so we can stick with that.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

pub enum NoFile {}

#[allow(unused_variables)]
impl ReadFileLike for NoFile {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }
}

#[allow(unused_variables)]
impl WriteFileLike for NoFile {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }

    fn flush(&self) -> Result<()> {
        Err(libc::ENOSYS)
    }
}

pub struct ReadWriteAdaptor<R, W> {
    reader: R,
    writer: W,
}

impl<R, W> ReadFileLike for ReadWriteAdaptor<R, W> where R: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        self.reader.read_at(buf, offset)
    }
}

impl<R, W> WriteFileLike for ReadWriteAdaptor<R, W> where W: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        self.writer.write_at(buf, offset)
    }

    fn flush(&self) -> Result<()> {
        self.writer.flush()
    }
}

pub enum ModalFileLike<R, W, RW> {
    ReadOnly(R),
    WriteOnly(W),
    ReadWrite(RW),
}

use self::ModalFileLike::*;

impl<R, W, RW> ReadFileLike for ModalFileLike<R, W, RW> where
    R: ReadFileLike, RW: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(ref r) => r.read_at(buf, offset),
            WriteOnly(_) => Err(libc::EBADF),
            ReadWrite(ref rw) => rw.read_at(buf, offset),
        }
    }
}

impl<R, W, RW> WriteFileLike for ModalFileLike<R, W, RW> where
    W: WriteFileLike, RW: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.write_at(buf, offset),
            ReadWrite(ref rw) => rw.write_at(buf, offset),
        }
    }

    fn flush(&self) -> Result<()> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.flush(),
            ReadWrite(ref rw) => rw.flush(),
        }
    }
}

pub trait FilesystemFLRwOpen {
    type ReadLike: ReadFileLike; // = NoFile;
    type WriteLike: WriteFileLike; // = NoFile;
    type ReadWriteLike: ReadFileLike+WriteFileLike; // = ReadWriteAdaptor<Self::ReadLike, Self::WriteLike>;

    fn open_read(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn open_write(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn open_readwrite(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_read(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn create_write(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_readwrite(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &ModalFileLike<
        Self::ReadLike,
        Self::WriteLike,
        Self::ReadWriteLike,
    >) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
pub trait FilesystemFLOpen {
    type FileLike: ReadFileLike+WriteFileLike;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    fn read(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _size: u32) -> ResultData {
        let _size = _size as usize;
        let mut vec = Vec::<u8>::with_capacity(_size);
        unsafe { vec.set_len(_size) };
        let num_read = _fl.read_at(vec.as_mut_slice(), _offset)?;
        assert!(num_read <= _size);
        unsafe { vec.set_len(num_read) };
        Ok(vec)
    }

    fn write(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _data: Vec<u8>, _flags: u32) -> ResultWrite {
        assert!(_data.len() <= u32::max_value() as usize);
        _fl.write_at(_data.as_slice(), _offset).map(|x| x as u32)
    }

    fn fsync(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _datasync: bool) -> ResultEmpty {
        _fl.flush()?;
        if !_datasync {
            self.fsync_metadata(_req, _path, _fl)
        } else {
            Ok(())
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
impl<T> FilesystemFLOpen for T where T: FilesystemFLRwOpen {
    type FileLike = ModalFileLike<
        <Self as FilesystemFLRwOpen>::ReadLike,
        <Self as FilesystemFLRwOpen>::WriteLike,
        <Self as FilesystemFLRwOpen>::ReadWriteLike,
    >;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_open(self.open_read(_req, _path, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_open(self.open_write(_req, _path, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_open(self.open_readwrite(_req, _path, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_create(self.create_read(_req, _parent, _name, _mode, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_create(self.create_write(_req, _parent, _name, _mode, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_create(self.create_readwrite(_req, _parent, _name, _mode, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        FilesystemFLRwOpen::fsync_metadata(self, _req, _path, _fl)
    }
}

<|endoftext|>


<commit_before>#![allow(unused_variables)]

use std::thread::Thread;

use super::square::{Square,Rank,File};
use super::moves::Move;
use super::color::{White,Black};
use super::piece::Piece;
use super::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
use super::pos::{Position,Kingside,Queenside};

use self::Action::{Continue,Stop};

macro_rules! send {
    ($tx: expr, $x: expr) => ({
        match $tx.send_opt($x) {
            Ok(()) => (),
            Err(_) => return Stop,
        }
    })
}

enum Action {
    Continue,
    Stop,
}
impl Action {
    fn is_stop(&self) -> bool {
        match *self {
            Continue => false,
            Stop => true,
        }
    }
}

pub fn receive_psudo_legal(p: Position) -> Receiver<Move> {
    let (tx,rx) = sync_channel(0);
    Thread::spawn(move || gen_psudo_legal(&p, tx)).detach();
    rx
}

pub fn gen_psudo_legal(p: &Position, tx: SyncSender<Move>) {
    if gen_en_passant(p, &tx).is_stop() {
        return;
    }
    if gen_castle(p, &tx).is_stop() {
        return;
    }
    for file in range(0,8) {
        for rank in range(0,8) {
            if gen_move_from(p, Square::new(File(file),Rank(rank)), &tx).is_stop() {
                return;
            }
        }
    }
}

//Functions below return Err(()) if the receiver hung up.

fn gen_move_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_id = match p.at(from) {
        None => return Continue,
        Some(val) => val,
    };
    if piece_id.color() != p.side_to_move() {
        return Continue;
    }
    match piece_id.piece_type() {
        Pawn => gen_pawn_from(p, from, tx),
        Queen|Bishop|Rook => gen_slider_from(p, from, tx),
        King|Knight => gen_fixed_from(p, from, tx),
    }
}

// (file, rank)
type Diff = (int, int);

fn shift(s: Square, dir: Diff) -> Option<Square> {
    let (dx, dy) = dir;
    let (File(file), Rank(rank)) = s.to_tuple();
    Square::from_int(file as int + dx, rank as int + dy)
}

fn gen_slider_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let rook_slide:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1)];
    let bishop_slide: &[Diff] = &[(1,1), (1,-1), (-1,-1), (-1,1)];
    let queen_slide:  &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let slide = match piece_type {
        Rook => rook_slide,
        Bishop => bishop_slide,
        Queen => queen_slide,
        _ => unreachable!(),
    };

    for dir in slide.iter() {
        let mut curr_pos = shift(from, *dir);
        //while curr_pos is valid and there is no piece there.
        while curr_pos.is_some() && p.at(curr_pos.unwrap()).is_none() {
            let curr_move = Move::new(from, curr_pos.unwrap());
            send!(tx, curr_move);
            curr_pos = shift(curr_pos.unwrap(), *dir);
        }
        //if curr_pos is valid
        if curr_pos.is_some() {
            let to = curr_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            //that square is always occupied
            let dest_piece: Piece = dest.unwrap();
            let dest_color = dest_piece.color();
            //if there is an enemy at the destination
            if dest_color == piece_color.invert() {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_fixed_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let king_fixed:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];
    let knight_fixed: &[Diff] = &[(2,1), (2,-1), (-2,-1), (-2,1), (1,2), (1,-2), (-1,-2), (-1,2)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let fixed = match piece_type {
        King => king_fixed,
        Knight => knight_fixed,
        _ => unreachable!(),
    };

    for dir in fixed.iter() {
        let new_pos = shift(from, *dir);
        if new_pos.is_some() {
            let to = new_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            let (is_valid, is_capture) = match dest {
                Some(val) => {
                    if val.color() == piece_color { (false, false) }
                    else { (true, true) }
                },
                None => (true, false),
            };
            if is_valid {
                let curr_move = Move::new(from, to).set_capture(is_capture);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_pawn_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_color = p.at(from).unwrap().color();
    let (File(_), Rank(from_rank)) = from.to_tuple();
    //rank_up is the 1-based rank from the piece-owner's side.
    let (dy, rank_up): (int, u8) = match piece_color {
        White => ( 1, 1u8 + from_rank ),
        Black => (-1, 8u8 - from_rank ),
    };
    let move_dir: Diff = (0, dy);
    let to: Square = shift(from, move_dir).unwrap();
    // if destination is empty
    if p.at(to).is_none() {
        match rank_up {
            7 => {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, to).set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            },
            2 => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
                let to2: Square = shift(to, move_dir).unwrap();
                let curr_move2 = Move::new(from, to2);
                send!(tx, curr_move2);
            },
            _ => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
            },
        }
    }

    for dx in [1,-1].iter() {
        let capture_dir: Diff = (*dx, dy);
        let capture_new_pos: Option<Square> = shift(from, capture_dir);
        let capture_to: Square = match capture_new_pos {
            Some(val) => val,
            None => continue,
        };
        let dest: Option<Piece> = p.at(capture_to);
        let dest_piece: Piece = match dest {
            Some(val) => val,
            None => continue,
        };
        let dest_color = dest_piece.color();
        if dest_color == piece_color.invert() {
            if rank_up == 7 {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, capture_to).set_capture(true);
                    let curr_move = curr_move.set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            } else {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_en_passant(p: &Position, tx: &SyncSender<Move>) -> Action {
    //TODO implement en passant generation
    unimplemented!()
}

fn gen_castle(p: &Position, tx: &SyncSender<Move>) -> Action {
    match p.side_to_move() {
        White => {
            if p.can_castle(Kingside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(6),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(2),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
        Black => {
            if p.can_castle(Kingside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(6),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(2),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}
<commit_msg>Change unreachable!() to panic!().<commit_after>#![allow(unused_variables)]

use std::thread::Thread;

use super::square::{Square,Rank,File};
use super::moves::Move;
use super::color::{White,Black};
use super::piece::Piece;
use super::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
use super::pos::{Position,Kingside,Queenside};

use self::Action::{Continue,Stop};

macro_rules! send {
    ($tx: expr, $x: expr) => ({
        match $tx.send_opt($x) {
            Ok(()) => (),
            Err(_) => return Stop,
        }
    })
}

enum Action {
    Continue,
    Stop,
}
impl Action {
    fn is_stop(&self) -> bool {
        match *self {
            Continue => false,
            Stop => true,
        }
    }
}

pub fn receive_psudo_legal(p: Position) -> Receiver<Move> {
    let (tx,rx) = sync_channel(0);
    Thread::spawn(move || gen_psudo_legal(&p, tx)).detach();
    rx
}

pub fn gen_psudo_legal(p: &Position, tx: SyncSender<Move>) {
    if gen_en_passant(p, &tx).is_stop() {
        return;
    }
    if gen_castle(p, &tx).is_stop() {
        return;
    }
    for file in range(0,8) {
        for rank in range(0,8) {
            if gen_move_from(p, Square::new(File(file),Rank(rank)), &tx).is_stop() {
                return;
            }
        }
    }
}

//Functions below return Err(()) if the receiver hung up.

fn gen_move_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_id = match p.at(from) {
        None => return Continue,
        Some(val) => val,
    };
    if piece_id.color() != p.side_to_move() {
        return Continue;
    }
    match piece_id.piece_type() {
        Pawn => gen_pawn_from(p, from, tx),
        Queen|Bishop|Rook => gen_slider_from(p, from, tx),
        King|Knight => gen_fixed_from(p, from, tx),
    }
}

// (file, rank)
type Diff = (int, int);

fn shift(s: Square, dir: Diff) -> Option<Square> {
    let (dx, dy) = dir;
    let (File(file), Rank(rank)) = s.to_tuple();
    Square::from_int(file as int + dx, rank as int + dy)
}

fn gen_slider_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let rook_slide:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1)];
    let bishop_slide: &[Diff] = &[(1,1), (1,-1), (-1,-1), (-1,1)];
    let queen_slide:  &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let slide = match piece_type {
        Rook => rook_slide,
        Bishop => bishop_slide,
        Queen => queen_slide,
        _ => panic!(),
    };

    for dir in slide.iter() {
        let mut curr_pos = shift(from, *dir);
        //while curr_pos is valid and there is no piece there.
        while curr_pos.is_some() && p.at(curr_pos.unwrap()).is_none() {
            let curr_move = Move::new(from, curr_pos.unwrap());
            send!(tx, curr_move);
            curr_pos = shift(curr_pos.unwrap(), *dir);
        }
        //if curr_pos is valid
        if curr_pos.is_some() {
            let to = curr_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            //that square is always occupied
            let dest_piece: Piece = dest.unwrap();
            let dest_color = dest_piece.color();
            //if there is an enemy at the destination
            if dest_color == piece_color.invert() {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_fixed_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let king_fixed:   &[Diff] = &[(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)];
    let knight_fixed: &[Diff] = &[(2,1), (2,-1), (-2,-1), (-2,1), (1,2), (1,-2), (-1,-2), (-1,2)];

    let piece_id = p.at(from).unwrap();
    let piece_type = piece_id.piece_type();
    let piece_color = piece_id.color();

    let fixed = match piece_type {
        King => king_fixed,
        Knight => knight_fixed,
        _ => panic!(),
    };

    for dir in fixed.iter() {
        let new_pos = shift(from, *dir);
        if new_pos.is_some() {
            let to = new_pos.unwrap();
            let dest: Option<Piece> = p.at(to);
            let (is_valid, is_capture) = match dest {
                Some(val) => {
                    if val.color() == piece_color { (false, false) }
                    else { (true, true) }
                },
                None => (true, false),
            };
            if is_valid {
                let curr_move = Move::new(from, to).set_capture(is_capture);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_pawn_from(p: &Position, from: Square, tx: &SyncSender<Move>) -> Action {
    let piece_color = p.at(from).unwrap().color();
    let (File(_), Rank(from_rank)) = from.to_tuple();
    //rank_up is the 1-based rank from the piece-owner's side.
    let (dy, rank_up): (int, u8) = match piece_color {
        White => ( 1, 1u8 + from_rank ),
        Black => (-1, 8u8 - from_rank ),
    };
    let move_dir: Diff = (0, dy);
    let to: Square = shift(from, move_dir).unwrap();
    // if destination is empty
    if p.at(to).is_none() {
        match rank_up {
            7 => {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, to).set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            },
            2 => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
                let to2: Square = shift(to, move_dir).unwrap();
                let curr_move2 = Move::new(from, to2);
                send!(tx, curr_move2);
            },
            _ => {
                let curr_move = Move::new(from, to);
                send!(tx, curr_move);
            },
        }
    }

    for dx in [1,-1].iter() {
        let capture_dir: Diff = (*dx, dy);
        let capture_new_pos: Option<Square> = shift(from, capture_dir);
        let capture_to: Square = match capture_new_pos {
            Some(val) => val,
            None => continue,
        };
        let dest: Option<Piece> = p.at(capture_to);
        let dest_piece: Piece = match dest {
            Some(val) => val,
            None => continue,
        };
        let dest_color = dest_piece.color();
        if dest_color == piece_color.invert() {
            if rank_up == 7 {
                for new_piece in [Queen, Knight, Rook, Bishop].iter() {
                    let curr_move = Move::new(from, capture_to).set_capture(true);
                    let curr_move = curr_move.set_promote(Some(*new_piece));
                    send!(tx, curr_move);
                }
            } else {
                let curr_move = Move::new(from, to).set_capture(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}

fn gen_en_passant(p: &Position, tx: &SyncSender<Move>) -> Action {
    //TODO implement en passant generation
    unimplemented!()
}

fn gen_castle(p: &Position, tx: &SyncSender<Move>) -> Action {
    match p.side_to_move() {
        White => {
            if p.can_castle(Kingside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(6),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, White) {
                let from = Square::new(File(4),Rank(0));
                let to   = Square::new(File(2),Rank(0));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
        Black => {
            if p.can_castle(Kingside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(6),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
            if p.can_castle(Queenside, Black) {
                let from = Square::new(File(4),Rank(7));
                let to   = Square::new(File(2),Rank(7));
                let curr_move = Move::new(from, to).set_castle(true);
                send!(tx, curr_move);
            }
        }
    }

    Continue
}
<|endoftext|>


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

use std::os::unix::io::AsRawFd;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

use crate::gpio::{
    interrupt::AsyncInterrupt,
    GpioState, Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    gpio_state: Arc<GpioState>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
        Pin { pin, gpio_state }
    }

    /// Returns the GPIO pin number.
    ///
    /// Pins are addressed by their BCM numbers, rather than their physical location.
    #[inline]
    pub fn pin(&self) -> u8 {
        self.pin
    }

    /// Consumes the pin, returns an [`InputPin`] and sets its mode to [`Mode::Input`].
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn into_input(self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Additionally to `into_input`, activates the pin's pull-up resistor.
    #[inline]
    pub fn into_input_pullup(self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Additionally to `into_input`, activates the pin's pull-down resistor.
    #[inline]
    pub fn into_input_pulldown(self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Consumes the pin, returns an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn into_output(self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Consumes the pin, returns an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn into_alt(self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        self.gpio_state.gpio_mem.set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        self.gpio_state.gpio_mem.mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        self.gpio_state.gpio_mem.set_pullupdown(self.pin, pud);
    }

    /// Reads the pin's current logic level.
    #[inline]
    pub fn read(&self) -> Level {
        self.gpio_state.gpio_mem.level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        self.gpio_state.gpio_mem.set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        self.gpio_state.gpio_mem.set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

impl Drop for Pin {
    fn drop(&mut self) {
        // Release taken pin
        self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
    }
}

macro_rules! impl_pin {
    () => {
        /// Returns the GPIO pin number.
        ///
        /// Pins are addressed by their BCM numbers, rather than their physical location.
        #[inline]
        pub fn pin(&self) -> u8 {
            self.pin.pin
        }
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }

        /// Returns `true` if the pin's logic level is [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn is_low(&self) -> bool {
            self.pin.read() == Level::Low
        }

        /// Returns `true` if the pin's logic level is [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn is_high(&self) -> bool {
            self.pin.read() == Level::High
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_clear_on_drop {
    () => {
        /// Returns the value of `clear_on_drop`.
        pub fn clear_on_drop(&self) -> bool {
            self.clear_on_drop
        }

        /// When enabled, resets pin's mode to its original state when it goes out of scope.
        /// By default, this is set to `true`.
        ///
        /// # Note
        ///
        /// Drop methods aren't called when a program is abnormally terminated, for
        /// instance when a user presses <kbd>Ctrl + C</kbd>, and the `SIGINT` signal
        /// isn't caught. You catch those using crates such as [`simple_signal`].
        ///
        /// [`simple_signal`]: https://crates.io/crates/simple-signal
        /// [`cleanup`]: #method.cleanup
        pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
            self.clear_on_drop = clear_on_drop;
        }
    };
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl Drop for $struct {
            /// Resets the pin's mode if `clear_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.clear_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode)
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin {
    pub(crate) pin: Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    clear_on_drop: bool,
}

impl InputPin {
    pub(crate) fn new(mut pin: Pin, pud_mode: PullUpDown) -> InputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            clear_on_drop: true,
        }
    }

    impl_pin!();
    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, use [`poll_interrupt`] or
    /// [`Gpio::poll_interrupts`] to block while waiting for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(self.pin(), trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
    }

    /// Blocks until an interrupt is triggered on the pin, or a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// Calling `poll_interrupt` blocks any other calls to `poll_interrupt` (including on other `InputPin`s) or
    /// [`Gpio::poll_interrupts`] until it returns. If you need to poll multiple pins simultaneously, use
    /// [`Gpio::poll_interrupts`] to block while waiting for any of the interrupts to trigger, or switch to
    /// using asynchronous interrupts with [`set_async_interrupt`].
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    /// [`set_async_interrupt`]: #method.set_async_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt =
            (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            self.pin.gpio_state.cdev.as_raw_fd(),
            self.pin(),
            trigger,
            callback,
        )?);

        Ok(())
    }

    /// Removes a previously configured asynchronous interrupt trigger.
    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }

    impl_clear_on_drop!();
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin {
    pin: Pin,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl OutputPin {
    pub(crate) fn new(mut pin: Pin) -> OutputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_clear_on_drop!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin {
    pin: Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl AltPin {
    pub(crate) fn new(mut pin: Pin, mode: Mode) -> AltPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_clear_on_drop!();
}

impl_drop!(AltPin);
<commit_msg>Reset pull-down/up resistor in InputPin::drop()<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::os::unix::io::AsRawFd;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

use crate::gpio::{
    interrupt::AsyncInterrupt,
    GpioState, Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    gpio_state: Arc<GpioState>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(pin: u8, gpio_state: Arc<GpioState>) -> Pin {
        Pin { pin, gpio_state }
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and disables the pin's built-in pull-down and pull-up resistors.
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn into_input(self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-down resistor.
    ///
    /// The pull-down resistor is disabled when `InputPin` goes out of scope if [`clear_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`clear_on_drop`]: #method.set_clear_on_drop
    #[inline]
    pub fn into_input_pulldown(self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Consumes the `Pin`, returns an [`InputPin`], sets its mode to [`Mode::Input`],
    /// and enables the pin's built-in pull-up resistor.
    ///
    /// The pull-up resistor is disabled when `InputPin` goes out of scope if [`clear_on_drop`]
    /// is set to `true` (default).
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    /// [`clear_on_drop`]: #method.set_clear_on_drop
    #[inline]
    pub fn into_input_pullup(self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Consumes the pin, returns an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn into_output(self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Consumes the pin, returns an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn into_alt(self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    /// Returns the GPIO pin number.
    ///
    /// Pins are addressed by their BCM numbers, rather than their physical location.
    #[inline]
    pub fn pin(&self) -> u8 {
        self.pin
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        self.gpio_state.gpio_mem.set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        self.gpio_state.gpio_mem.mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        self.gpio_state.gpio_mem.set_pullupdown(self.pin, pud);
    }

    /// Reads the pin's current logic level.
    #[inline]
    pub fn read(&self) -> Level {
        self.gpio_state.gpio_mem.level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        self.gpio_state.gpio_mem.set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        self.gpio_state.gpio_mem.set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

impl Drop for Pin {
    fn drop(&mut self) {
        // Release taken pin
        self.gpio_state.pins_taken[self.pin as usize].store(false, Ordering::SeqCst);
    }
}

macro_rules! impl_pin {
    () => {
        /// Returns the GPIO pin number.
        ///
        /// Pins are addressed by their BCM numbers, rather than their physical location.
        #[inline]
        pub fn pin(&self) -> u8 {
            self.pin.pin
        }
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }

        /// Returns `true` if the pin's logic level is [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn is_low(&self) -> bool {
            self.pin.read() == Level::Low
        }

        /// Returns `true` if the pin's logic level is [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn is_high(&self) -> bool {
            self.pin.read() == Level::High
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to [`Level::Low`].
        ///
        /// [`Level::Low`]: enum.Level.html
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to [`Level::High`].
        ///
        /// [`Level::High`]: enum.Level.html
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_clear_on_drop {
    () => {
        /// Returns the value of `clear_on_drop`.
        pub fn clear_on_drop(&self) -> bool {
            self.clear_on_drop
        }

        /// When enabled, resets the pin's mode and built-in pull-up/pull-down resistors
        /// to their original state when it goes out of scope.
        /// By default, this is set to `true`.
        ///
        /// # Note
        ///
        /// Drop methods aren't called when a program is abnormally terminated, for
        /// instance when a user presses <kbd>Ctrl + C</kbd>, and the `SIGINT` signal
        /// isn't caught. You catch those using crates such as [`simple_signal`].
        ///
        /// [`simple_signal`]: https://crates.io/crates/simple-signal
        pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
            self.clear_on_drop = clear_on_drop;
        }
    };
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl Drop for $struct {
            /// Resets the pin's mode and built-in pull-up/pull-down resistors if
            /// `clear_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.clear_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode);
                }

                if self.pud_mode != Off {
                    self.pin.set_pullupdown(Off);
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin {
    pub(crate) pin: Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    clear_on_drop: bool,
    pud_mode: PullUpDown,
}

impl InputPin {
    pub(crate) fn new(mut pin: Pin, pud_mode: PullUpDown) -> InputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            clear_on_drop: true,
            pud_mode,
        }
    }

    impl_pin!();
    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, use [`poll_interrupt`] or
    /// [`Gpio::poll_interrupts`] to block while waiting for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).set_interrupt(self.pin(), trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).clear_interrupt(self.pin())
    }

    /// Blocks until an interrupt is triggered on the pin, or a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// Calling `poll_interrupt` blocks any other calls to `poll_interrupt` (including on other `InputPin`s) or
    /// [`Gpio::poll_interrupts`] until it returns. If you need to poll multiple pins simultaneously, use
    /// [`Gpio::poll_interrupts`] to block while waiting for any of the interrupts to trigger, or switch to
    /// using asynchronous interrupts with [`set_async_interrupt`].
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    /// [`Gpio::poll_interrupts`]: struct.Gpio#method.poll_interrupts
    /// [`set_async_interrupt`]: #method.set_async_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt =
            (*self.pin.gpio_state.sync_interrupts.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            self.pin.gpio_state.cdev.as_raw_fd(),
            self.pin(),
            trigger,
            callback,
        )?);

        Ok(())
    }

    /// Removes a previously configured asynchronous interrupt trigger.
    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }

    impl_clear_on_drop!();
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin {
    pin: Pin,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
    pud_mode: PullUpDown,
}

impl OutputPin {
    pub(crate) fn new(mut pin: Pin) -> OutputPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            clear_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_clear_on_drop!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin {
    pin: Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
    pud_mode: PullUpDown,
}

impl AltPin {
    pub(crate) fn new(mut pin: Pin, mode: Mode) -> AltPin {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            clear_on_drop: true,
            pud_mode: Off,
        }
    }

    impl_pin!();
    impl_input!();
    impl_output!();
    impl_clear_on_drop!();
}

impl_drop!(AltPin);
<|endoftext|>