<commit_before>use std::collections::HashMap;
use std::sync::mpsc;
use std::collections::VecDeque;

use command::Command;
use updater::Updater;
use objects::update::Update;
use bot::Bot;

pub struct CommandHandler {
    commands: HashMap<String, Box<Command>>,
}

impl CommandHandler {
    pub fn new() -> Self {
        CommandHandler { commands: HashMap::new() }
    }

    pub fn add<C: Command>(&mut self, command_name: &str, command: C) {
        self.commands.insert(command_name.to_string(), Box::new(command));
    }

    pub fn start_command_handling(&mut self, rx: mpsc::Receiver<Update>, bot: &Bot) {
        loop {
            let update = rx.recv().unwrap();

            if let Some(t) = update.clone().message.and_then(|t| t.text) {
                if t.starts_with("/") {
                    let (_, bot_command) = t.split_whitespace().next().unwrap().split_at(1);
                    let command = self.commands.get_mut(bot_command);

                    if let Some(c) = command {
                        c.execute(bot, update);
                    }
                }
            }
        }
    }
}
<commit_msg>Making sure the commands still work when adding the username of the bot<commit_after>use std::collections::HashMap;
use std::sync::mpsc;

use command::Command;
use updater::Updater;
use objects::update::Update;
use bot::Bot;

pub struct CommandHandler {
    commands: HashMap<String, Box<Command>>,
}

impl CommandHandler {
    pub fn new() -> Self {
        CommandHandler { commands: HashMap::new() }
    }

    pub fn add<C: Command>(&mut self, command_name: &str, command: C) {
        self.commands.insert(command_name.to_string(), Box::new(command));
    }

    pub fn start_command_handling(&mut self, rx: mpsc::Receiver<Update>, bot: &Bot) {
        loop {
            let update = rx.recv().unwrap();

            if let Some(t) = update.clone().message.and_then(|t| t.text) {
                if t.starts_with("/") {
                    let (_, bot_command) = t.split_whitespace().next().unwrap().split_at(1);
                    let name_command: Vec<&str> = bot_command.split("@").collect();

                    if name_command.len() == 1 ||
                       name_command.len() == 2 && name_command[1] == bot.username {
                        let command = self.commands.get_mut(name_command[0]);

                        if let Some(c) = command {
                            c.execute(bot, update);
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use Display;
use BlitTarget;
use Rect;

use fbo::FramebufferAttachments;

use gl;
use context;
use version::Api;

pub fn blit(display: &Display, source: Option<&FramebufferAttachments>,
            target: Option<&FramebufferAttachments>, mask: gl::types::GLbitfield,
            src_rect: &Rect, target_rect: &BlitTarget, filter: gl::types::GLenum)
{
    unsafe {
        let mut ctxt = display.context.context.make_current();

        // FIXME: we don't draw on it
        let source = display.context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_drawing(source, &mut ctxt);
        let target = display.context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_drawing(target, &mut ctxt);

        // trying to do a named blit if possible
        if ctxt.version >= &context::GlVersion(Api::Gl, 4, 5) {
            ctxt.gl.BlitNamedFramebuffer(source, target,
                src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);

            return;
        }

        // binding source framebuffer
        if ctxt.state.read_framebuffer != source {
            if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
                ctxt.gl.BindFramebuffer(gl::READ_FRAMEBUFFER, source);
                ctxt.state.read_framebuffer = source;

            } else {
                ctxt.gl.BindFramebufferEXT(gl::READ_FRAMEBUFFER_EXT, source);
                ctxt.state.read_framebuffer = source;
            }
        }

        // binding target framebuffer
        if ctxt.state.draw_framebuffer != target {
            if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
                ctxt.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, target);
                ctxt.state.draw_framebuffer = target;

            } else {
                ctxt.gl.BindFramebufferEXT(gl::DRAW_FRAMEBUFFER_EXT, target);
                ctxt.state.draw_framebuffer = target;
            }
        }

        // doing the blit
        if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
            ctxt.gl.BlitFramebuffer(src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);

        } else {
            ctxt.gl.BlitFramebufferEXT(src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);
        }
    }
}
<commit_msg>Disable scissor testing before blitting<commit_after>use Display;
use BlitTarget;
use Rect;

use fbo::FramebufferAttachments;

use gl;
use context;
use version::Api;

pub fn blit(display: &Display, source: Option<&FramebufferAttachments>,
            target: Option<&FramebufferAttachments>, mask: gl::types::GLbitfield,
            src_rect: &Rect, target_rect: &BlitTarget, filter: gl::types::GLenum)
{
    unsafe {
        let mut ctxt = display.context.context.make_current();

        // FIXME: we don't draw on it
        let source = display.context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_drawing(source, &mut ctxt);
        let target = display.context.framebuffer_objects.as_ref().unwrap()
                            .get_framebuffer_for_drawing(target, &mut ctxt);

        // scissor testing influences blitting
        if ctxt.state.enabled_scissor_test {
            ctxt.gl.Disable(gl::SCISSOR_TEST);
            ctxt.state.enabled_scissor_test = false;
        }

        // trying to do a named blit if possible
        if ctxt.version >= &context::GlVersion(Api::Gl, 4, 5) {
            ctxt.gl.BlitNamedFramebuffer(source, target,
                src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);

            return;
        }

        // binding source framebuffer
        if ctxt.state.read_framebuffer != source {
            if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
                ctxt.gl.BindFramebuffer(gl::READ_FRAMEBUFFER, source);
                ctxt.state.read_framebuffer = source;

            } else {
                ctxt.gl.BindFramebufferEXT(gl::READ_FRAMEBUFFER_EXT, source);
                ctxt.state.read_framebuffer = source;
            }
        }

        // binding target framebuffer
        if ctxt.state.draw_framebuffer != target {
            if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
                ctxt.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, target);
                ctxt.state.draw_framebuffer = target;

            } else {
                ctxt.gl.BindFramebufferEXT(gl::DRAW_FRAMEBUFFER_EXT, target);
                ctxt.state.draw_framebuffer = target;
            }
        }

        // doing the blit
        if ctxt.version >= &context::GlVersion(Api::Gl, 3, 0) {
            ctxt.gl.BlitFramebuffer(src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);

        } else {
            ctxt.gl.BlitFramebufferEXT(src_rect.left as gl::types::GLint,
                src_rect.bottom as gl::types::GLint,
                (src_rect.left + src_rect.width) as gl::types::GLint,
                (src_rect.bottom + src_rect.height) as gl::types::GLint,
                target_rect.left as gl::types::GLint, target_rect.bottom as gl::types::GLint,
                (target_rect.left as i32 + target_rect.width) as gl::types::GLint,
                (target_rect.bottom as i32 + target_rect.height) as gl::types::GLint, mask, filter);
        }
    }
}
<|endoftext|>


<commit_before>extern crate serialize;

use std::collections::HashMap;
use serialize::{Decodable, json};

static json: &'static str = include_str!("../data/mime.json");

pub struct Types {
    ext_by_type: HashMap<String, Vec<String>>,
    type_by_ext: HashMap<String, String>

}

