<commit_before>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//! The datatypes module defines the abstract datatypes used by other components of notty.
//!
//! The types in this module are intended to be passed between modules. As a design restriction,
//! any methods on any type in this submodule are required to take the receiver immutably.
use std::cmp;

use mime::Mime;

use cfg;

mod iter;
mod key;

pub use self::iter::CoordsIter;
pub use self::key::Key;

pub use notty_encoding::args::*;

pub mod args {
    pub use super::{
        Area,
        BufferSettings,
        Coords,
        Color,
        Direction,
        EchoSettings,
        InputSettings,
        MediaAlignment,
        MediaPosition,
        Movement,
        Region,
        Style,
    };
    pub use super::Area::*;
    pub use super::Direction::*;
    pub use super::InputSettings::*;
    pub use super::MediaAlignment::*;
    pub use super::MediaPosition::*;
    pub use super::Movement::*;
    pub use super::Style::*;
    pub use notty_encoding::args::Argument;
}

/// Data that could be placed in a character cell.
#[derive(Clone)]
pub enum CellData {
    /// A single unicode code point.
    Char(char),
    /// An extension code point such as U+301. Normally, writing this to the screen does not
    /// overwrite a cell, but instead applies it to the char in the cell.
    ExtensionChar(char),
    /// Non-character media data, with a mime type, positioning and size info.
    Image {
        pos: MediaPosition,
        width: u32,
        height: u32,
        data: Vec<u8>,
        mime: Mime,
    }
}

/// A kind of escape code format (used for structuring response strings).
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Code {
    ANSI,
    Notty,
}