impl Types {
    pub fn new() -> Result<Types, ()> {
        let parsed = try!(json::from_str(json).map_err(|_| ()));
        let mut decoder = json::Decoder::new(parsed);
        let decoded: HashMap<String, Vec<String>> =
            try!(Decodable::decode(&mut decoder).map_err(|_| ()));

        let mut by_ext = HashMap::new();

        for (mime_type, exts) in decoded.iter() {
            for ext in exts.iter() {
                by_ext.insert(ext.clone(), mime_type.clone());
            }
        }

        Ok(Types { ext_by_type: decoded, type_by_ext: by_ext })
    }

    pub fn get_extension<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.ext_by_type.find_equiv(&name).map(|v| v.as_slice())
    }

    pub fn get_mime_type<'a>(&'a self, ext: &str) -> Option<&'a str> {
        self.type_by_ext.find_equiv(&ext).map(|v| v.as_slice())
    }

    pub fn mime_for_path<'a>(&'a self, path: &Path) -> &'a str {
        path.extension_str()
            .and_then(|ext| self.get_mime_type(ext))
            .unwrap_or_else(|| "text/plain")
    }
}

#[cfg(test)]
mod test {
    extern crate test;
    use Types;

    #[bench]
    fn bench_load_types(b: &mut test::Bencher) {
        b.iter(|| {
            Types::new()
        });
    }

    #[test]
    fn test_by_ext() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_extension("text/css"), Some(["css".to_str()].as_slice()));
    }

    #[test]
    fn test_by_type() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_mime_type("css"), Some("text/css"));
    }

    #[test]
    fn test_by_path() {
        let t = Types::new().ok().expect("Types didn't load");

        test_path(&t, "foo", "text/plain");
        test_path(&t, "/path/to/foo", "text/plain");
        test_path(&t, "foo.css", "text/css");
        test_path(&t, "/path/to/foo.css", "text/css");
        test_path(&t, "foo.html.css", "text/css");
        test_path(&t, "/path/to/foo.html.css", "text/css");
        test_path(&t, "/path/to.html/foo.css", "text/css");
    }

    fn test_path(types: &Types, path: &str, expected: &str) {
        assert_eq!(types.mime_for_path(&Path::new(path)), expected);
    }
}
<commit_msg>Update to rust master<commit_after>extern crate serialize;

use std::collections::HashMap;
use serialize::{Decodable, json};

static json: &'static str = include_str!("../data/mime.json");

pub struct Types {
    ext_by_type: HashMap<String, Vec<String>>,
    type_by_ext: HashMap<String, String>

}

impl Types {
    pub fn new() -> Result<Types, ()> {
        let parsed = try!(json::from_str(json).map_err(|_| ()));
        let mut decoder = json::Decoder::new(parsed);
        let decoded: HashMap<String, Vec<String>> =
            try!(Decodable::decode(&mut decoder).map_err(|_| ()));

        let mut by_ext = HashMap::new();

        for (mime_type, exts) in decoded.iter() {
            for ext in exts.iter() {
                by_ext.insert(ext.clone(), mime_type.clone());
            }
        }

        Ok(Types { ext_by_type: decoded, type_by_ext: by_ext })
    }

    pub fn get_extension<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.ext_by_type.find_equiv(name).map(|v| v.as_slice())
    }

    pub fn get_mime_type<'a>(&'a self, ext: &str) -> Option<&'a str> {
        self.type_by_ext.find_equiv(ext).map(|v| v.as_slice())
    }

    pub fn mime_for_path<'a>(&'a self, path: &Path) -> &'a str {
        path.extension_str()
            .and_then(|ext| self.get_mime_type(ext))
            .unwrap_or_else(|| "text/plain")
    }
}

#[cfg(test)]
mod test {
    extern crate test;
    use Types;

    #[bench]
    fn bench_load_types(b: &mut test::Bencher) {
        b.iter(|| {
            Types::new()
        });
    }

    #[test]
    fn test_by_ext() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_extension("text/css"), Some(["css".to_string()].as_slice()));
    }

    #[test]
    fn test_by_type() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_mime_type("css"), Some("text/css"));
    }

    #[test]
    fn test_by_path() {
        let t = Types::new().ok().expect("Types didn't load");

        test_path(&t, "foo", "text/plain");
        test_path(&t, "/path/to/foo", "text/plain");
        test_path(&t, "foo.css", "text/css");
        test_path(&t, "/path/to/foo.css", "text/css");
        test_path(&t, "foo.html.css", "text/css");
        test_path(&t, "/path/to/foo.html.css", "text/css");
        test_path(&t, "/path/to.html/foo.css", "text/css");
    }

    fn test_path(types: &Types, path: &str, expected: &str) {
        assert_eq!(types.mime_for_path(&Path::new(path)), expected);
    }
}
<|endoftext|>


<commit_before>//! Miscellaneous helper traits, enums, and structs for models.

use super::prelude::*;
use internal::RwLockExt;

#[cfg(all(feature = "model", feature = "utils"))]
use std::error::Error as StdError;
#[cfg(all(feature = "model", feature = "utils"))]
use std::result::Result as StdResult;
#[cfg(all(feature = "model", feature = "utils"))]
use std::str::FromStr;
#[cfg(all(feature = "model", feature = "utils"))]
use std::fmt;
#[cfg(all(feature = "model", any(feature = "cache", feature = "utils")))]
use utils;

/// Allows something - such as a channel or role - to be mentioned in a message.
pub trait Mentionable {
    /// Creates a mentionable string, that will be able to notify and/or create
    /// a link to the item.
    fn mention(&self) -> String;
}

impl Mentionable for ChannelId {
    fn mention(&self) -> String { format!("<#{}>", self.0) }
}

impl Mentionable for Channel {
    fn mention(&self) -> String {
        match *self {
            Channel::Guild(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
            Channel::Private(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
            Channel::Group(ref x) => format!("<#{}>", x.with(|x| x.channel_id.0)),
            Channel::Category(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
        }
    }
}

impl Mentionable for ChannelCategory {
    fn mention(&self) -> String {
        format!("#{}", self.name)
    }
}

impl Mentionable for CurrentUser {
    fn mention(&self) -> String {
        format!("<@{}>", self.id.0)
    }
}

impl Mentionable for Emoji {
    fn mention(&self) -> String { format!("<:{}:{}>", self.name, self.id.0) }
}

impl Mentionable for Member {
    fn mention(&self) -> String { format!("<@{}>", self.user.with(|u| u.id.0)) }
}

impl Mentionable for PrivateChannel {
    fn mention(&self) -> String {
        format!("<#{}>", self.id.0)
    }
}

impl Mentionable for RoleId {
    fn mention(&self) -> String { format!("<@&{}>", self.0) }
}

impl Mentionable for Role {
    fn mention(&self) -> String { format!("<@&{}>", self.id.0) }
}

impl Mentionable for UserId {
    fn mention(&self) -> String { format!("<@{}>", self.0) }
}

impl Mentionable for User {
    fn mention(&self) -> String { format!("<@{}>", self.id.0) }
}

impl Mentionable for GuildChannel {
    fn mention(&self) -> String { format!("<#{}>", self.id.0) }
}

#[cfg(all(feature = "model", feature = "utils"))]
#[derive(Debug)]
pub enum UserParseError {
    InvalidUsername,
    Rest(Box<Error>),
}

#[cfg(all(feature = "model", feature = "utils"))]
impl fmt::Display for UserParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl StdError for UserParseError {
    fn description(&self) -> &str {
        use self::UserParseError::*;

        match *self {
            InvalidUsername => "invalid username",
            Rest(_) => "could not fetch",
        }
    }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for User {
    type Err = UserParseError;

    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
        match utils::parse_username(s) {
            Some(x) => UserId(x as u64)
                .get()
                .map_err(|e| UserParseError::Rest(Box::new(e))),
            _ => Err(UserParseError::InvalidUsername),
        }
    }
}

macro_rules! impl_from_str {
    (id: $($id:tt, $err:ident;)*) => {
        $(
            #[cfg(all(feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                InvalidFormat,
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        InvalidFormat => "invalid id format",
                    }
                }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl FromStr for $id {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    Ok(match utils::parse_username(s) {
                        Some(id) => $id(id),
                        None => s.parse::<u64>().map($id).map_err(|_| $err::InvalidFormat)?,
                    })
                }
            }
        )*
    };

    (struct: $($struct:ty, $id:tt, $err:ident, $invalid_variant:tt, $parse_fn:ident, $desc:expr;)*) => {
        $(
            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                NotPresentInCache,
                $invalid_variant,
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        NotPresentInCache => "not present in cache",
                        $invalid_variant => $desc,
                    }
                }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl FromStr for $struct {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    match utils::$parse_fn(s) {
                        Some(x) => match $id(x).find() {
                            Some(user) => Ok(user),
                            _ => Err($err::NotPresentInCache),
                        },
                        _ => Err($err::$invalid_variant),
                    }
                }
            }
        )*
    };
}

impl_from_str! { id:
    UserId, UserIdParseError;
    RoleId, RoleIdParseError;
    ChannelId, ChannelIdParseError;
}

impl_from_str! { struct:
    Channel, ChannelId, ChannelParseError, InvalidChannel, parse_channel, "invalid channel";
    Role, RoleId, RoleParseError, InvalidRole, parse_role, "invalid role";
}

/// A version of an emoji used only when solely the Id and name are known.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct EmojiIdentifier {
    /// The Id of the emoji.
    pub id: EmojiId,
    /// The name of the emoji. It must be at least 2 characters long and can
    /// only contain alphanumeric characters and underscores.
    pub name: String,
}

#[cfg(all(feature = "model", feature = "utils"))]
impl EmojiIdentifier {
    /// Generates a URL to the emoji's image.
    #[inline]
    pub fn url(&self) -> String { format!(cdn!("/emojis/{}.png"), self.id) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for EmojiIdentifier {
    type Err = ();

    fn from_str(s: &str) -> StdResult<Self, ()> { utils::parse_emoji(s).ok_or_else(|| ()) }
}

/// A component that was affected during a service incident.
///
/// This is pulled from the Discord status page.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AffectedComponent {
    pub name: String,
}

/// An incident retrieved from the Discord status page.
///
/// This is not necessarily a representation of an ongoing incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Incident {
    pub created_at: String,
    pub id: String,
    pub impact: String,
    pub incident_updates: Vec<IncidentUpdate>,
    pub monitoring_at: Option<String>,
    pub name: String,
    pub page_id: String,
    pub resolved_at: Option<String>,
    pub short_link: String,
    pub status: String,
    pub updated_at: String,
}

/// An update to an incident from the Discord status page.
///
/// This will typically state what new information has been discovered about an
/// incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IncidentUpdate {
    pub affected_components: Vec<AffectedComponent>,
    pub body: String,
    pub created_at: String,
    pub display_at: String,
    pub id: String,
    pub incident_id: String,
    pub status: IncidentStatus,
    pub updated_at: String,
}

/// The type of status update during a service incident.
#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum IncidentStatus {
    Identified,
    Investigating,
    Monitoring,
    Postmortem,
    Resolved,
}

/// A Discord status maintenance message. This can be either for active
/// maintenances or for scheduled maintenances.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Maintenance {
    pub description: String,
    pub id: String,
    pub name: String,
    pub start: String,
    pub stop: String,
}
<commit_msg>Implement Mentionable for Group<commit_after>//! Miscellaneous helper traits, enums, and structs for models.

use super::prelude::*;
use internal::RwLockExt;

#[cfg(all(feature = "model", feature = "utils"))]
use std::error::Error as StdError;
#[cfg(all(feature = "model", feature = "utils"))]
use std::result::Result as StdResult;
#[cfg(all(feature = "model", feature = "utils"))]
use std::str::FromStr;
#[cfg(all(feature = "model", feature = "utils"))]
use std::fmt;
#[cfg(all(feature = "model", any(feature = "cache", feature = "utils")))]
use utils;

/// Allows something - such as a channel or role - to be mentioned in a message.
pub trait Mentionable {
    /// Creates a mentionable string, that will be able to notify and/or create
    /// a link to the item.
    fn mention(&self) -> String;
}

impl Mentionable for ChannelId {
    fn mention(&self) -> String { format!("<#{}>", self.0) }
}

impl Mentionable for Channel {
    fn mention(&self) -> String {
        match *self {
            Channel::Guild(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
            Channel::Private(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
            Channel::Group(ref x) => format!("<#{}>", x.with(|x| x.channel_id.0)),
            Channel::Category(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
        }
    }
}

impl Mentionable for ChannelCategory {
    fn mention(&self) -> String {
        format!("#{}", self.name)
    }
}

impl Mentionable for CurrentUser {
    fn mention(&self) -> String {
        format!("<@{}>", self.id.0)
    }
}

impl Mentionable for Emoji {
    fn mention(&self) -> String { format!("<:{}:{}>", self.name, self.id.0) }
}

impl Mentionable for Group {
    fn mention(&self) -> String {
        format!("<#{}", self.channel_id.0)
    }
}

impl Mentionable for Member {
    fn mention(&self) -> String { format!("<@{}>", self.user.with(|u| u.id.0)) }
}

impl Mentionable for PrivateChannel {
    fn mention(&self) -> String {
        format!("<#{}>", self.id.0)
    }
}

impl Mentionable for RoleId {
    fn mention(&self) -> String { format!("<@&{}>", self.0) }
}

impl Mentionable for Role {
    fn mention(&self) -> String { format!("<@&{}>", self.id.0) }
}

impl Mentionable for UserId {
    fn mention(&self) -> String { format!("<@{}>", self.0) }
}

impl Mentionable for User {
    fn mention(&self) -> String { format!("<@{}>", self.id.0) }
}

impl Mentionable for GuildChannel {
    fn mention(&self) -> String { format!("<#{}>", self.id.0) }
}

#[cfg(all(feature = "model", feature = "utils"))]
#[derive(Debug)]
pub enum UserParseError {
    InvalidUsername,
    Rest(Box<Error>),
}

#[cfg(all(feature = "model", feature = "utils"))]
impl fmt::Display for UserParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl StdError for UserParseError {
    fn description(&self) -> &str {
        use self::UserParseError::*;

        match *self {
            InvalidUsername => "invalid username",
            Rest(_) => "could not fetch",
        }
    }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for User {
    type Err = UserParseError;

    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
        match utils::parse_username(s) {
            Some(x) => UserId(x as u64)
                .get()
                .map_err(|e| UserParseError::Rest(Box::new(e))),
            _ => Err(UserParseError::InvalidUsername),
        }
    }
}

macro_rules! impl_from_str {
    (id: $($id:tt, $err:ident;)*) => {
        $(
            #[cfg(all(feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                InvalidFormat,
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        InvalidFormat => "invalid id format",
                    }
                }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl FromStr for $id {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    Ok(match utils::parse_username(s) {
                        Some(id) => $id(id),
                        None => s.parse::<u64>().map($id).map_err(|_| $err::InvalidFormat)?,
                    })
                }
            }
        )*
    };