/// Calculate the movement from one coordinate to another within a region.
pub fn move_within(Coords {x, y}: Coords, movement: Movement, region: Region) -> Coords {
    use self::Movement::*;
    use self::Direction::*;
    match movement {
        Position(coords)    => region.xy_within(coords),
        Column(n)           => Coords {x: region.x_within(n), y: y},
        Row(n)              => Coords {x: x, y: region.y_within(n)},
        ToEdge(Up)          => Coords {x: x, y: region.top},
        ToEdge(Down)        => Coords {x: x, y: region.bottom - 1},
        ToEdge(Left)        => Coords {x: region.left, y: y},
        ToEdge(Right)       => Coords {x: region.right - 1, y: y},
        ToBeginning         => Coords {x: region.left, y: region.top},
        ToEnd               => Coords {x: region.right - 1, y: region.bottom - 1},
        To(Up, n, true) if region.top + n > y       => {
            let x = x.saturating_sub((region.top + n - y) / (region.bottom - region.top) + 1);
            let y = region.bottom - (region.top + n - y) % (region.bottom - region.top);
            if x < region.left {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Down, n, true) if y + n >= region.bottom  => {
            let x = x + (y + n - region.bottom) / (region.bottom - region.top) + 1;
            let y = region.top + (y + n - region.bottom) % (region.bottom - region.top);
            if x >= region.right {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Left, n, true) if region.left + n > x    => {
            let y = y.saturating_sub((region.left + n - x) / (region.right - region.left) + 1);
            let x = region.right - (region.left + n - x) % (region.right - region.left);
            if y < region.top {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Right, n, true) if x + n >= region.right  => {
            let y = y + (x + n - region.right) / (region.right - region.left) + 1;
            let x = region.left + (x + n - region.right) % (region.right - region.left);
            if y >= region.bottom {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Up, n, _) | IndexTo(Up, n)         => {
            Coords {x: x, y: cmp::max(region.top, y.saturating_sub(n))}
        }
        To(Down, n, _) | IndexTo(Down, n)     => {
            Coords {x: x, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
        To(Left, n, _) | IndexTo(Left, n)     => {
            Coords {x: cmp::max(region.left, x.saturating_sub(n)), y: y}
        }
        To(Right, n, _) | IndexTo(Right, n)   => {
            Coords {x: cmp::min(x.saturating_add(n), region.right - 1), y: y}
        }
        Tab(Left, n, true) if region.left + n > x => {
            unimplemented!()
        }
        Tab(Right, n, true) if x + n >= region.right => {
            unimplemented!()
        }
        Tab(Left, n, false)                 => {
            let tab = ((x / cfg::TAB_STOP).saturating_sub(n)) * cfg::TAB_STOP;
            Coords {x: cmp::max(tab, region.left), y: y}
        }
        Tab(Right, n, _)                => {
            let tab = ((x / cfg::TAB_STOP) + n) * cfg::TAB_STOP;
            Coords {x: cmp::min(tab, region.right - 1), y: y}
        }
        Tab(..)                             => unimplemented!(),
        PreviousLine(n)                     => {
            Coords {x: 0, y: cmp::max(y.saturating_sub(n), region.top)}
        }
        NextLine(n)                         => {
            Coords {x: 0, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
    }
}
<commit_msg>Also fix left tab case.<commit_after>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//! The datatypes module defines the abstract datatypes used by other components of notty.
//!
//! The types in this module are intended to be passed between modules. As a design restriction,
//! any methods on any type in this submodule are required to take the receiver immutably.
use std::cmp;

use mime::Mime;

use cfg;

mod iter;
mod key;

pub use self::iter::CoordsIter;
pub use self::key::Key;

pub use notty_encoding::args::*;

pub mod args {
    pub use super::{
        Area,
        BufferSettings,
        Coords,
        Color,
        Direction,
        EchoSettings,
        InputSettings,
        MediaAlignment,
        MediaPosition,
        Movement,
        Region,
        Style,
    };
    pub use super::Area::*;
    pub use super::Direction::*;
    pub use super::InputSettings::*;
    pub use super::MediaAlignment::*;
    pub use super::MediaPosition::*;
    pub use super::Movement::*;
    pub use super::Style::*;
    pub use notty_encoding::args::Argument;
}

/// Data that could be placed in a character cell.
#[derive(Clone)]
pub enum CellData {
    /// A single unicode code point.
    Char(char),
    /// An extension code point such as U+301. Normally, writing this to the screen does not
    /// overwrite a cell, but instead applies it to the char in the cell.
    ExtensionChar(char),
    /// Non-character media data, with a mime type, positioning and size info.
    Image {
        pos: MediaPosition,
        width: u32,
        height: u32,
        data: Vec<u8>,
        mime: Mime,
    }
}

/// A kind of escape code format (used for structuring response strings).
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Code {
    ANSI,
    Notty,
}

/// Calculate the movement from one coordinate to another within a region.
pub fn move_within(Coords {x, y}: Coords, movement: Movement, region: Region) -> Coords {
    use self::Movement::*;
    use self::Direction::*;
    match movement {
        Position(coords)    => region.xy_within(coords),
        Column(n)           => Coords {x: region.x_within(n), y: y},
        Row(n)              => Coords {x: x, y: region.y_within(n)},
        ToEdge(Up)          => Coords {x: x, y: region.top},
        ToEdge(Down)        => Coords {x: x, y: region.bottom - 1},
        ToEdge(Left)        => Coords {x: region.left, y: y},
        ToEdge(Right)       => Coords {x: region.right - 1, y: y},
        ToBeginning         => Coords {x: region.left, y: region.top},
        ToEnd               => Coords {x: region.right - 1, y: region.bottom - 1},
        To(Up, n, true) if region.top + n > y       => {
            let x = x.saturating_sub((region.top + n - y) / (region.bottom - region.top) + 1);
            let y = region.bottom - (region.top + n - y) % (region.bottom - region.top);
            if x < region.left {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Down, n, true) if y + n >= region.bottom  => {
            let x = x + (y + n - region.bottom) / (region.bottom - region.top) + 1;
            let y = region.top + (y + n - region.bottom) % (region.bottom - region.top);
            if x >= region.right {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Left, n, true) if region.left + n > x    => {
            let y = y.saturating_sub((region.left + n - x) / (region.right - region.left) + 1);
            let x = region.right - (region.left + n - x) % (region.right - region.left);
            if y < region.top {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Right, n, true) if x + n >= region.right  => {
            let y = y + (x + n - region.right) / (region.right - region.left) + 1;
            let x = region.left + (x + n - region.right) % (region.right - region.left);
            if y >= region.bottom {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Up, n, _) | IndexTo(Up, n)         => {
            Coords {x: x, y: cmp::max(region.top, y.saturating_sub(n))}
        }
        To(Down, n, _) | IndexTo(Down, n)     => {
            Coords {x: x, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
        To(Left, n, _) | IndexTo(Left, n)     => {
            Coords {x: cmp::max(region.left, x.saturating_sub(n)), y: y}
        }
        To(Right, n, _) | IndexTo(Right, n)   => {
            Coords {x: cmp::min(x.saturating_add(n), region.right - 1), y: y}
        }
        Tab(Left, n, true) if region.left + n > x => {
            unimplemented!()
        }
        Tab(Right, n, true) if x + n >= region.right => {
            unimplemented!()
        }
        Tab(Left, n, _)                 => {
            let tab = ((x / cfg::TAB_STOP).saturating_sub(n)) * cfg::TAB_STOP;
            Coords {x: cmp::max(tab, region.left), y: y}
        }
        Tab(Right, n, _)                => {
            let tab = ((x / cfg::TAB_STOP) + n) * cfg::TAB_STOP;
            Coords {x: cmp::min(tab, region.right - 1), y: y}
        }
        Tab(..)                             => unimplemented!(),
        PreviousLine(n)                     => {
            Coords {x: 0, y: cmp::max(y.saturating_sub(n), region.top)}
        }
        NextLine(n)                         => {
            Coords {x: 0, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Aleksander Bielawski <pabzdzdzwiagief@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use retain_mut::RetainMut;
use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::PathBuf;
use uucore::display::Quotable;

#[cfg(unix)]
use uucore::libc;

static ABOUT: &str = "Copy standard input to each FILE, and also to standard output.";

mod options {
    pub const APPEND: &str = "append";
    pub const IGNORE_INTERRUPTS: &str = "ignore-interrupts";
    pub const FILE: &str = "file";
}

#[allow(dead_code)]
struct Options {
    append: bool,
    ignore_interrupts: bool,
    files: Vec<String>,
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = Options {
        append: matches.is_present(options::APPEND),
        ignore_interrupts: matches.is_present(options::IGNORE_INTERRUPTS),
        files: matches
            .values_of(options::FILE)
            .map(|v| v.map(ToString::to_string).collect())
            .unwrap_or_default(),
    };

    match tee(options) {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help("If a FILE is -, it refers to a file named - .")
        .arg(
            Arg::with_name(options::APPEND)
                .long(options::APPEND)
                .short("a")
                .help("append to the given FILEs, do not overwrite"),
        )
        .arg(
            Arg::with_name(options::IGNORE_INTERRUPTS)
                .long(options::IGNORE_INTERRUPTS)
                .short("i")
                .help("ignore interrupt signals (ignored on non-Unix platforms)"),
        )
        .arg(Arg::with_name(options::FILE).multiple(true))
}

#[cfg(unix)]
fn ignore_interrupts() -> Result<()> {
    let ret = unsafe { libc::signal(libc::SIGINT, libc::SIG_IGN) };
    if ret == libc::SIG_ERR {
        return Err(Error::new(ErrorKind::Other, ""));
    }
    Ok(())
}

#[cfg(not(unix))]
fn ignore_interrupts() -> Result<()> {
    // Do nothing.
    Ok(())
}

fn tee(options: Options) -> Result<()> {
    if options.ignore_interrupts {
        ignore_interrupts()?
    }
    let mut writers: Vec<NamedWriter> = options
        .files
        .clone()
        .into_iter()
        .map(|file| NamedWriter {
            name: file.clone(),
            inner: open(file, options.append),
        })
        .collect();

    writers.insert(
        0,
        NamedWriter {
            name: "'standard output'".to_owned(),
            inner: Box::new(stdout()),
        },
    );

    let mut output = MultiWriter::new(writers);
    let input = &mut NamedReader {
        inner: Box::new(stdin()) as Box<dyn Read>,
    };

    // TODO: replaced generic 'copy' call to be able to stop copying
    // if all outputs are closed (due to errors)
    if copy(input, &mut output).is_err() || output.flush().is_err() || output.error_occurred() {
        Err(Error::new(ErrorKind::Other, ""))
    } else {
        Ok(())
    }
}

fn open(name: String, append: bool) -> Box<dyn Write> {
    let path = PathBuf::from(name.clone());
    let inner: Box<dyn Write> = {
        let mut options = OpenOptions::new();
        let mode = if append {
            options.append(true)
        } else {
            options.truncate(true)
        };
        match mode.write(true).create(true).open(path.as_path()) {
            Ok(file) => Box::new(file),
            Err(_) => Box::new(sink()),
        }
    };
    Box::new(NamedWriter { inner, name }) as Box<dyn Write>
}

struct MultiWriter {
    writers: Vec<NamedWriter>,
    initial_len: usize,
}

impl MultiWriter {
    fn new(writers: Vec<NamedWriter>) -> Self {
        Self {
            initial_len: writers.len(),
            writers,
        }
    }
    fn error_occurred(&self) -> bool {
        self.writers.len() != self.initial_len
    }
}

impl Write for MultiWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.writers.retain_mut(|writer| {
            let result = writer.write_all(buf);
            match result {
                Err(f) => {
                    show_error!("{}: {}", writer.name.maybe_quote(), f);
                    false
                }
                _ => true,
            }
        });
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        self.writers.retain_mut(|writer| {
            let result = writer.flush();
            match result {
                Err(f) => {
                    show_error!("{}: {}", writer.name.maybe_quote(), f);
                    false
                }
                _ => true,
            }
        });
        Ok(())
    }
}

struct NamedWriter {
    inner: Box<dyn Write>,
    pub name: String,
}

impl Write for NamedWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.inner.write(buf)
    }

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

struct NamedReader {
    inner: Box<dyn Read>,
}

impl Read for NamedReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        match self.inner.read(buf) {
            Err(f) => {
                show_error!("stdin: {}", f);
                Err(f)
            }
            okay => okay,
        }
    }
}
<commit_msg>fix/tee ~ repair 'unstable_name_collisions' compiler warning<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Aleksander Bielawski <pabzdzdzwiagief@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use retain_mut::RetainMut;
use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::PathBuf;
use uucore::display::Quotable;

#[cfg(unix)]
use uucore::libc;

static ABOUT: &str = "Copy standard input to each FILE, and also to standard output.";

mod options {
    pub const APPEND: &str = "append";
    pub const IGNORE_INTERRUPTS: &str = "ignore-interrupts";
    pub const FILE: &str = "file";
}

#[allow(dead_code)]
struct Options {
    append: bool,
    ignore_interrupts: bool,
    files: Vec<String>,
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = Options {
        append: matches.is_present(options::APPEND),
        ignore_interrupts: matches.is_present(options::IGNORE_INTERRUPTS),
        files: matches
            .values_of(options::FILE)
            .map(|v| v.map(ToString::to_string).collect())
            .unwrap_or_default(),
    };

    match tee(options) {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help("If a FILE is -, it refers to a file named - .")
        .arg(
            Arg::with_name(options::APPEND)
                .long(options::APPEND)
                .short("a")
                .help("append to the given FILEs, do not overwrite"),
        )
        .arg(
            Arg::with_name(options::IGNORE_INTERRUPTS)
                .long(options::IGNORE_INTERRUPTS)
                .short("i")
                .help("ignore interrupt signals (ignored on non-Unix platforms)"),
        )
        .arg(Arg::with_name(options::FILE).multiple(true))
}

#[cfg(unix)]
fn ignore_interrupts() -> Result<()> {
    let ret = unsafe { libc::signal(libc::SIGINT, libc::SIG_IGN) };
    if ret == libc::SIG_ERR {
        return Err(Error::new(ErrorKind::Other, ""));
    }
    Ok(())
}

#[cfg(not(unix))]
fn ignore_interrupts() -> Result<()> {
    // Do nothing.
    Ok(())
}

fn tee(options: Options) -> Result<()> {
    if options.ignore_interrupts {
        ignore_interrupts()?
    }
    let mut writers: Vec<NamedWriter> = options
        .files
        .clone()
        .into_iter()
        .map(|file| NamedWriter {
            name: file.clone(),
            inner: open(file, options.append),
        })
        .collect();

    writers.insert(
        0,
        NamedWriter {
            name: "'standard output'".to_owned(),
            inner: Box::new(stdout()),
        },
    );

    let mut output = MultiWriter::new(writers);
    let input = &mut NamedReader {
        inner: Box::new(stdin()) as Box<dyn Read>,
    };

    // TODO: replaced generic 'copy' call to be able to stop copying
    // if all outputs are closed (due to errors)
    if copy(input, &mut output).is_err() || output.flush().is_err() || output.error_occurred() {
        Err(Error::new(ErrorKind::Other, ""))
    } else {
        Ok(())
    }
}

fn open(name: String, append: bool) -> Box<dyn Write> {
    let path = PathBuf::from(name.clone());
    let inner: Box<dyn Write> = {
        let mut options = OpenOptions::new();
        let mode = if append {
            options.append(true)
        } else {
            options.truncate(true)
        };
        match mode.write(true).create(true).open(path.as_path()) {
            Ok(file) => Box::new(file),
            Err(_) => Box::new(sink()),
        }
    };
    Box::new(NamedWriter { inner, name }) as Box<dyn Write>
}

struct MultiWriter {
    writers: Vec<NamedWriter>,
    initial_len: usize,
}

impl MultiWriter {
    fn new(writers: Vec<NamedWriter>) -> Self {
        Self {
            initial_len: writers.len(),
            writers,
        }
    }
    fn error_occurred(&self) -> bool {
        self.writers.len() != self.initial_len
    }
}

impl Write for MultiWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        RetainMut::retain_mut(&mut self.writers, |writer| {
            let result = writer.write_all(buf);
            match result {
                Err(f) => {
                    show_error!("{}: {}", writer.name.maybe_quote(), f);
                    false
                }
                _ => true,
            }
        });
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        RetainMut::retain_mut(&mut self.writers, |writer| {
            let result = writer.flush();
            match result {
                Err(f) => {
                    show_error!("{}: {}", writer.name.maybe_quote(), f);
                    false
                }
                _ => true,
            }
        });
        Ok(())
    }
}

struct NamedWriter {
    inner: Box<dyn Write>,
    pub name: String,
}

impl Write for NamedWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.inner.write(buf)
    }

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

struct NamedReader {
    inner: Box<dyn Read>,
}

impl Read for NamedReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        match self.inner.read(buf) {
            Err(f) => {
                show_error!("stdin: {}", f);
                Err(f)
            }
            okay => okay,
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>document `ReadToString` and conceal constructor<commit_after><|endoftext|>


<commit_before><commit_msg>Fix month parse for months with leading whitespace<commit_after><|endoftext|>


<commit_before>use bot::BotEnv;
use filters::Filter;

use lalafell::commands::prelude::*;
use lalafell::error::*;

use serenity::prelude::Mentionable;
use serenity::model::guild::Role;
use serenity::builder::CreateEmbed;

use chrono::Utc;

const USAGE: &'static str = "!search <filters>";

pub struct SearchCommand;

impl SearchCommand {
  pub fn new(_: Arc<BotEnv>) -> Self {
    SearchCommand
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  filter_strings: Vec<String>
}

impl HasParams for SearchCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for SearchCommand {
  fn run(&self, _: &Context, message: &Message, guild: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;

    let member = guild.member(&message.author).chain_err(|| "could not get member")?;
    if !member.permissions().chain_err(|| "could not get permissions")?.manage_roles() {
      return Err(ExternalCommandFailure::default()
        .message(|e: CreateEmbed| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let filters: Vec<Filter> = match params.filter_strings.iter().map(|x| Filter::parse(x)).collect::<Option<_>>() {
      Some(f) => f,
      None => return Err("Invalid filter.".into())
    };
    let guild = some_or!(guild.find(), bail!("could not find guild"));
    let roles: Vec<Role> = guild.read().roles.values().cloned().collect();
    let now = Utc::now();
    let matches: Vec<String> = guild.read().members.values()
      .filter(|m| filters.iter().all(|f| f.matches(m, &roles)))
      .map(|m| format!("{} - {}",
        m.mention(),
        m.joined_at
          .map(|d| now.clone().signed_duration_since(d))
          .map(|d| {
            let mut res = String::new();
            let seconds = d.num_seconds() % 60;
            let minutes = d.num_minutes() % 60;
            let hours = d.num_hours() % 24;
            let days = d.num_days();
            if days > 0 {
              res.push_str(&format!("{} day{}, ", days, if days == 1 { "" } else { "s" }));
            }
            if hours > 0 {
              res.push_str(&format!("{} hour{}, ", hours, if hours == 1 { "" } else { "s" }));
            }
            if minutes > 0 {
              res.push_str(&format!("{} minute{}, ", minutes, if minutes == 1 { "" } else { "s" }));
            }
            if seconds > 0 {
              res.push_str(&format!("{} second{}", seconds, if seconds == 1 { "" } else { "s" }));
            }
            res
          })
          .unwrap_or_else(|| String::from("unknown"))))
      .collect();
    Ok(matches.join("\n").into())
  }
}
<commit_msg>refactor: make !search public<commit_after>use bot::BotEnv;
use filters::Filter;

use lalafell::commands::prelude::*;

use serenity::prelude::Mentionable;
use serenity::model::guild::Role;

use chrono::Utc;

const USAGE: &'static str = "!search <filters>";

pub struct SearchCommand;

impl SearchCommand {
  pub fn new(_: Arc<BotEnv>) -> Self {
    SearchCommand
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  filter_strings: Vec<String>
}

impl HasParams for SearchCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for SearchCommand {
  fn run(&self, _: &Context, _: &Message, guild: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;

    let filters: Vec<Filter> = match params.filter_strings.iter().map(|x| Filter::parse(x)).collect::<Option<_>>() {
      Some(f) => f,
      None => return Err("Invalid filter.".into())
    };
    let guild = some_or!(guild.find(), bail!("could not find guild"));
    let roles: Vec<Role> = guild.read().roles.values().cloned().collect();
    let now = Utc::now();
    let matches: Vec<String> = guild.read().members.values()
      .filter(|m| filters.iter().all(|f| f.matches(m, &roles)))
      .map(|m| format!("{} - {}",
        m.mention(),
        m.joined_at
          .map(|d| now.clone().signed_duration_since(d))
          .map(|d| {
            let mut res = String::new();
            let seconds = d.num_seconds() % 60;
            let minutes = d.num_minutes() % 60;
            let hours = d.num_hours() % 24;
            let days = d.num_days();
            if days > 0 {
              res.push_str(&format!("{} day{}, ", days, if days == 1 { "" } else { "s" }));
            }
            if hours > 0 {
              res.push_str(&format!("{} hour{}, ", hours, if hours == 1 { "" } else { "s" }));
            }
            if minutes > 0 {
              res.push_str(&format!("{} minute{}, ", minutes, if minutes == 1 { "" } else { "s" }));
            }
            if seconds > 0 {
              res.push_str(&format!("{} second{}", seconds, if seconds == 1 { "" } else { "s" }));
            }
            res
          })
          .unwrap_or_else(|| String::from("unknown"))))
      .collect();
    Ok(matches.join("\n").into())
  }
}
<|endoftext|>


<commit_before>use {SemanticColor, Color, GeneralLayoutSettings, Timer, TimerPhase, TimeSpan, TimingMethod};
use time_formatter::{timer as formatter, TimeFormatter, Accuracy, DigitsFormat};
use analysis::split_color;
use serde_json::{to_writer, Result};
use std::io::Write;
use std::borrow::Cow;
use layout::editor::{SettingsDescription, Value, Field};
use palette::{Hsva, Rgba};

#[derive(Default, Clone)]
pub struct Component {
    settings: Settings,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct Settings {
    pub timing_method: Option<TimingMethod>,
    pub digits_format: DigitsFormat,
    pub accuracy: Accuracy,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            timing_method: None,
            digits_format: DigitsFormat::SingleDigitSeconds,
            accuracy: Accuracy::Hundredths,
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct State {
    pub time: String,
    pub fraction: String,
    pub semantic_color: SemanticColor,
    pub top_color: Color,
    pub bottom_color: Color,
}

impl State {
    pub fn write_json<W>(&self, writer: W) -> Result<()>
    where
        W: Write,
    {
        to_writer(writer, self)
    }
}

impl Component {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn with_settings(settings: Settings) -> Self {
        Self {
            settings,
            ..Default::default()
        }
    }

    pub fn settings(&self) -> &Settings {
        &self.settings
    }

    pub fn settings_mut(&mut self) -> &mut Settings {
        &mut self.settings
    }

    pub fn name(&self) -> Cow<str> {
        "Timer".into()
    }

    pub fn state(&self, timer: &Timer, layout_settings: &GeneralLayoutSettings) -> State {
        let method = self.settings
            .timing_method
            .unwrap_or_else(|| timer.current_timing_method());
        let time = timer.current_time();
        let time = time[method].or(time.real_time).unwrap_or_default();
        let current_comparison = timer.current_comparison();

        let semantic_color = match timer.current_phase() {
            TimerPhase::Running if time >= TimeSpan::zero() => {
                let pb_split_time = timer
                    .current_split()
                    .unwrap()
                    .comparison(current_comparison)
                    [method];

                if let Some(pb_split_time) = pb_split_time {
                    split_color(
                        timer,
                        Some(time - pb_split_time),
                        timer.current_split_index() as usize,
                        true,
                        false,
                        current_comparison,
                        method,
                    ).or(SemanticColor::AheadGainingTime)
                } else {
                    SemanticColor::AheadGainingTime
                }
            }
            TimerPhase::Paused => SemanticColor::Paused,
            TimerPhase::Ended => {
                let pb_time = timer
                    .run()
                    .segments()
                    .last()
                    .unwrap()
                    .comparison(current_comparison)
                    [method];

                if pb_time.map_or(true, |t| time < t) {
                    SemanticColor::PersonalBest
                } else {
                    SemanticColor::BehindLosingTime
                }
            }
            _ => SemanticColor::NotRunning,
        };

        let visual_color = semantic_color.visualize(layout_settings);
        let (top_color, bottom_color) = top_and_bottom_color(visual_color);

        State {
            time: formatter::Time::with_digits_format(self.settings.digits_format)
                .format(time)
                .to_string(),
            fraction: formatter::Fraction::with_accuracy(self.settings.accuracy)
                .format(time)
                .to_string(),
            semantic_color,
            top_color,
            bottom_color,
        }
    }

    pub fn settings_description(&self) -> SettingsDescription {
        SettingsDescription::with_fields(vec![
            Field::new("Timing Method".into(), self.settings.timing_method.into()),
            Field::new("Digits Format".into(), self.settings.digits_format.into()),
            Field::new("Accuracy".into(), self.settings.accuracy.into()),
        ])
    }

    pub fn set_value(&mut self, index: usize, value: Value) {
        match index {
            0 => self.settings.timing_method = value.into(),
            1 => self.settings.digits_format = value.into(),
            2 => self.settings.accuracy = value.into(),
            _ => panic!("Unsupported Setting Index"),
        }
    }
}

pub fn top_and_bottom_color(color: Color) -> (Color, Color) {
    let hsv: Hsva = color.rgba.color.into();
    let top_color = Rgba::from(Hsva::new(
        hsv.hue,
        0.5 * hsv.saturation,
        (1.5 * hsv.value + 0.1).min(1.0),
        hsv.alpha,
    )).into();
    let bottom_color = Rgba::from(Hsva::new(
        hsv.hue,
        hsv.saturation,
        0.8 * hsv.value,
        hsv.alpha,
    )).into();
    (top_color, bottom_color)
}
<commit_msg>Work around WebAssembly f64-rem bug<commit_after>use {SemanticColor, Color, GeneralLayoutSettings, Timer, TimerPhase, TimeSpan, TimingMethod};
use time_formatter::{timer as formatter, TimeFormatter, Accuracy, DigitsFormat};
use analysis::split_color;
use serde_json::{to_writer, Result};
use std::io::Write;
use std::borrow::Cow;
use layout::editor::{SettingsDescription, Value, Field};
use palette::{Hsv, Rgb};

#[derive(Default, Clone)]
pub struct Component {
    settings: Settings,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct Settings {
    pub timing_method: Option<TimingMethod>,
    pub digits_format: DigitsFormat,
    pub accuracy: Accuracy,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            timing_method: None,
            digits_format: DigitsFormat::SingleDigitSeconds,
            accuracy: Accuracy::Hundredths,
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct State {
    pub time: String,
    pub fraction: String,
    pub semantic_color: SemanticColor,
    pub top_color: Color,
    pub bottom_color: Color,
}

impl State {
    pub fn write_json<W>(&self, writer: W) -> Result<()>
    where
        W: Write,
    {
        to_writer(writer, self)
    }
}

impl Component {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn with_settings(settings: Settings) -> Self {
        Self {
            settings,
            ..Default::default()
        }
    }

    pub fn settings(&self) -> &Settings {
        &self.settings
    }

    pub fn settings_mut(&mut self) -> &mut Settings {
        &mut self.settings
    }

    pub fn name(&self) -> Cow<str> {
        "Timer".into()
    }

    pub fn state(&self, timer: &Timer, layout_settings: &GeneralLayoutSettings) -> State {
        let method = self.settings
            .timing_method
            .unwrap_or_else(|| timer.current_timing_method());
        let time = timer.current_time();
        let time = time[method].or(time.real_time).unwrap_or_default();
        let current_comparison = timer.current_comparison();

        let semantic_color = match timer.current_phase() {
            TimerPhase::Running if time >= TimeSpan::zero() => {
                let pb_split_time = timer
                    .current_split()
                    .unwrap()
                    .comparison(current_comparison)
                    [method];

                if let Some(pb_split_time) = pb_split_time {
                    split_color(
                        timer,
                        Some(time - pb_split_time),
                        timer.current_split_index() as usize,
                        true,
                        false,
                        current_comparison,
                        method,
                    ).or(SemanticColor::AheadGainingTime)
                } else {
                    SemanticColor::AheadGainingTime
                }
            }
            TimerPhase::Paused => SemanticColor::Paused,
            TimerPhase::Ended => {
                let pb_time = timer
                    .run()
                    .segments()
                    .last()
                    .unwrap()
                    .comparison(current_comparison)
                    [method];

                if pb_time.map_or(true, |t| time < t) {
                    SemanticColor::PersonalBest
                } else {
                    SemanticColor::BehindLosingTime
                }
            }
            _ => SemanticColor::NotRunning,
        };

        let visual_color = semantic_color.visualize(layout_settings);
        let (top_color, bottom_color) = top_and_bottom_color(visual_color);

        State {
            time: formatter::Time::with_digits_format(self.settings.digits_format)
                .format(time)
                .to_string(),
            fraction: formatter::Fraction::with_accuracy(self.settings.accuracy)
                .format(time)
                .to_string(),
            semantic_color,
            top_color,
            bottom_color,
        }
    }

    pub fn settings_description(&self) -> SettingsDescription {
        SettingsDescription::with_fields(vec![
            Field::new("Timing Method".into(), self.settings.timing_method.into()),
            Field::new("Digits Format".into(), self.settings.digits_format.into()),
            Field::new("Accuracy".into(), self.settings.accuracy.into()),
        ])
    }

    pub fn set_value(&mut self, index: usize, value: Value) {
        match index {
            0 => self.settings.timing_method = value.into(),
            1 => self.settings.digits_format = value.into(),
            2 => self.settings.accuracy = value.into(),
            _ => panic!("Unsupported Setting Index"),
        }
    }
}

pub fn top_and_bottom_color(color: Color) -> (Color, Color) {
    let hsv: Hsv = color.rgba.into();

    let h = hsv.hue.to_degrees() as f64;
    let s = hsv.saturation as f64;
    let v = hsv.value as f64;
    let a = color.rgba.alpha;

    let top_color = Rgb::from(Hsv::new(h.into(), 0.5 * s, (1.5 * v + 0.1).min(1.0)));
    let bottom_color = Rgb::from(Hsv::new(h.into(), s, 0.8 * v));

    let top_color = Color::from((
        top_color.red as f32,
        top_color.green as f32,
        top_color.blue as f32,
        a,
    ));

    let bottom_color = Color::from((
        bottom_color.red as f32,
        bottom_color.green as f32,
        bottom_color.blue as f32,
        a,
    ));

    (top_color, bottom_color)
}
<|endoftext|>


<commit_before>// ignore-tidy-linelength
// compile-flags: -C no-prepopulate-passes
// min-llvm-version 8.0

#![crate_type = "lib"]

#![allow(non_camel_case_types)]
#![feature(repr_simd, platform_intrinsics, min_const_generics)]

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct S<const N: usize>([f32; N]);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct T([f32; 4]);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct U(f32, f32, f32, f32);

// CHECK-LABEL: @build_array_s
#[no_mangle]
pub fn build_array_s(x: [f32; 4]) -> S<4> {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
    S::<4>(x)
}

// CHECK-LABEL: @build_array_t
#[no_mangle]
pub fn build_array_t(x: [f32; 4]) -> T {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
    T(x)
}

// CHECK-LABEL: @build_array_u
#[no_mangle]
pub fn build_array_u(x: [f32; 4]) -> U {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
    unsafe { std::mem::transmute(x) }
}
<commit_msg>generalize codegen to non 64bit platforms<commit_after>// ignore-tidy-linelength
// compile-flags: -C no-prepopulate-passes
// min-llvm-version 8.0

#![crate_type = "lib"]

#![allow(non_camel_case_types)]
#![feature(repr_simd, platform_intrinsics, min_const_generics)]

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct S<const N: usize>([f32; N]);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct T([f32; 4]);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct U(f32, f32, f32, f32);

// CHECK-LABEL: @build_array_s
#[no_mangle]
pub fn build_array_s(x: [f32; 4]) -> S<4> {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i{{[0-9]+}} 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i{{[0-9]+}} 16, i1 false)
    S::<4>(x)
}

// CHECK-LABEL: @build_array_t
#[no_mangle]
pub fn build_array_t(x: [f32; 4]) -> T {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i{{[0-9]+}} 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i{{[0-9]+}} 16, i1 false)
    T(x)
}

// CHECK-LABEL: @build_array_u
#[no_mangle]
pub fn build_array_u(x: [f32; 4]) -> U {
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i{{[0-9]+}} 16, i1 false)
    // CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{[0-9]+}}(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i{{[0-9]+}} 16, i1 false)
    unsafe { std::mem::transmute(x) }
}
<|endoftext|>


<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.
use std::collections::HashMap;

use clap::{crate_version, App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert(
            $name,
            Help {
                short: $short,
                long: concat!($long, "\n "),
            },
        );
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .after_help(
            "Note: `fd -h` prints a short and concise overview while `fd --help` \
             gives all details.",
        )
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .long("unrestricted")
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(
            arg("glob")
                .long("glob")
                .short("g")
                .conflicts_with("fixed-strings"),
        )
        .arg(
            arg("regex")
                .long("regex")
                .overrides_with("glob")
                .hidden_short_help(true),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        // support --maxdepth as well, for compatibility with rg
        .arg(
            arg("rg-depth")
                .long("maxdepth")
                .hidden(true)
                .takes_value(true),
        )
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&[
                    "f",
                    "file",
                    "d",
                    "directory",
                    "l",
                    "symlink",
                    "x",
                    "executable",
                    "e",
                    "empty",
                ])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exec-batch")
                .long("exec-batch")
                .short("X")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd")
                .conflicts_with("exec"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("ignore-file")
                .long("ignore-file")
                .takes_value(true)
                .value_name("path")
                .number_of_values(1)
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num")
                .hidden_short_help(true),
        )
        .arg(
            arg("size")
                .long("size")
                .short("S")
                .takes_value(true)
                .number_of_values(1)
                .allow_hyphen_values(true)
                .multiple(true),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(
            arg("changed-within")
                .long("changed-within")
                .alias("change-newer-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("changed-before")
                .long("changed-before")
                .alias("change-older-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("show-errors")
                .long("show-errors")
                .hidden_short_help(true),
        )
        .arg(arg("pattern"))
        .arg(
            arg("path-separator")
                .takes_value(true)
                .value_name("separator")
                .long("path-separator")
                .hidden_short_help(true),
        )
        .arg(arg("path").multiple(true))
        .arg(
            arg("search-path")
                .long("search-path")
                .takes_value(true)
                .conflicts_with("path")
                .multiple(true)
                .hidden_short_help(true)
                .number_of_values(1),
        )
}

#[rustfmt::skip]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped). Files and directories are considered to be hidden if \
           their name starts with a `.` sign (dot).");
    doc!(h, "no-ignore"
        , "Do not respect .(git|fd)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore', '.ignore' or '.fdignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "glob"
        , "Glob-based search (default: regular expression)"
        , "Perform a glob-based search instead of a regular expression search.");
    doc!(h, "regex"
        , "Perform a regex-based search"
        , "Perform a regular-expression based seach (default). This can be used to override --glob.");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "path-separator"
        , "Set the path separator to use when printing file paths."
        , "Set the path separator to use when printing file paths. The default is the OS-specific \
           separator ('/' on Unix, '\\' on Windows).");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "rg-depth"
        , "See --max-depth"
        , "See --max-depth");
    doc!(h, "file-type"
        , "Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), empty (e)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links\n  \
             'x' or 'executable':   executables\n  \
             'e' or 'empty':        empty files or directories");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exec-batch"
        , "Execute a command with all search results at once"
        , "Execute a command with all search results at once.\n\
           All arguments following --exec-batch are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           A single occurence of the following placeholders is authorized and substituted by the paths derived from the \
           search results before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "ignore-file"
        , "Add a custom ignore-file in .gitignore format"
        , "Add a custom ignore-file in '.gitignore' format. These files have a low precedence.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to \
           the console.");
    doc!(h, "pattern"
        , "the search pattern: a regular expression unless '--glob' is used (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , ""
        , "Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'.");
    doc!(h, "size"
        , "Limit results based on the size of files."
        , "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n   \
            '+': file size must be greater than or equal to this\n   \
            '-': file size must be less than or equal to this\n   \
            'NUM':  The numeric size (e.g. 500)\n   \
            'UNIT': The units for NUM. They are not case-sensitive.\n\
            Allowed unit values:\n   \
                'b':  bytes\n   \
                'k':  kilobytes\n   \
                'm':  megabytes\n   \
                'g':  gigabytes\n   \
                't':  terabytes\n   \
                'ki': kibibytes\n   \
                'mi': mebibytes\n   \
                'gi': gibibytes\n   \
                'ti': tebibytes");
    doc!(h, "changed-within"
        , "Filter by file modification time (newer than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-newer-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-within 2weeks\n    \
               --change-newer-than '2018-10-27 10:00:00'");
    doc!(h, "changed-before"
        , "Filter by file modification time (older than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-older-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-before '2018-10-27 10:00:00'\n    \
               --change-older-than 2weeks");
    doc!(h, "show-errors"
        , "Enable display of filesystem errors"
        , "Enable the display of filesystem errors for situations such as insufficient permissions \
            or dead symlinks.");
    doc!(h, "search-path"
        , "(hidden)"
        , "Provide paths to search as an alternative to the positional <path> argument. \
           Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`");
    h
}
<commit_msg>Fix idempotent flags<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.
use std::collections::HashMap;

use clap::{crate_version, App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert(
            $name,
            Help {
                short: $short,
                long: concat!($long, "\n "),
            },
        );
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .after_help(
            "Note: `fd -h` prints a short and concise overview while `fd --help` \
             gives all details.",
        )
        .arg(
            arg("hidden")
                .long("hidden")
                .short("H")
                .overrides_with("hidden")
        )
        .arg(arg("no-ignore").long("no-ignore").short("I").overrides_with("no-ignore"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs").overrides_with("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .long("unrestricted")
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with_all(&["ignore-case", "case-sensitive"]),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with_all(&["case-sensitive", "ignore-case"]),
        )
        .arg(
            arg("glob")
                .long("glob")
                .short("g")
                .conflicts_with("fixed-strings")
                .overrides_with("glob"),
        )
        .arg(
            arg("regex")
                .long("regex")
                .overrides_with_all(&["glob", "regex"])
                .hidden_short_help(true),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal")
                .overrides_with("fixed-strings"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a").overrides_with("absolute-path"))
        .arg(arg("follow").long("follow").short("L").alias("dereference").overrides_with("follow"))
        .arg(arg("full-path").long("full-path").short("p").overrides_with("full-path"))
        .arg(arg("null_separator").long("print0").short("0").overrides_with("print0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        // support --maxdepth as well, for compatibility with rg
        .arg(
            arg("rg-depth")
                .long("maxdepth")
                .hidden(true)
                .takes_value(true),
        )
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&[
                    "f",
                    "file",
                    "d",
                    "directory",
                    "l",
                    "symlink",
                    "x",
                    "executable",
                    "e",
                    "empty",
                ])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exec-batch")
                .long("exec-batch")
                .short("X")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd")
                .conflicts_with("exec"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("ignore-file")
                .long("ignore-file")
                .takes_value(true)
                .value_name("path")
                .number_of_values(1)
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num")
                .hidden_short_help(true),
        )
        .arg(
            arg("size")
                .long("size")
                .short("S")
                .takes_value(true)
                .number_of_values(1)
                .allow_hyphen_values(true)
                .multiple(true),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(
            arg("changed-within")
                .long("changed-within")
                .alias("change-newer-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("changed-before")
                .long("changed-before")
                .alias("change-older-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("show-errors")
                .long("show-errors")
                .hidden_short_help(true)
                .overrides_with("show-errors"),
        )
        .arg(arg("pattern"))
        .arg(
            arg("path-separator")
                .takes_value(true)
                .value_name("separator")
                .long("path-separator")
                .hidden_short_help(true),
        )
        .arg(arg("path").multiple(true))
        .arg(
            arg("search-path")
                .long("search-path")
                .takes_value(true)
                .conflicts_with("path")
                .multiple(true)
                .hidden_short_help(true)
                .number_of_values(1),
        )
}

#[rustfmt::skip]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped). Files and directories are considered to be hidden if \
           their name starts with a `.` sign (dot).");
    doc!(h, "no-ignore"
        , "Do not respect .(git|fd)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore', '.ignore' or '.fdignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "glob"
        , "Glob-based search (default: regular expression)"
        , "Perform a glob-based search instead of a regular expression search.");
    doc!(h, "regex"
        , "Perform a regex-based search"
        , "Perform a regular-expression based seach (default). This can be used to override --glob.");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "path-separator"
        , "Set the path separator to use when printing file paths."
        , "Set the path separator to use when printing file paths. The default is the OS-specific \
           separator ('/' on Unix, '\\' on Windows).");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "rg-depth"
        , "See --max-depth"
        , "See --max-depth");
    doc!(h, "file-type"
        , "Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), empty (e)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links\n  \
             'x' or 'executable':   executables\n  \
             'e' or 'empty':        empty files or directories");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exec-batch"
        , "Execute a command with all search results at once"
        , "Execute a command with all search results at once.\n\
           All arguments following --exec-batch are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           A single occurence of the following placeholders is authorized and substituted by the paths derived from the \
           search results before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "ignore-file"
        , "Add a custom ignore-file in .gitignore format"
        , "Add a custom ignore-file in '.gitignore' format. These files have a low precedence.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to \
           the console.");
    doc!(h, "pattern"
        , "the search pattern: a regular expression unless '--glob' is used (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , ""
        , "Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for '--no-ignore --hidden'.");
    doc!(h, "size"
        , "Limit results based on the size of files."
        , "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n   \
            '+': file size must be greater than or equal to this\n   \
            '-': file size must be less than or equal to this\n   \
            'NUM':  The numeric size (e.g. 500)\n   \
            'UNIT': The units for NUM. They are not case-sensitive.\n\
            Allowed unit values:\n   \
                'b':  bytes\n   \
                'k':  kilobytes\n   \
                'm':  megabytes\n   \
                'g':  gigabytes\n   \
                't':  terabytes\n   \
                'ki': kibibytes\n   \
                'mi': mebibytes\n   \
                'gi': gibibytes\n   \
                'ti': tebibytes");
    doc!(h, "changed-within"
        , "Filter by file modification time (newer than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-newer-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-within 2weeks\n    \
               --change-newer-than '2018-10-27 10:00:00'");
    doc!(h, "changed-before"
        , "Filter by file modification time (older than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-older-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-before '2018-10-27 10:00:00'\n    \
               --change-older-than 2weeks");
    doc!(h, "show-errors"
        , "Enable display of filesystem errors"
        , "Enable the display of filesystem errors for situations such as insufficient permissions \
            or dead symlinks.");
    doc!(h, "search-path"
        , "(hidden)"
        , "Provide paths to search as an alternative to the positional <path> argument. \
           Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`");
    h
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::{thread, time};
use std::cell::{RefCell, RefMut};

use reqwest::{Response};

use errors::*;
use client::MatrixClient;
use matrix_types::Event;

#[derive(Clone)]
pub struct RoomEvent<'a> {
    pub room_id: &'a str,
    pub raw_event: Event,
}

pub struct Bot <'a, 'b> {
    client: RefCell<&'b mut MatrixClient>,
    root_services: Vec<&'a str>,
    all_services: HashMap<&'a str, RefCell<Box<Node<'a>>>>,

    rooms: RefCell<Vec<String>>,
}

impl<'a, 'b> Bot<'a, 'b> {
    pub fn new(client: &'b mut MatrixClient) -> Self{
        Bot {
            client: RefCell::new(client),
            root_services: Vec::new(),
            all_services: HashMap::new(),
            rooms: RefCell::new(Vec::new()),
        }
    }

    pub fn join(&self, room_id: &str) -> Result<Response, RustixError>{
        self.rooms.borrow_mut().push(room_id.clone().to_string());

        self.client.borrow().join(room_id)
    }

    pub fn say(&self, room_id: &str, message: &str) -> Result<Response, RustixError> {
        self.client.borrow_mut().send_msg(room_id, message)
    }

    pub fn reply(&self, event: &RoomEvent, message: &str) -> Result<Response, RustixError> {
        self.say(event.room_id, message)
    }

    pub fn kick(&self, room_id: &str, user_id: &str, reason: Option<&str>) {
        self.client.borrow().kick(room_id, user_id, reason);
    }

    /*
    pub fn action(&self, room_id: &str, action: &str) {
    }

    pub fn set_name(&self, name: &str) {
    }
    */

    pub fn register_service(&mut self,
                            name: &'a str,
                            parent: Option<&'a str>,
                            mut service: Box<Node<'a>>) -> Option<&'a str> {
        match parent {
            Some(p) => {
                self.all_services.get_mut(p).expect("Invalid parent node")
                    .borrow_mut().register_child(name)
            },
            None => self.root_services.push(name),
        };

        service.on_load();

        self.all_services.insert(name, RefCell::new(service));

        Some(name)
    }

    pub fn get_service(&self, name: &str) -> RefMut<Box<Node<'a>>> {
        self.all_services.get(name).unwrap().borrow_mut()
    }

    pub fn propagate_event(&self, event: &RoomEvent) {
        for service in &self.root_services {
            self.all_services.get(service).unwrap()
                .borrow_mut().handle(self, event.clone());
        }
    }

    pub fn run(&mut self) {
        let initial_rooms = vec!["test", "test2", "#geeks"];
        for room in initial_rooms {
            if let Some(rid) = self.client.borrow().get_public_room_id(room) {
                println!("Joining {} id: {}", &room, &rid);
                self.join(&rid);
            } else {
                println!("Could not join room {}", &room);
            }
        }

        let mut next_batch: String = self.client.borrow().sync(None).unwrap().next_batch;

        let delay = time::Duration::from_millis(800);

        loop {
            // TODO: NLL May let this extra temp variable die
            let sync = self.client.borrow().sync(Some(&next_batch));

            if let Ok(sync_data) = sync {
                for room_id in self.rooms.borrow().iter() {
                    if let Some(room) = sync_data.rooms.join.get(room_id) {
                        for raw_event in &room.timeline.events {
                            self.propagate_event(
                                &RoomEvent{room_id, raw_event: raw_event.clone()}
                            );
                        }
                    }
                }

                next_batch = sync_data.next_batch;
            } else {
                println!("Had a sync timeout.");
            }

            thread::sleep(delay);
        }
    }
}

pub trait Node<'a> {
    fn children(&self) -> Option<&Vec<&'a str>> {
        None
    }

    fn register_child(&mut self, name: &'a str) {
    }

    fn propagate_event(&self, bot: &Bot, event: &RoomEvent) {
        if let Some(children) = self.children() {
            for child in children {
                bot.get_service(child).handle(bot, event.clone());
            }
        }
    }

    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        self.propagate_event(bot, &event);
    }

    fn on_load(&mut self) { }

    fn on_exit(&self) { }
}

/*
if let Ok(x) = serde_json::to_string_pretty(&res.rooms.join) {
    println!("{}", x);
} */
<commit_msg>Additionally autojoin #random now<commit_after>use std::collections::HashMap;
use std::{thread, time};
use std::cell::{RefCell, RefMut};

use reqwest::{Response};

use errors::*;
use client::MatrixClient;
use matrix_types::Event;

#[derive(Clone)]
pub struct RoomEvent<'a> {
    pub room_id: &'a str,
    pub raw_event: Event,
}

pub struct Bot <'a, 'b> {
    client: RefCell<&'b mut MatrixClient>,
    root_services: Vec<&'a str>,
    all_services: HashMap<&'a str, RefCell<Box<Node<'a>>>>,

    rooms: RefCell<Vec<String>>,
}

impl<'a, 'b> Bot<'a, 'b> {
    pub fn new(client: &'b mut MatrixClient) -> Self{
        Bot {
            client: RefCell::new(client),
            root_services: Vec::new(),
            all_services: HashMap::new(),
            rooms: RefCell::new(Vec::new()),
        }
    }

    pub fn join(&self, room_id: &str) -> Result<Response, RustixError>{
        self.rooms.borrow_mut().push(room_id.clone().to_string());

        self.client.borrow().join(room_id)
    }

    pub fn say(&self, room_id: &str, message: &str) -> Result<Response, RustixError> {
        self.client.borrow_mut().send_msg(room_id, message)
    }

    pub fn reply(&self, event: &RoomEvent, message: &str) -> Result<Response, RustixError> {
        self.say(event.room_id, message)
    }

    pub fn kick(&self, room_id: &str, user_id: &str, reason: Option<&str>) {
        self.client.borrow().kick(room_id, user_id, reason);
    }

    /*
    pub fn action(&self, room_id: &str, action: &str) {
    }

    pub fn set_name(&self, name: &str) {
    }
    */

    pub fn register_service(&mut self,
                            name: &'a str,
                            parent: Option<&'a str>,
                            mut service: Box<Node<'a>>) -> Option<&'a str> {
        match parent {
            Some(p) => {
                self.all_services.get_mut(p).expect("Invalid parent node")
                    .borrow_mut().register_child(name)
            },
            None => self.root_services.push(name),
        };

        service.on_load();

        self.all_services.insert(name, RefCell::new(service));

        Some(name)
    }

    pub fn get_service(&self, name: &str) -> RefMut<Box<Node<'a>>> {
        self.all_services.get(name).unwrap().borrow_mut()
    }

    pub fn propagate_event(&self, event: &RoomEvent) {
        for service in &self.root_services {
            self.all_services.get(service).unwrap()
                .borrow_mut().handle(self, event.clone());
        }
    }

    pub fn run(&mut self) {
        let initial_rooms = vec!["test", "test2", "#geeks", "#random"];
        for room in initial_rooms {
            if let Some(rid) = self.client.borrow().get_public_room_id(room) {
                println!("Joining {} id: {}", &room, &rid);
                self.join(&rid);
            } else {
                println!("Could not join room {}", &room);
            }
        }

        let mut next_batch: String = self.client.borrow().sync(None).unwrap().next_batch;

        let delay = time::Duration::from_millis(800);

        loop {
            // TODO: NLL May let this extra temp variable die
            let sync = self.client.borrow().sync(Some(&next_batch));

            if let Ok(sync_data) = sync {
                for room_id in self.rooms.borrow().iter() {
                    if let Some(room) = sync_data.rooms.join.get(room_id) {
                        for raw_event in &room.timeline.events {
                            self.propagate_event(
                                &RoomEvent{room_id, raw_event: raw_event.clone()}
                            );
                        }
                    }
                }

                next_batch = sync_data.next_batch;
            } else {
                println!("Had a sync timeout.");
            }

            thread::sleep(delay);
        }
    }
}

pub trait Node<'a> {
    fn children(&self) -> Option<&Vec<&'a str>> {
        None
    }

    fn register_child(&mut self, name: &'a str) {
    }

    fn propagate_event(&self, bot: &Bot, event: &RoomEvent) {
        if let Some(children) = self.children() {
            for child in children {
                bot.get_service(child).handle(bot, event.clone());
            }
        }
    }

    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        self.propagate_event(bot, &event);
    }

    fn on_load(&mut self) { }

    fn on_exit(&self) { }
}

/*
if let Ok(x) = serde_json::to_string_pretty(&res.rooms.join) {
    println!("{}", x);
} */
<|endoftext|>


<commit_before>
use super::*;

//

tests_impls!
{

  #[ test ]
  fn pair() -> Result< () >
  {

    // test.case( "basic" );
    let code = qt!( x core::option::Option< i32 > );
    let got = syn::parse2::< TheModule::Pair< syn::Ident, syn::Type > >( code ).unwrap();
    let exp = TheModule::Pair::< syn::Ident, syn::Type >::new
    (
      syn::Ident::new( "x", proc_macro2::Span::call_site() ),
      syn::parse2::< syn::Type >( qt!( core::option::Option< i32 > ) )?,
    );
    a_id!( got, exp );

    Ok( () )
  }

  //

  #[ test ]
  fn many() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "basic" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      #[ derive( Clone ) ]
      //
      #[ derive( Debug ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributeOuter > >( code ).unwrap();

    // syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) );

    let exp = TheModule::Many::< TheModule::AttributeOuter >::new_with( vec!
    [
      TheModule::AttributeOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ),
    ]);
    // a_id!( got, exp );

    Ok( () )
  }

}

//

tests_index!
{
  pair,
  many,
}
<commit_msg>!test<commit_after>
use super::*;

//

tests_impls!
{

  #[ test ]
  fn pair() -> Result< () >
  {

    // test.case( "basic" );
    let code = qt!( x core::option::Option< i32 > );
    let got = syn::parse2::< TheModule::Pair< syn::Ident, syn::Type > >( code ).unwrap();
    let exp = TheModule::Pair::< syn::Ident, syn::Type >::new
    (
      syn::Ident::new( "x", proc_macro2::Span::call_site() ),
      syn::parse2::< syn::Type >( qt!( core::option::Option< i32 > ) )?,
    );
    a_id!( got, exp );

    Ok( () )
  }

  //

  #[ test ]
  fn many() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "basic" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      #[ derive( Clone ) ]
      //
      #[ derive( Debug ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributeOuter > >( code ).unwrap();

    // syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) );

    let exp = TheModule::Many::< TheModule::AttributeOuter >::new_with( vec!
    [
      TheModule::AttributeOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ),
      TheModule::AttributeOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Clone ) ] ) )? ),
      TheModule::AttributeOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Debug ) ] ) )? ),
    ]);
    a_id!( got, exp );

    Ok( () )
  }

}

//

tests_index!
{
  pair,
  many,
}

<|endoftext|>