    (struct: $($struct:ty, $id:tt, $err:ident, $invalid_variant:tt, $parse_fn:ident, $desc:expr;)*) => {
        $(
            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                NotPresentInCache,
                $invalid_variant,
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        NotPresentInCache => "not present in cache",
                        $invalid_variant => $desc,
                    }
                }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl FromStr for $struct {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    match utils::$parse_fn(s) {
                        Some(x) => match $id(x).find() {
                            Some(user) => Ok(user),
                            _ => Err($err::NotPresentInCache),
                        },
                        _ => Err($err::$invalid_variant),
                    }
                }
            }
        )*
    };
}

impl_from_str! { id:
    UserId, UserIdParseError;
    RoleId, RoleIdParseError;
    ChannelId, ChannelIdParseError;
}

impl_from_str! { struct:
    Channel, ChannelId, ChannelParseError, InvalidChannel, parse_channel, "invalid channel";
    Role, RoleId, RoleParseError, InvalidRole, parse_role, "invalid role";
}

/// A version of an emoji used only when solely the Id and name are known.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct EmojiIdentifier {
    /// The Id of the emoji.
    pub id: EmojiId,
    /// The name of the emoji. It must be at least 2 characters long and can
    /// only contain alphanumeric characters and underscores.
    pub name: String,
}

#[cfg(all(feature = "model", feature = "utils"))]
impl EmojiIdentifier {
    /// Generates a URL to the emoji's image.
    #[inline]
    pub fn url(&self) -> String { format!(cdn!("/emojis/{}.png"), self.id) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for EmojiIdentifier {
    type Err = ();

    fn from_str(s: &str) -> StdResult<Self, ()> { utils::parse_emoji(s).ok_or_else(|| ()) }
}

/// A component that was affected during a service incident.
///
/// This is pulled from the Discord status page.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AffectedComponent {
    pub name: String,
}

/// An incident retrieved from the Discord status page.
///
/// This is not necessarily a representation of an ongoing incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Incident {
    pub created_at: String,
    pub id: String,
    pub impact: String,
    pub incident_updates: Vec<IncidentUpdate>,
    pub monitoring_at: Option<String>,
    pub name: String,
    pub page_id: String,
    pub resolved_at: Option<String>,
    pub short_link: String,
    pub status: String,
    pub updated_at: String,
}

/// An update to an incident from the Discord status page.
///
/// This will typically state what new information has been discovered about an
/// incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IncidentUpdate {
    pub affected_components: Vec<AffectedComponent>,
    pub body: String,
    pub created_at: String,
    pub display_at: String,
    pub id: String,
    pub incident_id: String,
    pub status: IncidentStatus,
    pub updated_at: String,
}

/// The type of status update during a service incident.
#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum IncidentStatus {
    Identified,
    Investigating,
    Monitoring,
    Postmortem,
    Resolved,
}

/// A Discord status maintenance message. This can be either for active
/// maintenances or for scheduled maintenances.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Maintenance {
    pub description: String,
    pub id: String,
    pub name: String,
    pub start: String,
    pub stop: String,
}
<|endoftext|>


<commit_before>// This is a pretty complete functional test of the library.
// Feel free to read through these tests and their accompanying
// .gpx files to see how usage might be.

#[macro_use]
extern crate assert_approx_eq;

extern crate chrono;
extern crate geo;
extern crate geo_types;
extern crate gpx;

#[cfg(test)]
mod tests {
    use chrono::prelude::*;
    use geo::algorithm::haversine_distance::HaversineDistance;
    use geo::euclidean_length::EuclideanLength;
    use geo_types::{Geometry, Point};
    use std::fs::File;
    use std::io::BufReader;

    use gpx::read;
    use gpx::Fix;

    #[test]
    fn gpx_reader_read_test_badxml() {
        // Should fail with badly formatted XML.
        let file = File::open("tests/fixtures/badcharacter.xml").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);

        assert!(result.is_err());
    }

    #[test]
    fn gpx_reader_read_test_wikipedia() {
        // Should not give an error, and should have all the correct data.
        let file = File::open("tests/fixtures/wikipedia_example.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());

        let result = result.unwrap();

        // Check the metadata, of course; here it has a time.
        let metadata = result.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2009, 10, 17).and_hms(22, 58, 43)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.href, "http://www.garmin.com");
        assert_eq!(link.text, Some(String::from("Garmin International")));

        // There should just be one track, "example gpx document".
        assert_eq!(result.tracks.len(), 1);
        let track = &result.tracks[0];

        assert_eq!(track.name, Some(String::from("Example GPX Document")));

        // Each point has its own information; test elevation.
        assert_eq!(track.segments.len(), 1);
        let points = &track.segments[0].points;

        assert_eq!(points.len(), 3);
        assert_eq!(points[0].elevation, Some(4.46));
        assert_eq!(points[1].elevation, Some(4.94));
        assert_eq!(points[2].elevation, Some(6.87));
    }

    #[test]
    fn gpx_reader_read_test_gpsies() {
        // Should not give an error, and should have all the correct data.
        let file = File::open("tests/fixtures/gpsies_example.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        match result {
            Ok(_) => {}
            Err(ref e) => {
                println!("{:?}", e);
            }
        }
        assert!(result.is_ok());

        let result = result.unwrap();

        // Check the metadata, of course; here it has a time.
        let metadata = result.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2019, 09, 11).and_hms(17, 08, 31)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.href, "https://www.gpsies.com/");
        assert_eq!(link.text, Some(String::from("Innrunde on AllTrails")));

        // There should just be one track, "example gpx document".
        assert_eq!(result.tracks.len(), 1);
        let track = &result.tracks[0];

        assert_eq!(track.name, Some(String::from("Innrunde on AllTrails")));

        let link = &result.tracks[0].links[0];

        assert_eq!(link.href, "https://www.gpsies.com/map.do");

        // Each point has its own information; test elevation.
        assert_eq!(track.segments.len(), 1);
        let points = &track.segments[0].points;

        assert_eq!(points[0].elevation, Some(305.0));
        assert_eq!(points[1].elevation, Some(304.0));
        assert_eq!(points[2].elevation, Some(305.0));
    }

    #[test]
    fn gpx_reader_read_test_garmin_activity() {
        let file = File::open("tests/fixtures/garmin-activity.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2017, 7, 29).and_hms_micro(14, 46, 35, 000_000)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.text, Some(String::from("Garmin Connect")));
        assert_eq!(link.href, String::from("connect.garmin.com"));

        // Check the main track.
        assert_eq!(res.tracks.len(), 1);
        let track = &res.tracks[0];

        assert_eq!(track.name, Some(String::from("casual stroll")));
        assert_eq!(track._type, Some(String::from("running")));

        // Check some Geo operations on the track.
        let mls = track.multilinestring();
        assert_approx_eq!(mls.euclidean_length(), 0.12704048);

        // Get the first track segment.
        assert_eq!(track.segments.len(), 1);
        let segment = &track.segments[0];

        // Test for every single point in the file.
        for point in segment.points.iter() {
            // Elevation is between 90 and 220.
            let elevation = point.elevation.unwrap();
            assert!(elevation > 90. && elevation < 220.);

            // All the points should be close (5000 units, its closer than you think).
            let reference_point = Point::new(-121.97, 37.24);
            let distance = reference_point.haversine_distance(&point.point());
            assert!(distance < 5000.);

            // Time is between a day before and after.
            let time = point.time.unwrap();
            assert!(time > Utc.ymd(2017, 7, 28).and_hms_micro(0, 0, 0, 000_000));
            assert!(time < Utc.ymd(2017, 7, 30).and_hms_micro(0, 0, 0, 000_000));

            // Should coerce to Point.
            let geo: Geometry<f64> = point.clone().into();
            match geo {
                Geometry::Point(_) => {} // ok
                _ => panic!("point.into() gave bad geometry"),
            }

            // It's missing almost all fields, actually.
            assert!(point.name.is_none());
            assert!(point.comment.is_none());
            assert!(point.description.is_none());
            assert!(point.source.is_none());
            assert!(point.symbol.is_none());
            assert!(point._type.is_none());
            assert_eq!(point.links.len(), 0);
        }
    }

    #[test]
    fn gpx_reader_read_test_lovers_lane() {
        let file = File::open("tests/fixtures/ecology-trail-and-lovers-lane-loop.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();

        assert_eq!(metadata.name, Some(String::from("Trail Planner Map")));
        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(
            link.text,
            Some(String::from("Trail Planner Map on AllTrails"))
        );
        assert_eq!(link.href, String::from("https://www.gpsies.com/"));

        // Check the main track.
        let route = &res.route;
        assert_eq!(
            route.name,
            Some(String::from("Trail Planner Map on AllTrails"))
        );
        assert_eq!(route.points.len(), 139);

        // Test for every single point in the file.
        for point in route.points.iter() {
            // Elevation is between 90 and 220.
            let elevation = point.elevation.unwrap();
            assert!(elevation > 15. && elevation < 100.);

            // Should coerce to Point.
            let geo: Geometry<f64> = point.clone().into();
            match geo {
                Geometry::Point(_) => {} // ok
                _ => panic!("point.into() gave bad geometry"),
            }

            // It's missing almost all fields, actually.
            assert!(point.name.is_none());
            assert!(point.comment.is_none());
            assert!(point.description.is_none());
            assert!(point.source.is_none());
            assert!(point.symbol.is_none());
            assert!(point._type.is_none());
            assert_eq!(point.links.len(), 0);
        }
    }

    #[test]
    fn gpx_reader_read_test_with_accuracy() {
        let file = File::open("tests/fixtures/with_accuracy.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();
        assert_eq!(metadata.name.unwrap(), "20170412_CARDIO.gpx");

        assert_eq!(metadata.links.len(), 0);

        // Check the main track.
        assert_eq!(res.tracks.len(), 1);
        let track = &res.tracks[0];

        assert_eq!(track.name, Some(String::from("Cycling")));

        // Get the first track segment.
        assert_eq!(track.segments.len(), 1);
        let segment = &track.segments[0];

        // Get the first point
        assert_eq!(segment.points.len(), 3);
        let points = &segment.points;

        assert_eq!(points[0].fix, Some(Fix::DGPS));
        assert_eq!(points[0].sat.unwrap(), 4);
        assert_eq!(points[0].hdop.unwrap(), 5.);
        assert_eq!(points[0].vdop.unwrap(), 6.2);
        assert_eq!(points[0].pdop.unwrap(), 728.);
        assert_eq!(points[0].age.unwrap(), 1.);
        assert_eq!(points[0].dgpsid.unwrap(), 3);

        assert_eq!(points[1].fix, Some(Fix::ThreeDimensional));
        assert_eq!(points[1].sat.unwrap(), 5);
        assert_eq!(points[1].hdop.unwrap(), 3.6);
        assert_eq!(points[1].vdop.unwrap(), 5.);
        assert_eq!(points[1].pdop.unwrap(), 619.1);
        assert_eq!(points[1].age.unwrap(), 2.01);
        assert_eq!(points[1].dgpsid.unwrap(), 4);

        assert_eq!(
            points[2].fix,
            Some(Fix::Other("something_not_in_the_spec".to_string()))
        );
    }
}
<commit_msg>Fix test comment<commit_after>// This is a pretty complete functional test of the library.
// Feel free to read through these tests and their accompanying
// .gpx files to see how usage might be.

#[macro_use]
extern crate assert_approx_eq;

extern crate chrono;
extern crate geo;
extern crate geo_types;
extern crate gpx;

#[cfg(test)]
mod tests {
    use chrono::prelude::*;
    use geo::algorithm::haversine_distance::HaversineDistance;
    use geo::euclidean_length::EuclideanLength;
    use geo_types::{Geometry, Point};
    use std::fs::File;
    use std::io::BufReader;

    use gpx::read;
    use gpx::Fix;

    #[test]
    fn gpx_reader_read_test_badxml() {
        // Should fail with badly formatted XML.
        let file = File::open("tests/fixtures/badcharacter.xml").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);

        assert!(result.is_err());
    }

    #[test]
    fn gpx_reader_read_test_wikipedia() {
        // Should not give an error, and should have all the correct data.
        let file = File::open("tests/fixtures/wikipedia_example.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());

        let result = result.unwrap();

        // Check the metadata, of course; here it has a time.
        let metadata = result.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2009, 10, 17).and_hms(22, 58, 43)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.href, "http://www.garmin.com");
        assert_eq!(link.text, Some(String::from("Garmin International")));

        // There should just be one track, "example gpx document".
        assert_eq!(result.tracks.len(), 1);
        let track = &result.tracks[0];

        assert_eq!(track.name, Some(String::from("Example GPX Document")));

        // Each point has its own information; test elevation.
        assert_eq!(track.segments.len(), 1);
        let points = &track.segments[0].points;

        assert_eq!(points.len(), 3);
        assert_eq!(points[0].elevation, Some(4.46));
        assert_eq!(points[1].elevation, Some(4.94));
        assert_eq!(points[2].elevation, Some(6.87));
    }

    #[test]
    fn gpx_reader_read_test_gpsies() {
        // Should not give an error, and should have all the correct data.
        let file = File::open("tests/fixtures/gpsies_example.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        match result {
            Ok(_) => {}
            Err(ref e) => {
                println!("{:?}", e);
            }
        }
        assert!(result.is_ok());

        let result = result.unwrap();

        // Check the metadata, of course; here it has a time.
        let metadata = result.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2019, 09, 11).and_hms(17, 08, 31)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.href, "https://www.gpsies.com/");
        assert_eq!(link.text, Some(String::from("Innrunde on AllTrails")));

        // There should just be one track, "example gpx document".
        assert_eq!(result.tracks.len(), 1);
        let track = &result.tracks[0];

        assert_eq!(track.name, Some(String::from("Innrunde on AllTrails")));

        let link = &result.tracks[0].links[0];

        assert_eq!(link.href, "https://www.gpsies.com/map.do");

        // Each point has its own information; test elevation.
        assert_eq!(track.segments.len(), 1);
        let points = &track.segments[0].points;

        assert_eq!(points[0].elevation, Some(305.0));
        assert_eq!(points[1].elevation, Some(304.0));
        assert_eq!(points[2].elevation, Some(305.0));
    }

    #[test]
    fn gpx_reader_read_test_garmin_activity() {
        let file = File::open("tests/fixtures/garmin-activity.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();
        assert_eq!(
            metadata.time.unwrap(),
            Utc.ymd(2017, 7, 29).and_hms_micro(14, 46, 35, 000_000)
        );

        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(link.text, Some(String::from("Garmin Connect")));
        assert_eq!(link.href, String::from("connect.garmin.com"));

        // Check the main track.
        assert_eq!(res.tracks.len(), 1);
        let track = &res.tracks[0];

        assert_eq!(track.name, Some(String::from("casual stroll")));
        assert_eq!(track._type, Some(String::from("running")));

        // Check some Geo operations on the track.
        let mls = track.multilinestring();
        assert_approx_eq!(mls.euclidean_length(), 0.12704048);

        // Get the first track segment.
        assert_eq!(track.segments.len(), 1);
        let segment = &track.segments[0];

        // Test for every single point in the file.
        for point in segment.points.iter() {
            // Elevation is between 90 and 220.
            let elevation = point.elevation.unwrap();
            assert!(elevation > 90. && elevation < 220.);

            // All the points should be close (5000 units, its closer than you think).
            let reference_point = Point::new(-121.97, 37.24);
            let distance = reference_point.haversine_distance(&point.point());
            assert!(distance < 5000.);

            // Time is between a day before and after.
            let time = point.time.unwrap();
            assert!(time > Utc.ymd(2017, 7, 28).and_hms_micro(0, 0, 0, 000_000));
            assert!(time < Utc.ymd(2017, 7, 30).and_hms_micro(0, 0, 0, 000_000));

            // Should coerce to Point.
            let geo: Geometry<f64> = point.clone().into();
            match geo {
                Geometry::Point(_) => {} // ok
                _ => panic!("point.into() gave bad geometry"),
            }

            // It's missing almost all fields, actually.
            assert!(point.name.is_none());
            assert!(point.comment.is_none());
            assert!(point.description.is_none());
            assert!(point.source.is_none());
            assert!(point.symbol.is_none());
            assert!(point._type.is_none());
            assert_eq!(point.links.len(), 0);
        }
    }

    #[test]
    fn gpx_reader_read_test_lovers_lane() {
        let file = File::open("tests/fixtures/ecology-trail-and-lovers-lane-loop.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();

        assert_eq!(metadata.name, Some(String::from("Trail Planner Map")));
        assert_eq!(metadata.links.len(), 1);
        let link = &metadata.links[0];
        assert_eq!(
            link.text,
            Some(String::from("Trail Planner Map on AllTrails"))
        );
        assert_eq!(link.href, String::from("https://www.gpsies.com/"));

        // Check the main track.
        let route = &res.route;
        assert_eq!(
            route.name,
            Some(String::from("Trail Planner Map on AllTrails"))
        );
        assert_eq!(route.points.len(), 139);

        // Test for every single point in the file.
        for point in route.points.iter() {
            // Elevation is between 15 and 100
            let elevation = point.elevation.unwrap();
            assert!(elevation > 15. && elevation < 100.);

            // Should coerce to Point.
            let geo: Geometry<f64> = point.clone().into();
            match geo {
                Geometry::Point(_) => {} // ok
                _ => panic!("point.into() gave bad geometry"),
            }

            // It's missing almost all fields, actually.
            assert!(point.name.is_none());
            assert!(point.comment.is_none());
            assert!(point.description.is_none());
            assert!(point.source.is_none());
            assert!(point.symbol.is_none());
            assert!(point._type.is_none());
            assert_eq!(point.links.len(), 0);
        }
    }

    #[test]
    fn gpx_reader_read_test_with_accuracy() {
        let file = File::open("tests/fixtures/with_accuracy.gpx").unwrap();
        let reader = BufReader::new(file);

        let result = read(reader);
        assert!(result.is_ok());
        let res = result.unwrap();

        // Check the info on the metadata.
        let metadata = res.metadata.unwrap();
        assert_eq!(metadata.name.unwrap(), "20170412_CARDIO.gpx");

        assert_eq!(metadata.links.len(), 0);

        // Check the main track.
        assert_eq!(res.tracks.len(), 1);
        let track = &res.tracks[0];

        assert_eq!(track.name, Some(String::from("Cycling")));

        // Get the first track segment.
        assert_eq!(track.segments.len(), 1);
        let segment = &track.segments[0];

        // Get the first point
        assert_eq!(segment.points.len(), 3);
        let points = &segment.points;

        assert_eq!(points[0].fix, Some(Fix::DGPS));
        assert_eq!(points[0].sat.unwrap(), 4);
        assert_eq!(points[0].hdop.unwrap(), 5.);
        assert_eq!(points[0].vdop.unwrap(), 6.2);
        assert_eq!(points[0].pdop.unwrap(), 728.);
        assert_eq!(points[0].age.unwrap(), 1.);
        assert_eq!(points[0].dgpsid.unwrap(), 3);

        assert_eq!(points[1].fix, Some(Fix::ThreeDimensional));
        assert_eq!(points[1].sat.unwrap(), 5);
        assert_eq!(points[1].hdop.unwrap(), 3.6);
        assert_eq!(points[1].vdop.unwrap(), 5.);
        assert_eq!(points[1].pdop.unwrap(), 619.1);
        assert_eq!(points[1].age.unwrap(), 2.01);
        assert_eq!(points[1].dgpsid.unwrap(), 4);

        assert_eq!(
            points[2].fix,
            Some(Fix::Other("something_not_in_the_spec".to_string()))
        );
    }
}
<|endoftext|>


<commit_before>/*!
`crypto_stream_xsalsa20`, a particular cipher specified in
[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html), Section 7.
This cipher is conjectured to meet the standard notion of
unpredictability.
*/
use libc::{c_ulonglong, c_int};
use std::intrinsics::volatile_set_memory;
use randombytes::randombytes_into;

stream_module!(crypto_stream_xsalsa20, crypto_stream_xsalsa20_xor, 32, 24)
#[test]
fn test_vector_1() {
    // corresponding to tests/stream.c and tests/stream5.cpp from NaCl
    use crypto::hash::sha256::{hash, Digest};
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let output = stream(4194304, &nonce, &firstkey);
    let digest_expected = [0x66, 0x2b, 0x9d, 0x0e, 0x34, 0x63, 0x02, 0x91,
                           0x56, 0x06, 0x9b, 0x12, 0xf9, 0x18, 0x69, 0x1a,
                           0x98, 0xf7, 0xdf, 0xb2, 0xca, 0x03, 0x93, 0xc9,
                           0x6b, 0xbf, 0xc6, 0xb1, 0xfb, 0xd6, 0x30, 0xa2];
    let Digest(digest) = hash(output.as_slice());
    assert!(digest == digest_expected);

}

#[test]
fn test_vector_2() {
    // corresponding to tests/stream3.c and tests/stream7.cpp from NaCl
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let rs = stream(32, &nonce, &firstkey);
    let rs_expected = vec![0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91
                       ,0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25
                       ,0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65
                       ,0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80];
    assert!(rs == rs_expected);
}

#[test]
fn test_vector_3() {
    // corresponding to tests/stream4.c and tests/stream8.cpp from NaCl
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let m = &[    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             , 0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5
             , 0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b
             , 0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4
             , 0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc
             , 0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a
             , 0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29
             , 0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4
             , 0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31
             , 0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d
             , 0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57
             , 0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a
             , 0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde
             , 0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd
             , 0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52
             , 0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40
             , 0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64
             , 0x5e,0x07,0x05];

    let c = stream_xor(m, &nonce, &firstkey);
    let c_expected = [0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73
                     ,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce
                     ,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4
                     ,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a
                     ,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b
                     ,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72
                     ,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2
                     ,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38
                     ,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a
                     ,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae
                     ,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea
                     ,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda
                     ,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde
                     ,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3
                     ,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6
                     ,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74
                     ,0xe3,0x55,0xa5];
    assert!(c.slice_from(32) == &c_expected);
}
<commit_msg>Fix compilation failure on latest rust-nightly<commit_after>/*!
`crypto_stream_xsalsa20`, a particular cipher specified in
[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html), Section 7.
This cipher is conjectured to meet the standard notion of
unpredictability.
*/
use libc::{c_ulonglong, c_int};
use std::intrinsics::volatile_set_memory;
use randombytes::randombytes_into;

stream_module!(crypto_stream_xsalsa20, crypto_stream_xsalsa20_xor, 32, 24)
#[test]
fn test_vector_1() {
    // corresponding to tests/stream.c and tests/stream5.cpp from NaCl
    use crypto::hash::sha256::{hash, Digest};
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let output = stream(4194304, &nonce, &firstkey);
    let digest_expected = [0x66, 0x2b, 0x9d, 0x0e, 0x34, 0x63, 0x02, 0x91,
                           0x56, 0x06, 0x9b, 0x12, 0xf9, 0x18, 0x69, 0x1a,
                           0x98, 0xf7, 0xdf, 0xb2, 0xca, 0x03, 0x93, 0xc9,
                           0x6b, 0xbf, 0xc6, 0xb1, 0xfb, 0xd6, 0x30, 0xa2];
    let Digest(digest) = hash(output.as_slice());
    assert!(digest == digest_expected);

}

#[test]
fn test_vector_2() {
    // corresponding to tests/stream3.c and tests/stream7.cpp from NaCl
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let rs = stream(32, &nonce, &firstkey);
    let rs_expected = vec![0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91
                       ,0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25
                       ,0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65
                       ,0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80];
    assert!(rs == rs_expected);
}

#[test]
fn test_vector_3() {
    // corresponding to tests/stream4.c and tests/stream8.cpp from NaCl
    let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4
                       ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7
                       ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2
                       ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]);
    let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73
                      ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6
                      ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]);
    let m = &[    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             ,    0,   0,   0,   0,   0,   0,   0,   0
             , 0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5
             , 0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b
             , 0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4
             , 0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc
             , 0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a
             , 0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29
             , 0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4
             , 0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31
             , 0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d
             , 0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57
             , 0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a
             , 0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde
             , 0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd
             , 0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52
             , 0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40
             , 0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64
             , 0x5e,0x07,0x05];

    let c = stream_xor(m, &nonce, &firstkey);
    let c_expected = [0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73
                     ,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce
                     ,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4
                     ,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a
                     ,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b
                     ,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72
                     ,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2
                     ,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38
                     ,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a
                     ,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae
                     ,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea
                     ,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda
                     ,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde
                     ,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3
                     ,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6
                     ,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74
                     ,0xe3,0x55,0xa5];
    assert!(c.slice_from(32) == c_expected.as_slice());
}
<|endoftext|>


<commit_before>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag;
use libp2p_core::PeerId;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
    duration: u32,
    album: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            Some("audio") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            // FIXME: video in taglib holds also oga which are audio indeed ...
            Some("text") | Some("application") | Some("image") | Some("video") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        Tag::read_from_path(cb.to_str().unwrap())
            .and_then(|tag| {
                let artist = tag.artist().unwrap_or("");
                let title = tag.title().unwrap_or("");
                let duration = tag.duration().unwrap_or(0);
                let album = tag.album().unwrap_or("");

                self.stats.files.analyzed += 1;
                file_stats.analyzed += 1;

                // artist + song name is key for bktree
                let key = [artist, title].join(" ");

                let ref mut locked_bktree = data.lock().unwrap().bk_tree;
                let (vec_exact_match, vec_similarities) = locked_bktree.find(&key, TOLERANCE);
                if !vec_similarities.is_empty() {
                    trace!("close: {:?} to {:?},", &vec_similarities, &key);
                }
                // if exact match, don't insert!!
                if vec_exact_match.is_empty() {
                    // todo: also decide when to not add and insert then
                    locked_bktree.insert(
                        key.clone(),
                        Box::new(AudioInfo {
                            duration,
                            album: album.to_string(),
                        }),
                    );
                } else {
                    // exact match with certain AudioInfo
                    trace!(
                        "for {:?}, {} exact matches found!",
                        &key,
                        vec_exact_match.len()
                    );
                    for audio_info in vec_exact_match {
                        let time_distance = audio_info.duration as i32 - duration as i32;
                        if time_distance.abs() > 0 {
                            trace!(
                                "same: but time differs {} seconds with album name old '{}' and new: '{}'!",
                                time_distance,
                                audio_info.album,
                                album
                            );
                        }
                    }
                }
                Ok(())
            })
            .or_else(|_| {
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }
}
<commit_msg>[collection] watch memory, added possible info for decision, and tried a few of them<commit_after>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag;
use libp2p_core::PeerId;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
    duration: u32,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            Some("audio") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            // FIXME: video in taglib holds also oga which are audio indeed ...
            Some("text") | Some("application") | Some("image") | Some("video") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        Tag::read_from_path(cb.to_str().unwrap())
            .and_then(|tag| {
                let artist = tag.artist().unwrap_or("");
                let title = tag.title().unwrap_or("");
                let duration = tag.duration().unwrap_or(0);

                // audio book genre set is a strong indicator
                let _genre = tag.genre().unwrap_or("");
                //
                let album = tag.album().unwrap_or("");
                let _album_artist = tag.album_artist().unwrap_or("");
                // many discs and total discs is a strong indicator
                let _disc = tag.disc().unwrap_or(0);
                let _total_discs = tag.total_discs().unwrap_or(0);
                //
                // having a good path pattern is a strong indicator: cb.to_str().unwrap()

                let _total_tracks = tag.total_tracks().unwrap_or(0);
                let _track = tag.track().unwrap_or(0);
                let _year = tag.year().unwrap_or(0);

                self.stats.files.analyzed += 1;
                file_stats.analyzed += 1;

                let mut has_enough_information = true;

                // artist + song name is key for bktree
                if artist.is_empty() && title.is_empty() {
                    has_enough_information = false;
                }

                if has_enough_information {
                    let key = [artist, title].join(" ");

                    // a) filter numbers / remove
                    // b) use album name / substract it?

                    // todo: bktree and levenshtein distance, use cosine similarity instead
                    let ref mut locked_container = data.lock().unwrap();
                    let (vec_exact_match, vec_similarities) = locked_container.bk_tree.find(&key, TOLERANCE);
                    if !vec_similarities.is_empty() {
                        trace!("close: {:?} to {:?},", &vec_similarities, &key);
                    }
                    // if exact match, don't insert!!
                    if vec_exact_match.is_empty() {
                        // todo: also decide when to not add and insert then

                        let key = key.clone();
                        let value = Box::new(AudioInfo {
                            duration,
                            album: album.to_string(),
                            path: cb.to_str().unwrap().to_string()
                        });
                        let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                        locked_container.bk_tree.insert(
                            key,
                            value,
                        );
                        self.stats.memory += mem_size as u64;
                    } else {
                        // exact match with certain AudioInfo
                        trace!(
                            "for {:?}, {} exact matches found!",
                            &key,
                            vec_exact_match.len()
                        );
                        for audio_info in vec_exact_match {
                            let time_distance = audio_info.duration as i32 - duration as i32;
                            if time_distance.abs() > 0 {
                                trace!(
                                    "same: but time differs {} seconds with album name old '{}' and new: '{}'!",
                                    time_distance,
                                    audio_info.album,
                                    album
                                );
                            }
                        }
                    }
                }
                Ok(())
            })
            .or_else(|_| {
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }
}
<|endoftext|>


<commit_before>use std::iter::Repeat;

fn replicate<A:Clone>(x:A, l:uint) -> Vec<A> {
    FromIterator::from_iter(Repeat::new(x).take(l))
}

fn pad_right(c:char, l:uint, mut str:String) -> String {
    let str_len = str.len();
    if l > str_len {
        let padding = replicate(String::from_chars([c]), l - str_len).concat();
        // let mut res = str.clone();
        str.push_str(padding.as_slice());
        str
    } else {
        str
    }
}

fn pad_left(c:char, l:uint, str:String) -> String {
    let str_len = str.len();
    if l > str_len {
      let mut res = replicate(String::from_chars([c]), l - str_len).concat();
      res.push_str(str.as_slice());
      res
    } else {
        str
    }
}

pub fn nl_space(i:uint) -> String {
    pad_right(' ', i + String::from_str("\n").len(), String::from_str("\n"))
}

pub fn spaces(i:uint) -> String {
    pad_left(' ', i, String::from_str(""))
}
<commit_msg>Simplify replicate; inline<commit_after>fn pad_right(c:char, l:uint, mut str:String) -> String {
    let str_len = str.len();
    if l > str_len {
        let padding =
            Vec::from_elem(l - str_len, String::from_chars([c]))
                .concat();
        // let mut res = str.clone();
        str.push_str(padding.as_slice());
        str
    } else {
        str
    }
}

fn pad_left(c:char, l:uint, str:String) -> String {
    let str_len = str.len();
    if l > str_len {
        let mut res =
            Vec::from_elem(l - str_len, String::from_chars([c]))
                .concat();
        res.push_str(str.as_slice());
        res
    } else {
        str
    }
}

pub fn nl_space(i:uint) -> String {
    pad_right(' ', i + String::from_str("\n").len(), String::from_str("\n"))
}

pub fn spaces(i:uint) -> String {
    pad_left(' ', i, String::from_str(""))
}
<|endoftext|>


<commit_before>//! Parser module is responsible for parsing FITS files.

named!(fits<&[u8], (Vec<&[u8]>, Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], Vec<&[u8]> >, many_m_n!(2, 2, take!(2880)));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(header.len(), 2);
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<commit_msg>Parse END and BLANK headers<commit_after>//! Parser module is responsible for parsing FITS files.

named!(fits<&[u8], (Vec<&[u8]>, Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], Vec<&[u8]> >, many_m_n!(2, 2, take!(2880)));

named!(end_header<&[u8],(&[u8], Vec<&[u8]>)>, pair!(tag!("END"), count!(tag!(" "), 77)));

named!(blank_header<&[u8],Vec<&[u8]> >, count!(tag!(" "), 80));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits, end_header, blank_header};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(header.len(), 2);
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_header_should_parse_an_END_header(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_header_should_parse_a_BLANK_header(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<|endoftext|>


<commit_before>use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

use world::World;

static OFFSET: u32 = 100;

struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    cell_size: u32,
    world: World
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const BG_GOLOR: [f32; 4] = [0.150, 0.164, 0.176, 1.0];
        const CELL_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

        let cells = self.world.get_generation();
        let cell_size = self.cell_size as f64;
        let offset = OFFSET as f64;

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

            for cell in cells {
                let (x, y): (isize, isize) = *cell;
                let x = x as f64;
                let y = y as f64;
                Rectangle::new(CELL_COLOR)
                .draw([(x * cell_size) + offset, (y * cell_size) + offset, cell_size, cell_size],
                      &c.draw_state,
                      c.transform,
                      gl
                );
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        self.world.evolve();
    }
}

pub fn start_piston_app(world: World, period: u64) {
    let cell_size: u32 = 5;
    let opengl = OpenGL::_3_2;
    let (width, height) = world.get_size();
    // Create an Glutin window.
    let window = Window::new(
        opengl,
        WindowSettings::new(
            "G-O-L-R",
            [width as u32 * cell_size + 2 * OFFSET, height as u32 * cell_size + 2 * OFFSET]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        cell_size: cell_size,
        world: world
    };

    for e in window.events().max_fps(30).ups(period) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}
<commit_msg>Get rid of warning<commit_after>use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

use world::World;

static OFFSET: u32 = 100;

struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    cell_size: u32,
    world: World
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const BG_GOLOR: [f32; 4] = [0.150, 0.164, 0.176, 1.0];
        const CELL_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

        let cells = self.world.get_generation();
        let cell_size = self.cell_size as f64;
        let offset = OFFSET as f64;

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

            for cell in cells {
                let (x, y): (isize, isize) = *cell;
                let x = x as f64;
                let y = y as f64;
                Rectangle::new(CELL_COLOR)
                .draw([(x * cell_size) + offset, (y * cell_size) + offset, cell_size, cell_size],
                      &c.draw_state,
                      c.transform,
                      gl
                );
            }
        });
    }

    fn update(&mut self, _: &UpdateArgs) {
        self.world.evolve();
    }
}

pub fn start_piston_app(world: World, period: u64) {
    let cell_size: u32 = 5;
    let opengl = OpenGL::_3_2;
    let (width, height) = world.get_size();
    // Create an Glutin window.
    let window = Window::new(
        opengl,
        WindowSettings::new(
            "G-O-L-R",
            [width as u32 * cell_size + 2 * OFFSET, height as u32 * cell_size + 2 * OFFSET]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        cell_size: cell_size,
        world: world
    };

    for e in window.events().max_fps(30).ups(period) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}
<|endoftext|>