<commit_before>use std::{fmt, fs, io};
use std::io::Read;
use syntax::{ast, codemap, ptr};
use syntax::parse::token;
use toml;

/// Get the configuration file from arguments.
pub fn file(args: &[ptr::P<ast::MetaItem>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> {
    for arg in args {
        match arg.node {
            ast::MetaItemKind::Word(ref name) |
            ast::MetaItemKind::List(ref name, _) => {
                if name == &"conf_file" {
                    return Err(("`conf_file` must be a named value", arg.span));
                }
            }
            ast::MetaItemKind::NameValue(ref name, ref value) => {
                if name == &"conf_file" {
                    return if let ast::LitKind::Str(ref file, _) = value.node {
                        Ok(Some(file.clone()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    };
                }
            }
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Toml(Vec<toml::ParserError>),
    Type(&'static str, &'static str, &'static str),
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref errs) => {
                let mut first = true;
                for err in errs {
                    if !first {
                        try!(", ".fmt(f));
                        first = false;
                    }

                    try!(err.fmt(f));
                }

                Ok(())
            }
            Error::Type(ref key, ref expected, ref got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            }
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Io(e)
    }
}

macro_rules! define_Conf {
    ($(#[$doc: meta] ($toml_name: tt, $rust_name: ident, $default: expr => $($ty: tt)+),)+) => {
        /// Type used to store lint configuration.
        pub struct Conf {
            $(#[$doc] pub $rust_name: define_Conf!(TY $($ty)+),)+
        }

        impl Default for Conf {
            fn default() -> Conf {
                Conf {
                    $($rust_name: define_Conf!(DEFAULT $($ty)+, $default),)+
                }
            }
        }

        impl Conf {
            /// Set the property `name` (which must be the `toml` name) to the given value
            #[allow(cast_sign_loss)]
            fn set(&mut self, name: String, value: toml::Value) -> Result<(), Error> {
                match name.as_str() {
                    $(
                        define_Conf!(PAT $toml_name) => {
                            if let Some(value) = define_Conf!(CONV $($ty)+, value) {
                                self.$rust_name = value;
                            }
                            else {
                                return Err(Error::Type(define_Conf!(EXPR $toml_name),
                                                       stringify!($($ty)+),
                                                       value.type_str()));
                            }
                        },
                    )+
                    "third-party" => {
                        // for external tools such as clippy-service
                        return Ok(());
                    }
                    _ => {
                        return Err(Error::UnknownKey(name));
                    }
                }

                Ok(())
            }
        }
    };

    // hack to convert tts
    (PAT $pat: pat) => { $pat };
    (EXPR $e: expr) => { $e };
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => { $value.as_integer().iter().filter_map(|&i| if i >= 0 { Some(i as u64) } else { None }).next() };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_slice();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            }
            else {
                Some(slice.iter().map(|v| v.as_str().unwrap_or_else(|| unreachable!()).to_owned()).collect())
            }
        }
        else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    ("blacklisted-names", blacklisted_names, ["foo", "bar", "baz"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    ("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    ("doc-valid-idents", doc_valid_idents, ["MiB", "GiB", "TiB", "PiB", "EiB", "GitHub", "NaN", "GPLv2", "GPLv3"] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    ("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    ("type-complexity-threshold", type_complexity_threshold, 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    ("single-char-binding-names-threshold", max_single_char_names, 5 => u64),
}

/// Read the `toml` configuration file. The function will ignore “File not found” errors iif
/// `!must_exist`, in which case, it will return the default configuration.
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
    let mut conf = Conf::default();
    let mut errors = Vec::new();

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                errors.push(err.into());
                return (conf, errors);
            }

            buf
        }
        Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
            return (conf, errors);
        }
        Err(err) => {
            errors.push(err.into());
            return (conf, errors);
        }
    };

    let mut parser = toml::Parser::new(&file);
    let toml = if let Some(toml) = parser.parse() {
        toml
    } else {
        errors.push(Error::Toml(parser.errors));
        return (conf, errors);
    };

    for (key, value) in toml {
        if let Err(err) = conf.set(key, value) {
            errors.push(err);
        }
    }

    (conf, errors)
}
<commit_msg>Add "JavaScript" to doc-valid-idents<commit_after>use std::{fmt, fs, io};
use std::io::Read;
use syntax::{ast, codemap, ptr};
use syntax::parse::token;
use toml;

/// Get the configuration file from arguments.
pub fn file(args: &[ptr::P<ast::MetaItem>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> {
    for arg in args {
        match arg.node {
            ast::MetaItemKind::Word(ref name) |
            ast::MetaItemKind::List(ref name, _) => {
                if name == &"conf_file" {
                    return Err(("`conf_file` must be a named value", arg.span));
                }
            }
            ast::MetaItemKind::NameValue(ref name, ref value) => {
                if name == &"conf_file" {
                    return if let ast::LitKind::Str(ref file, _) = value.node {
                        Ok(Some(file.clone()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    };
                }
            }
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Toml(Vec<toml::ParserError>),
    Type(&'static str, &'static str, &'static str),
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref errs) => {
                let mut first = true;
                for err in errs {
                    if !first {
                        try!(", ".fmt(f));
                        first = false;
                    }

                    try!(err.fmt(f));
                }

                Ok(())
            }
            Error::Type(ref key, ref expected, ref got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            }
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Io(e)
    }
}

macro_rules! define_Conf {
    ($(#[$doc: meta] ($toml_name: tt, $rust_name: ident, $default: expr => $($ty: tt)+),)+) => {
        /// Type used to store lint configuration.
        pub struct Conf {
            $(#[$doc] pub $rust_name: define_Conf!(TY $($ty)+),)+
        }

        impl Default for Conf {
            fn default() -> Conf {
                Conf {
                    $($rust_name: define_Conf!(DEFAULT $($ty)+, $default),)+
                }
            }
        }

        impl Conf {
            /// Set the property `name` (which must be the `toml` name) to the given value
            #[allow(cast_sign_loss)]
            fn set(&mut self, name: String, value: toml::Value) -> Result<(), Error> {
                match name.as_str() {
                    $(
                        define_Conf!(PAT $toml_name) => {
                            if let Some(value) = define_Conf!(CONV $($ty)+, value) {
                                self.$rust_name = value;
                            }
                            else {
                                return Err(Error::Type(define_Conf!(EXPR $toml_name),
                                                       stringify!($($ty)+),
                                                       value.type_str()));
                            }
                        },
                    )+
                    "third-party" => {
                        // for external tools such as clippy-service
                        return Ok(());
                    }
                    _ => {
                        return Err(Error::UnknownKey(name));
                    }
                }

                Ok(())
            }
        }
    };

    // hack to convert tts
    (PAT $pat: pat) => { $pat };
    (EXPR $e: expr) => { $e };
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => { $value.as_integer().iter().filter_map(|&i| if i >= 0 { Some(i as u64) } else { None }).next() };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_slice();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            }
            else {
                Some(slice.iter().map(|v| v.as_str().unwrap_or_else(|| unreachable!()).to_owned()).collect())
            }
        }
        else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    ("blacklisted-names", blacklisted_names, ["foo", "bar", "baz"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    ("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    ("doc-valid-idents", doc_valid_idents, ["MiB", "GiB", "TiB", "PiB", "EiB", "GitHub", "NaN", "GPLv2", "GPLv3", "JavaScript"] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    ("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    ("type-complexity-threshold", type_complexity_threshold, 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    ("single-char-binding-names-threshold", max_single_char_names, 5 => u64),
}

/// Read the `toml` configuration file. The function will ignore “File not found” errors iif
/// `!must_exist`, in which case, it will return the default configuration.
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
    let mut conf = Conf::default();
    let mut errors = Vec::new();

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                errors.push(err.into());
                return (conf, errors);
            }

            buf
        }
        Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
            return (conf, errors);
        }
        Err(err) => {
            errors.push(err.into());
            return (conf, errors);
        }
    };

    let mut parser = toml::Parser::new(&file);
    let toml = if let Some(toml) = parser.parse() {
        toml
    } else {
        errors.push(Error::Toml(parser.errors));
        return (conf, errors);
    };

    for (key, value) in toml {
        if let Err(err) = conf.set(key, value) {
            errors.push(err);
        }
    }

    (conf, errors)
}
<|endoftext|>


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

// ignore-fast #[feature] doesn't work with check-fast
#[feature(asm)];

#[allow(dead_code)];

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
    // assignment not dead
    let mut x: int = 0;
    unsafe {
        // extra colon
        asm!("mov $1, $0" : "=r"(x) : "r"(5u), "0"(x) : : "cc");
        //~^ WARNING unrecognized option
    }
    assert_eq!(x, 5);

    unsafe {
        // comma in place of a colon
        asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile");
        //~^ WARNING expected a clobber, but found an option
    }
    assert_eq!(x, 13);
}

// #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
// pub fn main() {}

// At least one error is needed so that compilation fails
#[static_assert]
static b: bool = false; //~ ERROR static assertion failed
<commit_msg>auto merge of #12994 : alexcrichton/rust/fix-android, r=huonw<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-fast #[feature] doesn't work with check-fast
// ignore-android

#[feature(asm)];

#[allow(dead_code)];

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
    // assignment not dead
    let mut x: int = 0;
    unsafe {
        // extra colon
        asm!("mov $1, $0" : "=r"(x) : "r"(5u), "0"(x) : : "cc");
        //~^ WARNING unrecognized option
    }
    assert_eq!(x, 5);

    unsafe {
        // comma in place of a colon
        asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile");
        //~^ WARNING expected a clobber, but found an option
    }
    assert_eq!(x, 13);
}

// At least one error is needed so that compilation fails
#[static_assert]
static b: bool = false; //~ ERROR static assertion failed
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use super::{Drawable, BoundingBox, MeasureMode};
use ::platform::Context;
use ::paint::{Canvas, Point, Rect};
use std::cmp::Ordering;
use std::slice::{Iter, IterMut};

pub struct AbsoluteLayout<'a> {
    children: Vec<Child<'a>>,
    bounding_box: BoundingBox,

    baseline: Option<f32>,
    axis: Option<f32>,
}

pub struct Child<'a> {
    drawable: Box<Drawable+'a>,
    params: AbsoluteLayoutParams
}

pub struct AbsoluteLayoutParams {
    position: Point
}

impl AbsoluteLayoutParams {
    pub fn new(point: Point) -> AbsoluteLayoutParams {
        AbsoluteLayoutParams { position: point }
    }
}

impl<'a> Drawable for AbsoluteLayout<'a> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        for child in &self.children {
            child.drawable.draw(canvas, &(pen_pos+&child.params.position))
        }
    }

    fn calculate(&mut self, context: &Context, _: &MeasureMode, _: &MeasureMode) {
        for child in self.children.iter_mut() {
            child.drawable.calculate(context, &MeasureMode::Wrap,&MeasureMode::Wrap);
        }

        let end_x_calc = |child: &Child|
            child.drawable.bounding_box().width() + child.params.position.x();

        let width = self.children.iter().max_by(|c1, c2| {
            end_x_calc(*c1).partial_cmp(&end_x_calc(*c2)).unwrap_or(Ordering::Less)
        }).and_then(|c| {
            Some(end_x_calc(c))
        }).unwrap_or(0f32);

        let end_y_calc = |child: &Child|
            child.drawable.bounding_box().height() + child.params.position.y();

        let height = self.children.iter().max_by(|c1, c2| {
            end_y_calc(*c1).partial_cmp(&end_y_calc(*c2)).unwrap_or(Ordering::Less)
        }).and_then(|c| {
            Some(end_y_calc(c))
        }).unwrap_or(0f32);

        self.bounding_box = BoundingBox {
            rect: Rect::new(width, height),
            baseline: self.baseline.unwrap_or(height),
            axis: self.axis.unwrap_or(height/2f32),
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a> AbsoluteLayout<'a> {
    pub fn new() -> AbsoluteLayout<'a> {
        AbsoluteLayout {
            children: Vec::new(),
            bounding_box: BoundingBox::default(),
            baseline: None,
            axis: None,
        }
    }

    pub fn add_child(&mut self, child: Box<Drawable + 'a>, params: AbsoluteLayoutParams) {
        self.children.push(Child { drawable: child, params });
    }

    pub fn remove_child_at(&mut self, index: usize) {
        self.children.remove(index);
    }

    pub fn clear(&mut self) {
        self.children.clear();
    }

    pub fn set_baseline(&mut self, baseline: Option<f32>) {
        self.baseline = baseline;
    }

    pub fn set_axis(&mut self, axis: Option<f32>) {
        self.axis = axis;
    }

    pub fn set_child_params(&mut self, index: usize, params: AbsoluteLayoutParams) {
        if let Some(val) = self.children.get_mut(index) {
            val.params = params;
        }
    }

    pub fn iter(&self) -> Iter<Child<'a>> {
        self.children.iter()
    }

    pub fn iter_mut(&mut self) -> IterMut<Child<'a>> {
        self.children.iter_mut()
    }
}<commit_msg>Expose child members<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use super::{Drawable, BoundingBox, MeasureMode};
use ::platform::Context;
use ::paint::{Canvas, Point, Rect};
use std::cmp::Ordering;
use std::slice::{Iter, IterMut};

pub struct AbsoluteLayout<'a> {
    children: Vec<Child<'a>>,
    bounding_box: BoundingBox,

    baseline: Option<f32>,
    axis: Option<f32>,
}

pub struct Child<'a> {
    drawable: Box<Drawable+'a>,
    params: AbsoluteLayoutParams
}

impl<'a> Child<'a> {
    pub fn drawable(&self) -> &Drawable {
        self.drawable.as_ref()
    }

    pub fn params(&self) -> &AbsoluteLayoutParams {
        &self.params
    }
}

pub struct AbsoluteLayoutParams {
    position: Point
}

impl AbsoluteLayoutParams {
    pub fn new(point: Point) -> AbsoluteLayoutParams {
        AbsoluteLayoutParams { position: point }
    }
}

impl<'a> Drawable for AbsoluteLayout<'a> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        for child in &self.children {
            child.drawable.draw(canvas, &(pen_pos+&child.params.position))
        }
    }

    fn calculate(&mut self, context: &Context, _: &MeasureMode, _: &MeasureMode) {
        for child in self.children.iter_mut() {
            child.drawable.calculate(context, &MeasureMode::Wrap,&MeasureMode::Wrap);
        }

        let end_x_calc = |child: &Child|
            child.drawable.bounding_box().width() + child.params.position.x();

        let width = self.children.iter().max_by(|c1, c2| {
            end_x_calc(*c1).partial_cmp(&end_x_calc(*c2)).unwrap_or(Ordering::Less)
        }).and_then(|c| {
            Some(end_x_calc(c))
        }).unwrap_or(0f32);

        let end_y_calc = |child: &Child|
            child.drawable.bounding_box().height() + child.params.position.y();

        let height = self.children.iter().max_by(|c1, c2| {
            end_y_calc(*c1).partial_cmp(&end_y_calc(*c2)).unwrap_or(Ordering::Less)
        }).and_then(|c| {
            Some(end_y_calc(c))
        }).unwrap_or(0f32);

        self.bounding_box = BoundingBox {
            rect: Rect::new(width, height),
            baseline: self.baseline.unwrap_or(height),
            axis: self.axis.unwrap_or(height/2f32),
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a> AbsoluteLayout<'a> {
    pub fn new() -> AbsoluteLayout<'a> {
        AbsoluteLayout {
            children: Vec::new(),
            bounding_box: BoundingBox::default(),
            baseline: None,
            axis: None,
        }
    }

    pub fn add_child(&mut self, child: Box<Drawable + 'a>, params: AbsoluteLayoutParams) {
        self.children.push(Child { drawable: child, params });
    }

    pub fn remove_child_at(&mut self, index: usize) {
        self.children.remove(index);
    }

    pub fn clear(&mut self) {
        self.children.clear();
    }

    pub fn set_baseline(&mut self, baseline: Option<f32>) {
        self.baseline = baseline;
    }

    pub fn set_axis(&mut self, axis: Option<f32>) {
        self.axis = axis;
    }

    pub fn set_child_params(&mut self, index: usize, params: AbsoluteLayoutParams) {
        if let Some(val) = self.children.get_mut(index) {
            val.params = params;
        }
    }

    pub fn iter(&self) -> Iter<Child<'a>> {
        self.children.iter()
    }

    pub fn iter_mut(&mut self) -> IterMut<Child<'a>> {
        self.children.iter_mut()
    }
}<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    extern crate futures;
    extern crate hyper;
    extern crate fruitbasket;
    extern crate time;

    extern crate chrono;
    use webapi::chrono::TimeZone;

    use super::super::*;
    use super::super::super::SpotifyEndpoints;

    use std;
    use std::thread;
    use std::thread::sleep;
    use std::sync::atomic::AtomicBool;
    use std::sync::atomic::Ordering;
    use std::time::Duration;
    use std::sync::{Once, ONCE_INIT};

    use self::hyper::{Post, StatusCode};
    use self::hyper::server::{Service, Request, Response};
    use self::hyper::server::Http;
    use self::futures::Stream;
    use self::futures::Future;

    static START: Once = ONCE_INIT;

    lazy_static! {
        static ref WEBSERVER_STARTED: AtomicBool = AtomicBool::new(false);
    }

    pub const TEST_API: SpotifyEndpoints = SpotifyEndpoints {
        scopes: "user-read-private streaming user-read-playback-state",
        scopes_version: 1,
        authorize: "http://127.0.0.1:9799/en/authorize",
        token: "http://127.0.0.1:9799/api/token",
        devices: "http://127.0.0.1:9799/v1/me/player/devices",
        player_state: "http://127.0.0.1:9799/v1/me/player",
        play: "http://127.0.0.1:9799/v1/me/player/play",
        pause: "http://127.0.0.1:9799/v1/me/player/pause",
        next: "http://127.0.0.1:9799/v1/me/player/next",
        previous: "http://127.0.0.1:9799/v1/me/player/previous",
        seek: "http://127.0.0.1:9799/v1/me/player/seek",
        volume: "http://127.0.0.1:9799/v1/me/player/volume",
        shuffle: "http://127.0.0.1:9799/v1/me/player/shuffle",
        repeat: "http://127.0.0.1:9799/v1/me/player/repeat",
        player: "http://127.0.0.1:9799/v1/me/player",
        add_to_playlist: "http://127.0.0.1:9799/v1/users",
    };

    /// Macro to parse the body of a POST request and send a response.
    ///
    /// There's probably a "body.to_string()" function somewhere.  I didn't find it.
    /// So instead there's this unreadable, overly complicated bullshit.
    ///
    /// $body_in: a POST body (hyper::Body) from a received POST request
    /// $pairs_out: the name of the key/value pair variable provided to the $block_in
    /// $block_in: a block of code to be executed, with $pairs_out in scope, that evaluates
    ///            to tuple (status_code: StatusCode, body: &str) to send as a response.
    macro_rules! post {
    ($body_in:ident, $pairs_out:ident, $block_in:block) => {
        {
            // Read chunks from user provided body var $body_in
            $body_in.fold(vec![], |mut acc, chunk| {
                acc.extend(chunk);
                Ok::<_, hyper::Error>(acc)
            }).and_then(move |bytes| {
                // [u8] -> String
                let post_data: String = std::str::from_utf8(&bytes).unwrap().to_string();;
                // Split on & to get ["key=value"...]
                let pairs = post_data.split("&");
                // Split on = to get [[key,value]...], put in user provided var name $pairs_out
                let $pairs_out = pairs.map(|pair| pair.split("=").collect::<Vec<&str>>()).collect::<Vec<Vec<&str>>>();
                // User provided block takes $pairs_out and returns response string
                let (code, response) = $block_in;
                let res = Response::new();
                Ok(res.with_status(code).with_body(response))
            }).boxed()
        }
    };
    }

    fn token_response(pairs: &Vec<Vec<&str>>) -> (StatusCode, String) {
        let mut resp = String::new();
        let mut code = StatusCode::Ok;
        resp.push_str("{");
        resp.push_str(r#""access_token": "valid_access_code","#);
        resp.push_str(r#""token_type": "Bearer","#);
        resp.push_str(r#""scope": "user-read-private user-read-email","#);
        resp.push_str(r#""expires_in": 3600"#);
        resp.push_str("}");
        for pair in pairs {
            let (key,value) = (pair[0], pair[1]);
            if key == "refresh_token" && value == "error" {
                code = StatusCode::Forbidden;
            }
        }
        (code, resp)
    }

    fn init() {
        while !WEBSERVER_STARTED.load(Ordering::Relaxed) {
            sleep(Duration::from_millis(100));
        }
        START.call_once(|| {
            #[derive(Clone, Copy)]
            struct Webapi;
            impl Service for Webapi {
                type Request = Request;
                type Response = Response;
                type Error = hyper::Error;
                type Future = futures::BoxFuture<Response, hyper::Error>;
                fn call(&self, req: Request) -> Self::Future {
                    let (method, uri, _, _headers, body) = req.deconstruct();
                    match(method, uri.path()) {
                        (Post, "/api/token") => post!(body, pairs, { token_response(&pairs) }),
                        _ => futures::future::ok(Response::new().with_status(StatusCode::NotFound)).boxed(),
                    }
                }
            }
            thread::spawn(move || {
                let addr = "127.0.0.1:9799".parse().unwrap();
                let server = Http::new().bind(&addr, || Ok(Webapi)).unwrap();
                server.run().unwrap();
                WEBSERVER_STARTED.store(true, Ordering::Relaxed);
            });
            // Give it some time to really come up.  It would be nicer to
            // actually try to connect to it.
            sleep(Duration::from_millis(500));
        });
    }

    #[test]
    fn test_refresh_oauth_tokens_no_connection() {
        let now = time::now_utc().to_timespec().sec as u64;
        let spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        let res = spotify.refresh_oauth_tokens();
        // Unlock webserver init so all other tests can run
        WEBSERVER_STARTED.store(true, Ordering::Relaxed);
        assert!(res.is_none());
    }

    #[test]
    fn test_refresh_oauth_tokens_pass() {
        init();
        let now = time::now_utc().to_timespec().sec as u64;
        let spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        match spotify.refresh_oauth_tokens() {
            Some((access,expires)) => {
                assert_eq!(access, "valid_access_code");
                assert_eq!(expires, 3600);
            },
            None => { assert!(false) },
        }
    }

    #[test]
    fn test_refresh_oauth_tokens_error_status() {
        init();
        let now = time::now_utc().to_timespec().sec as u64;
        let mut spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        spotify.refresh_token = Some("error".to_string());
        match spotify.refresh_oauth_tokens() {
            Some(_) => { assert!(false) },
            None => { },
        }
    }

    fn build_alarm_entry(time: &str, repeat: AlarmRepeat, now: DateTime<Local>) -> AlarmEntry {
        return AlarmEntry {
            time: time.to_string(),
            repeat: repeat,
            context: PlayContext::new().build(),
            device: "12345".to_string(),
            now: Some(now),
        }
    }

    #[test]
    fn test_alarm_scheduler() {
        let mut spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .build()
            .unwrap();

        // From a Friday morning
        let today = Local.ymd(2017, 06, 16).and_hms_milli(9, 00, 00, 0);

        // Daily, later the same day
        let entry = build_alarm_entry("21:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-16 21:00:00");

        // Daily, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-17 08:00:00");

        // Weekends, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekends, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-17 08:00:00");

        // Weekdays, the next week
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekdays, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-19 08:00:00");

        // From a Wednesday night
        let today = Local.ymd(2017, 06, 21).and_hms_milli(21, 00, 00, 0);

        // Daily, later the same day
        let entry = build_alarm_entry("23:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-21 23:00:00");

        // Daily, the next day
        let entry = build_alarm_entry("00:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 00:00:00");

        // Weekends, 3 days later
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekends, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-24 08:00:00");

        // Weekdays, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekdays, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 08:00:00");
        // Disable/re-enable and verify it responds correctly
        assert_eq!(spotify.alarm_enabled(alarm), true);
        assert!(spotify.alarm_disable(alarm).is_ok());
        assert_eq!(spotify.alarm_enabled(alarm), false);
        assert!(spotify.alarm_time(alarm).is_err());
        assert!(spotify.alarm_reschedule(alarm).is_ok());
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 08:00:00");
    }
}
<commit_msg>Fix automated tests<commit_after>#[cfg(test)]
mod tests {
    extern crate futures;
    extern crate hyper;
    extern crate fruitbasket;
    extern crate time;

    extern crate chrono;
    use webapi::chrono::TimeZone;

    use super::super::*;
    use super::super::super::SpotifyEndpoints;

    use std;
    use std::thread;
    use std::thread::sleep;
    use std::sync::atomic::AtomicBool;
    use std::sync::atomic::Ordering;
    use std::time::Duration;
    use std::sync::{Once, ONCE_INIT};

    use self::hyper::{Post, StatusCode};
    use self::hyper::server::{Service, Request, Response};
    use self::hyper::server::Http;
    use self::futures::Stream;
    use self::futures::Future;

    static START: Once = ONCE_INIT;

    lazy_static! {
        static ref WEBSERVER_STARTED: AtomicBool = AtomicBool::new(false);
    }

    pub const TEST_API: SpotifyEndpoints = SpotifyEndpoints {
        scopes: "user-read-private streaming user-read-playback-state",
        scopes_version: 1,
        authorize: "http://127.0.0.1:9799/en/authorize",
        token: "http://127.0.0.1:9799/api/token",
        devices: "http://127.0.0.1:9799/v1/me/player/devices",
        player_state: "http://127.0.0.1:9799/v1/me/player",
        play: "http://127.0.0.1:9799/v1/me/player/play",
        pause: "http://127.0.0.1:9799/v1/me/player/pause",
        next: "http://127.0.0.1:9799/v1/me/player/next",
        previous: "http://127.0.0.1:9799/v1/me/player/previous",
        seek: "http://127.0.0.1:9799/v1/me/player/seek",
        volume: "http://127.0.0.1:9799/v1/me/player/volume",
        shuffle: "http://127.0.0.1:9799/v1/me/player/shuffle",
        repeat: "http://127.0.0.1:9799/v1/me/player/repeat",
        player: "http://127.0.0.1:9799/v1/me/player",
        add_to_playlist: "http://127.0.0.1:9799/v1/users",
    };

    pub const BAD_TEST_API: SpotifyEndpoints = SpotifyEndpoints {
        scopes: "user-read-private streaming user-read-playback-state",
        scopes_version: 1,
        authorize: "http://127.0.0.1:11111/en/authorize",
        token: "http://127.0.0.1:11111/api/token",
        devices: "http://127.0.0.1:11111/v1/me/player/devices",
        player_state: "http://127.0.0.1:11111/v1/me/player",
        play: "http://127.0.0.1:11111/v1/me/player/play",
        pause: "http://127.0.0.1:11111/v1/me/player/pause",
        next: "http://127.0.0.1:11111/v1/me/player/next",
        previous: "http://127.0.0.1:11111/v1/me/player/previous",
        seek: "http://127.0.0.1:11111/v1/me/player/seek",
        volume: "http://127.0.0.1:11111/v1/me/player/volume",
        shuffle: "http://127.0.0.1:11111/v1/me/player/shuffle",
        repeat: "http://127.0.0.1:11111/v1/me/player/repeat",
        player: "http://127.0.0.1:11111/v1/me/player",
        add_to_playlist: "http://127.0.0.1:11111/v1/users",
    };

    /// Macro to parse the body of a POST request and send a response.
    ///
    /// There's probably a "body.to_string()" function somewhere.  I didn't find it.
    /// So instead there's this unreadable, overly complicated bullshit.
    ///
    /// $body_in: a POST body (hyper::Body) from a received POST request
    /// $pairs_out: the name of the key/value pair variable provided to the $block_in
    /// $block_in: a block of code to be executed, with $pairs_out in scope, that evaluates
    ///            to tuple (status_code: StatusCode, body: &str) to send as a response.
    macro_rules! post {
    ($body_in:ident, $pairs_out:ident, $block_in:block) => {
        {
            // Read chunks from user provided body var $body_in
            $body_in.fold(vec![], |mut acc, chunk| {
                acc.extend(chunk);
                Ok::<_, hyper::Error>(acc)
            }).and_then(move |bytes| {
                // [u8] -> String
                let post_data: String = std::str::from_utf8(&bytes).unwrap().to_string();;
                // Split on & to get ["key=value"...]
                let pairs = post_data.split("&");
                // Split on = to get [[key,value]...], put in user provided var name $pairs_out
                let $pairs_out = pairs.map(|pair| pair.split("=").collect::<Vec<&str>>()).collect::<Vec<Vec<&str>>>();
                // User provided block takes $pairs_out and returns response string
                let (code, response) = $block_in;
                let res = Response::new();
                Ok(res.with_status(code).with_body(response))
            }).boxed()
        }
    };
    }

    fn token_response(pairs: &Vec<Vec<&str>>) -> (StatusCode, String) {
        let mut resp = String::new();
        let mut code = StatusCode::Ok;
        resp.push_str("{");
        resp.push_str(r#""access_token": "valid_access_code","#);
        resp.push_str(r#""token_type": "Bearer","#);
        resp.push_str(r#""scope": "user-read-private user-read-email","#);
        resp.push_str(r#""expires_in": 3600"#);
        resp.push_str("}");
        for pair in pairs {
            let (key,value) = (pair[0], pair[1]);
            if key == "refresh_token" && value == "error" {
                code = StatusCode::Forbidden;
            }
        }
        (code, resp)
    }

    fn init() {
        START.call_once(|| {
            #[derive(Clone, Copy)]
            struct Webapi;
            impl Service for Webapi {
                type Request = Request;
                type Response = Response;
                type Error = hyper::Error;
                type Future = futures::BoxFuture<Response, hyper::Error>;
                fn call(&self, req: Request) -> Self::Future {
                    let (method, uri, _, _headers, body) = req.deconstruct();
                    match(method, uri.path()) {
                        (Post, "/api/token") => post!(body, pairs, { token_response(&pairs) }),
                        _ => futures::future::ok(Response::new().with_status(StatusCode::NotFound)).boxed(),
                    }
                }
            }
            thread::spawn(move || {
                let addr = "127.0.0.1:9799".parse().unwrap();
                let server = Http::new().bind(&addr, || Ok(Webapi)).unwrap();
                WEBSERVER_STARTED.store(true, Ordering::Relaxed);
                server.run().unwrap();
            });
            // Give it some time to really come up.  It would be nicer to
            // actually try to connect to it.
            sleep(Duration::from_millis(500));
        });
        while !WEBSERVER_STARTED.load(Ordering::Relaxed) {
            sleep(Duration::from_millis(100));
        }
    }

    #[test]
    fn test_refresh_oauth_tokens_no_connection() {
        let now = time::now_utc().to_timespec().sec as u64;
        let spotify = SpotifyConnectr::new()
            .with_api(BAD_TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        let res = spotify.refresh_oauth_tokens();
        // Unlock webserver init so all other tests can run
        WEBSERVER_STARTED.store(true, Ordering::Relaxed);
        assert!(res.is_none());
    }

    #[test]
    fn test_refresh_oauth_tokens_pass() {
        init();
        let now = time::now_utc().to_timespec().sec as u64;
        let spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        match spotify.refresh_oauth_tokens() {
            Some((access,expires)) => {
                assert_eq!(access, "valid_access_code");
                assert_eq!(expires, 3600);
            },
            None => { assert!(false) },
        }
    }

    #[test]
    fn test_refresh_oauth_tokens_error_status() {
        init();
        let now = time::now_utc().to_timespec().sec as u64;
        let mut spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();
        spotify.refresh_token = Some("error".to_string());
        match spotify.refresh_oauth_tokens() {
            Some(_) => { assert!(false) },
            None => { },
        }
    }

    fn build_alarm_entry(time: &str, repeat: AlarmRepeat, now: DateTime<Local>) -> AlarmEntry {
        return AlarmEntry {
            time: time.to_string(),
            repeat: repeat,
            context: PlayContext::new().build(),
            device: "12345".to_string(),
            now: Some(now),
        }
    }

    #[test]
    fn test_alarm_scheduler() {
        let now = time::now_utc().to_timespec().sec as u64;
        let mut spotify = SpotifyConnectr::new()
            .with_api(TEST_API)
            .with_oauth_tokens("access", "refresh", now + 3600)
            .build()
            .unwrap();

        // From a Friday morning
        let today = Local.ymd(2017, 06, 16).and_hms_milli(9, 00, 00, 0);

        // Daily, later the same day
        let entry = build_alarm_entry("21:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-16 21:00:00");

        // Daily, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-17 08:00:00");

        // Weekends, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekends, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-17 08:00:00");

        // Weekdays, the next week
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekdays, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-19 08:00:00");

        // From a Wednesday night
        let today = Local.ymd(2017, 06, 21).and_hms_milli(21, 00, 00, 0);

        // Daily, later the same day
        let entry = build_alarm_entry("23:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-21 23:00:00");

        // Daily, the next day
        let entry = build_alarm_entry("00:00", AlarmRepeat::Daily, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 00:00:00");

        // Weekends, 3 days later
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekends, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-24 08:00:00");

        // Weekdays, the next day
        let entry = build_alarm_entry("08:00", AlarmRepeat::Weekdays, today.clone());
        let alarm = spotify.schedule_alarm(entry).unwrap();
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 08:00:00");
        // Disable/re-enable and verify it responds correctly
        assert_eq!(spotify.alarm_enabled(alarm), true);
        assert!(spotify.alarm_disable(alarm).is_ok());
        assert_eq!(spotify.alarm_enabled(alarm), false);
        assert!(spotify.alarm_time(alarm).is_err());
        assert!(spotify.alarm_reschedule(alarm).is_ok());
        assert_eq!(spotify.alarm_time(alarm).unwrap().format("%Y-%m-%d %H:%M:%S").to_string(), "2017-06-22 08:00:00");
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::hash::Hash;

use {Point, RegionQuery, ListPoints};

/// Clustering via the DBSCAN algorithm[1].
///
/// DBSCAN is a density-based clustering algorithm: given a set of
/// points in some space, it groups together points that are closely
/// packed together (points with many nearby neighbors), marking as
/// outliers points that lie alone in low-density regions (whose
/// nearest neighbors are too far away).wikipedia
///
/// An instance of `Dbscan` is an iterator over clusters of
/// `P`. Points classified as noise once all clusters are found are
/// available via `noise_points`.
///
/// This uses the `P::Point` yielded by the iterators provided by
/// `ListPoints` and `RegionQuery` as a unique identifier for each
/// point. The algorithm will behave strangely if the identifier is
/// not unique or not stable within a given execution of DBSCAN. The
/// identifier is cloned several times in the course of execution, so
/// it should be cheap to duplicate (e.g. a `usize` index, or a `&T`
/// reference).
///
/// [1]: Ester, Martin; Kriegel, Hans-Peter; Sander, Jörg; Xu, Xiaowei
/// (1996). Simoudis, Evangelos; Han, Jiawei; Fayyad, Usama M., eds. *A
/// density-based algorithm for discovering clusters in large spatial
/// databases with noise.* Proceedings of the Second International
/// Conference on Knowledge Discovery and Data Mining (KDD-96). AAAI
/// Press. pp. 226–231.
///
/// # Examples
///
/// A basic example:
///
/// ```rust
/// use cogset::{Dbscan, BruteScan, Euclid};
///
/// let points = [Euclid([0.1]), Euclid([0.2]), Euclid([1.0])];
///
/// let scanner = BruteScan::new(&points);
/// let mut dbscan = Dbscan::new(scanner, 0.2, 2);
///
/// // get the clusters themselves
/// let clusters = dbscan.by_ref().collect::<Vec<_>>();
/// // the first two points are the only cluster
/// assert_eq!(clusters, &[&[0, 1]]);
///
/// // now the noise
/// let noise = dbscan.noise_points();
/// // which is just the last point
/// assert_eq!(noise.iter().cloned().collect::<Vec<_>>(),
///            &[2]);
/// ```
///
/// A more complicated example that renders the output nicely:
///
/// ```rust
/// use std::str;
/// use cogset::{Dbscan, BruteScan, Euclid};
///
/// fn write_points<I>(output: &mut [u8; 76], byte: u8, it: I)
///     where I: Iterator<Item = Euclid<[f64; 1]>>
/// {
///     for p in it { output[(p.0[0] * 30.0) as usize] = byte; }
/// }
///
/// // the points we're going to cluster, considered as points in ℝ
/// // with the conventional distance.
/// let points = [Euclid([0.25]), Euclid([0.9]), Euclid([2.0]), Euclid([1.2]),
///               Euclid([1.9]), Euclid([1.1]),  Euclid([1.35]), Euclid([1.85]),
///               Euclid([1.05]), Euclid([0.1]), Euclid([2.5]), Euclid([0.05]),
///               Euclid([0.6]), Euclid([0.55]), Euclid([1.6])];
///
/// // print the points before clustering
/// let mut original = [b' '; 76];
/// write_points(&mut original, b'x', points.iter().cloned());
/// println!("{}", str::from_utf8(&original).unwrap());
///
/// // set-up the data structure that will manage the queries that
/// // Dbscan needs to do.
/// let scanner = BruteScan::new(&points);
///
/// // create the clusterer: we need 3 points to consider a group a
/// // cluster, and we're only looking at points 0.2 units apart.
/// let min_points = 3;
/// let epsilon = 0.2;
/// let mut dbscan = Dbscan::new(scanner, epsilon, min_points);
///
/// let mut clustered = [b' '; 76];
///
/// // run over all the clusters, writing each to the output
/// for (i, cluster) in dbscan.by_ref().enumerate() {
///     // since we used `BruteScan`, `cluster` is a vector of indices
///     // into `points`, not the points themselves, so lets map back
///     // to the points.
///     let actual_points = cluster.iter().map(|idx| points[*idx]);
///
///     write_points(&mut clustered, b'0' + i as u8,
///     actual_points)
/// }
/// // now run over the noise points, i.e. points that aren't close
/// // enough to others to be in a cluster.
/// let noise = dbscan.noise_points();
/// write_points(&mut clustered, b'.',
/// noise.iter().map(|idx| points[*idx]));
///
/// // print the numbered clusters
/// println!("{}", str::from_utf8(&clustered).unwrap());
/// ```
///
/// Output:
///
/// ```txt
///  x x   x        x x        x   x x  x   x       x      x x  x              x
///  0 0   0        . .        2   2 2  2   2       .      1 1  1              .
/// ```
pub struct Dbscan
 where P::Point: Hash + Eq + Clone {
    visited: HashSet
,
    in_cluster: HashSet
,
    unclustered: HashSet
,
    points: P,
    all_points: P::AllPoints,
    eps: f64,
    min_points: usize,
}

impl
 Dbscan
 where P::Point: Hash + Eq + Clone {
    /// Create a new DBSCAN instance, with the given `eps` and
    /// `min_points`.
    ///
    /// `eps` is the maximum distance between points when creating
    /// neighbours to construct clusters. `min_points` is the minimum
    /// of points for a cluster.
    pub fn new(points: P, eps: f64, min_points: usize) -> Dbscan
 {
        Dbscan {
            all_points: points.all_points(),
            points: points,
            eps: eps,
            min_points: min_points,
            visited: HashSet::new(),
            in_cluster: HashSet::new(),
            unclustered: HashSet::new(),
        }
    }

    /// Points that have been classified as noise once the algorithm
    /// finishes.
    ///
    /// This only makes sense to call once the iterator is exhausted,
    /// and will give unspecified nonsense if called earlier.
    pub fn noise_points(&self) -> &HashSet
 {
        &self.unclustered
    }
}

impl
 Iterator for Dbscan
 where P::Point: Hash + Eq + Clone {
    type Item = Vec
;
    fn next(&mut self) -> Option<Vec
> {
        let mut nbrs;
        loop {
            match self.all_points.next() {
                Some(p) => {
                    if self.visited.insert(p.clone()) {
                        let n = self.points.neighbours(&p, self.eps).collect::<Vec<_>>();
                        if n.len() >= self.min_points {
                            nbrs = n;
                            break
                        } else {
                            self.unclustered.insert(p);
                        }
                    }
                }
                None => return None
            }
        }

        let mut cluster = vec![];

        for idx in 0.. {
            if idx >= nbrs.len() { break }
            let p2 = nbrs[idx].clone();
            if self.visited.insert(p2.clone()) {
                let old_len = nbrs.len();
                nbrs.extend(self.points.neighbours(&p2, self.eps));
                if nbrs.len() - old_len < self.min_points {
                    // undo: the new point doesn't have enough close
                    nbrs.truncate(old_len)
                }
            }
            if self.in_cluster.insert(p2.clone()) {
                self.unclustered.remove(&p2);
                cluster.push(p2);
            }
        }
        Some(cluster)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {Point, BruteScan};

    struct Linear(f64);
    impl Point for Linear {
        fn dist(&self, other: &Linear) -> f64 {
            (self.0 - other.0).abs()
        }
        fn dist_lower_bound(&self, other: &Linear) -> f64 {
            self.dist(other)
        }
    }

    #[test]
    fn smoke() {
        // 0 ...        .         .... 10 (not to scale)
        let points = [Linear(0.0), Linear(10.0), Linear(9.5), Linear(0.5), Linear(0.6),
                      Linear(9.1), Linear(9.9), Linear(5.0)];
        let points = BruteScan::new(&points);
        let mut dbscan = Dbscan::new(points, 0.5, 3);
        let mut clusters = dbscan.by_ref().collect::<Vec<_>>();

        // normalise:
        for x in &mut clusters { x.sort() }
        clusters.sort();

        assert_eq!(clusters, &[&[0usize, 3, 4] as &[_], &[1usize, 2, 5, 6] as &_]);
        assert_eq!(dbscan.noise_points().iter().cloned().collect::<Vec<_>>(),
                   &[7]);
    }
}
<commit_msg>Fix broken indentation in documentation.<commit_after>use std::collections::HashSet;
use std::hash::Hash;

use {Point, RegionQuery, ListPoints};

/// Clustering via the DBSCAN algorithm[1].
///
/// DBSCAN is a density-based clustering algorithm: given a set of
/// points in some space, it groups together points that are closely
/// packed together (points with many nearby neighbors), marking as
/// outliers points that lie alone in low-density regions (whose
/// nearest neighbors are too far away).wikipedia
///
/// An instance of `Dbscan` is an iterator over clusters of
/// `P`. Points classified as noise once all clusters are found are
/// available via `noise_points`.
///
/// This uses the `P::Point` yielded by the iterators provided by
/// `ListPoints` and `RegionQuery` as a unique identifier for each
/// point. The algorithm will behave strangely if the identifier is
/// not unique or not stable within a given execution of DBSCAN. The
/// identifier is cloned several times in the course of execution, so
/// it should be cheap to duplicate (e.g. a `usize` index, or a `&T`
/// reference).
///
/// [1]: Ester, Martin; Kriegel, Hans-Peter; Sander, Jörg; Xu, Xiaowei
/// (1996). Simoudis, Evangelos; Han, Jiawei; Fayyad, Usama M., eds. *A
/// density-based algorithm for discovering clusters in large spatial
/// databases with noise.* Proceedings of the Second International
/// Conference on Knowledge Discovery and Data Mining (KDD-96). AAAI
/// Press. pp. 226–231.
///
/// # Examples
///
/// A basic example:
///
/// ```rust
/// use cogset::{Dbscan, BruteScan, Euclid};
///
/// let points = [Euclid([0.1]), Euclid([0.2]), Euclid([1.0])];
///
/// let scanner = BruteScan::new(&points);
/// let mut dbscan = Dbscan::new(scanner, 0.2, 2);
///
/// // get the clusters themselves
/// let clusters = dbscan.by_ref().collect::<Vec<_>>();
/// // the first two points are the only cluster
/// assert_eq!(clusters, &[&[0, 1]]);
///
/// // now the noise
/// let noise = dbscan.noise_points();
/// // which is just the last point
/// assert_eq!(noise.iter().cloned().collect::<Vec<_>>(),
///            &[2]);
/// ```
///
/// A more complicated example that renders the output nicely:
///
/// ```rust
/// use std::str;
/// use cogset::{Dbscan, BruteScan, Euclid};
///
/// fn write_points<I>(output: &mut [u8; 76], byte: u8, it: I)
///     where I: Iterator<Item = Euclid<[f64; 1]>>
/// {
///     for p in it { output[(p.0[0] * 30.0) as usize] = byte; }
/// }
///
/// // the points we're going to cluster, considered as points in ℝ
/// // with the conventional distance.
/// let points = [Euclid([0.25]), Euclid([0.9]), Euclid([2.0]), Euclid([1.2]),
///               Euclid([1.9]), Euclid([1.1]),  Euclid([1.35]), Euclid([1.85]),
///               Euclid([1.05]), Euclid([0.1]), Euclid([2.5]), Euclid([0.05]),
///               Euclid([0.6]), Euclid([0.55]), Euclid([1.6])];
///
/// // print the points before clustering
/// let mut original = [b' '; 76];
/// write_points(&mut original, b'x', points.iter().cloned());
/// println!("{}", str::from_utf8(&original).unwrap());
///
/// // set-up the data structure that will manage the queries that
/// // Dbscan needs to do.
/// let scanner = BruteScan::new(&points);
///
/// // create the clusterer: we need 3 points to consider a group a
/// // cluster, and we're only looking at points 0.2 units apart.
/// let min_points = 3;
/// let epsilon = 0.2;
/// let mut dbscan = Dbscan::new(scanner, epsilon, min_points);
///
/// let mut clustered = [b' '; 76];
///
/// // run over all the clusters, writing each to the output
/// for (i, cluster) in dbscan.by_ref().enumerate() {
///     // since we used `BruteScan`, `cluster` is a vector of indices
///     // into `points`, not the points themselves, so lets map back
///     // to the points.
///     let actual_points = cluster.iter().map(|idx| points[*idx]);
///
///     write_points(&mut clustered, b'0' + i as u8,
///                  actual_points)
/// }
/// // now run over the noise points, i.e. points that aren't close
/// // enough to others to be in a cluster.
/// let noise = dbscan.noise_points();
/// write_points(&mut clustered, b'.',
///              noise.iter().map(|idx| points[*idx]));
///
/// // print the numbered clusters
/// println!("{}", str::from_utf8(&clustered).unwrap());
/// ```
///
/// Output:
///
/// ```txt
///  x x   x        x x        x   x x  x   x       x      x x  x              x
///  0 0   0        . .        2   2 2  2   2       .      1 1  1              .
/// ```
pub struct Dbscan
 where P::Point: Hash + Eq + Clone {
    visited: HashSet
,
    in_cluster: HashSet
,
    unclustered: HashSet
,
    points: P,
    all_points: P::AllPoints,
    eps: f64,
    min_points: usize,
}

impl
 Dbscan
 where P::Point: Hash + Eq + Clone {
    /// Create a new DBSCAN instance, with the given `eps` and
    /// `min_points`.
    ///
    /// `eps` is the maximum distance between points when creating
    /// neighbours to construct clusters. `min_points` is the minimum
    /// of points for a cluster.
    pub fn new(points: P, eps: f64, min_points: usize) -> Dbscan
 {
        Dbscan {
            all_points: points.all_points(),
            points: points,
            eps: eps,
            min_points: min_points,
            visited: HashSet::new(),
            in_cluster: HashSet::new(),
            unclustered: HashSet::new(),
        }
    }

    /// Points that have been classified as noise once the algorithm
    /// finishes.
    ///
    /// This only makes sense to call once the iterator is exhausted,
    /// and will give unspecified nonsense if called earlier.
    pub fn noise_points(&self) -> &HashSet
 {
        &self.unclustered
    }
}

impl
 Iterator for Dbscan
 where P::Point: Hash + Eq + Clone {
    type Item = Vec
;
    fn next(&mut self) -> Option<Vec
> {
        let mut nbrs;
        loop {
            match self.all_points.next() {
                Some(p) => {
                    if self.visited.insert(p.clone()) {
                        let n = self.points.neighbours(&p, self.eps).collect::<Vec<_>>();
                        if n.len() >= self.min_points {
                            nbrs = n;
                            break
                        } else {
                            self.unclustered.insert(p);
                        }
                    }
                }
                None => return None
            }
        }

        let mut cluster = vec![];

        for idx in 0.. {
            if idx >= nbrs.len() { break }
            let p2 = nbrs[idx].clone();
            if self.visited.insert(p2.clone()) {
                let old_len = nbrs.len();
                nbrs.extend(self.points.neighbours(&p2, self.eps));
                if nbrs.len() - old_len < self.min_points {
                    // undo: the new point doesn't have enough close
                    nbrs.truncate(old_len)
                }
            }
            if self.in_cluster.insert(p2.clone()) {
                self.unclustered.remove(&p2);
                cluster.push(p2);
            }
        }
        Some(cluster)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {Point, BruteScan};

    struct Linear(f64);
    impl Point for Linear {
        fn dist(&self, other: &Linear) -> f64 {
            (self.0 - other.0).abs()
        }
        fn dist_lower_bound(&self, other: &Linear) -> f64 {
            self.dist(other)
        }
    }

    #[test]
    fn smoke() {
        // 0 ...        .         .... 10 (not to scale)
        let points = [Linear(0.0), Linear(10.0), Linear(9.5), Linear(0.5), Linear(0.6),
                      Linear(9.1), Linear(9.9), Linear(5.0)];
        let points = BruteScan::new(&points);
        let mut dbscan = Dbscan::new(points, 0.5, 3);
        let mut clusters = dbscan.by_ref().collect::<Vec<_>>();

        // normalise:
        for x in &mut clusters { x.sort() }
        clusters.sort();

        assert_eq!(clusters, &[&[0usize, 3, 4] as &[_], &[1usize, 2, 5, 6] as &_]);
        assert_eq!(dbscan.noise_points().iter().cloned().collect::<Vec<_>>(),
                   &[7]);
    }
}
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub unsafe fn get_raw(&self) -> id {
        self.0
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) != 0 }
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<commit_msg>Add crate-internal function to create a Device from a raw id<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub unsafe fn get_raw(&self) -> id {
        self.0
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) != 0 }
    }
}

/// Internal utility function to create a Device without exposing internals.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub fn _make_device(device: id) -> Device {
    Device(device)
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use Digest::*;

#[allow(non_camel_case_types)]
pub enum Digest {
    Sha1([u8; 20]),
    Sha2_256([u8; 32]),
    Sha2_512([u8; 64]),
    Sha3_512([u8; 64]),
    Sha3_384([u8; 48]),
    Sha3_256([u8; 32]),
    Sha3_224([u8; 28]),
    Shake128([u8; 16]),
    Shake256([u8; 32]),
    Blake2B([u8; 64]),
    Blake2S([u8; 32]),
    ApplicationSpecific {
        code: u8,
        bytes: Vec<u8>,
    },
}

impl Digest {
    // TODO: Real error type
    /// Returns an empty digest of the specified type, validates code and length
    pub fn from_code_and_length(code: u8, length: usize) -> Result<Digest, &'static str> {
        match code {
            0x11 if length <= 20 => Ok(Sha1([0; 20])),
            0x12 if length <= 32 => Ok(Sha2_256([0; 32])),
            0x13 if length <= 64 => Ok(Sha2_512([0; 64])),
            0x14 if length <= 64 => Ok(Sha3_512([0; 64])),
            0x15 if length <= 48 => Ok(Sha3_384([0; 48])),
            0x16 if length <= 32 => Ok(Sha3_256([0; 32])),
            0x17 if length <= 28 => Ok(Sha3_224([0; 28])),
            0x18 if length <= 16 => Ok(Shake128([0; 16])),
            0x19 if length <= 32 => Ok(Shake256([0; 32])),

            0x40 if length <= 64 => Ok(Blake2B([0; 64])),
            0x41 if length <= 32 => Ok(Blake2S([0; 32])),

            _ if code < 0x10 && length < 0x7f =>
                Ok(ApplicationSpecific { code: code, bytes: vec![0; length] }),
            _ if code > 0x7f => Err("Codes greater than 0x7f are an unsupported future feature"),
            _ => Err("Digest length exceeds allowed length for specified type"),
        }
    }

    pub fn code(&self) -> u8 {
        match *self {
            Sha1(_) => 0x11,
            Sha2_256(_) => 0x12,
            Sha2_512(_) => 0x13,
            Sha3_512(_) => 0x14,
            Sha3_384(_) => 0x15,
            Sha3_256(_) => 0x16,
            Sha3_224(_) => 0x17,
            Shake128(_) => 0x18,
            Shake256(_) => 0x19,
            Blake2B(_) => 0x40,
            Blake2S(_) => 0x41,

            ApplicationSpecific { code, .. } if code < 0x10 => code,

            // TODO: could just ignore them being invalid or return Error...
            ApplicationSpecific { code, .. } =>
                panic!("application specific code {:#02x} outside allowed range 0x00-0x0f", code),
        }
    }

    pub fn name(&self) -> &'static str {
        match *self {
            Sha1(_) => "sha1",
            Sha2_256(_) => "sha2-256",
            Sha2_512(_) => "sha2-512",
            Sha3_224(_) => "sha3-224",
            Sha3_256(_) => "sha3-256",
            Sha3_384(_) => "sha3-384",
            Sha3_512(_) => "sha3-512",
            Shake128(_) => "shake-128",
            Shake256(_) => "shake-256",
            Blake2B(_) => "blake2b",
            Blake2S(_) => "blake2s",

            ApplicationSpecific { code, .. } if code < 0x10 => "app-specific",
            _ => panic!("TODO: not panic"),
        }
    }

    pub fn bytes(&self) -> &[u8] {
        match *self {
            Sha1(ref bytes) => bytes,
            Sha2_256(ref bytes) => bytes,
            Sha2_512(ref bytes) => bytes,
            Sha3_224(ref bytes) => bytes,
            Sha3_256(ref bytes) => bytes,
            Sha3_384(ref bytes) => bytes,
            Sha3_512(ref bytes) => bytes,
            Shake128(ref bytes) => bytes,
            Shake256(ref bytes) => bytes,
            Blake2B(ref bytes) => bytes,
            Blake2S(ref bytes) => bytes,
            ApplicationSpecific { ref bytes, .. } => bytes,
        }
    }

    pub fn mut_bytes(&mut self) -> &mut [u8] {
        match *self {
            Sha1(ref mut bytes) => bytes,
            Sha2_256(ref mut bytes) => bytes,
            Sha2_512(ref mut bytes) => bytes,
            Sha3_224(ref mut bytes) => bytes,
            Sha3_256(ref mut bytes) => bytes,
            Sha3_384(ref mut bytes) => bytes,
            Sha3_512(ref mut bytes) => bytes,
            Shake128(ref mut bytes) => bytes,
            Shake256(ref mut bytes) => bytes,
            Blake2B(ref mut bytes) => bytes,
            Blake2S(ref mut bytes) => bytes,
            ApplicationSpecific { ref mut bytes, .. } => bytes,
        }
    }
}

impl fmt::Debug for Digest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple(self.name()).field(&self.bytes().to_vec()).finish()
    }
}

impl Clone for Digest {
    fn clone(&self) -> Digest {
        match *self {
            Sha1(ref bytes) => Sha1(*bytes),
            Sha2_256(ref bytes) => Sha2_256(*bytes),
            Sha2_512(ref bytes) => Sha2_512(*bytes),
            Sha3_224(ref bytes) => Sha3_224(*bytes),
            Sha3_256(ref bytes) => Sha3_256(*bytes),
            Sha3_384(ref bytes) => Sha3_384(*bytes),
            Sha3_512(ref bytes) => Sha3_512(*bytes),
            Shake128(ref bytes) => Shake128(*bytes),
            Shake256(ref bytes) => Shake256(*bytes),
            Blake2B(ref bytes) => Blake2B(*bytes),
            Blake2S(ref bytes) => Blake2S(*bytes),
            ApplicationSpecific { code, ref bytes } => ApplicationSpecific {
                code: code,
                bytes: bytes.clone(),
            },
        }
    }
}

impl Eq for Digest {}
impl PartialEq for Digest {
    fn eq(&self, other: &Digest) -> bool {
        match (self, other) {
            (&Sha1(ref left), &Sha1(ref right)) => left == right,
            (&Sha2_256(ref left), &Sha2_256(ref right)) => left == right,
            (&Sha2_512(ref left), &Sha2_512(ref right)) => left[..] == right[..],
            (&Sha3_224(ref left), &Sha3_224(ref right)) => left == right,
            (&Sha3_256(ref left), &Sha3_256(ref right)) => left == right,
            (&Sha3_384(ref left), &Sha3_384(ref right)) => left[..] == right[..],
            (&Sha3_512(ref left), &Sha3_512(ref right)) => left[..] == right[..],
            (&Shake128(ref left), &Shake128(ref right)) => left == right,
            (&Shake256(ref left), &Shake256(ref right)) => left == right,
            (&Blake2B(ref left), &Blake2B(ref right)) => left[..] == right[..],
            (&Blake2S(ref left), &Blake2S(ref right)) => left == right,
            (
                &ApplicationSpecific { code: left_code, bytes: ref left },
                &ApplicationSpecific { code: right_code, bytes: ref right }
            ) if left_code == right_code => left == right,
            _ => false,
        }
    }
}
<commit_msg>non-local error return<commit_after>use std::fmt;

use Digest::*;

#[allow(non_camel_case_types)]
pub enum Digest {
    Sha1([u8; 20]),
    Sha2_256([u8; 32]),
    Sha2_512([u8; 64]),
    Sha3_512([u8; 64]),
    Sha3_384([u8; 48]),
    Sha3_256([u8; 32]),
    Sha3_224([u8; 28]),
    Shake128([u8; 16]),
    Shake256([u8; 32]),
    Blake2B([u8; 64]),
    Blake2S([u8; 32]),
    ApplicationSpecific {
        code: u8,
        bytes: Vec<u8>,
    },
}

impl Digest {
    // TODO: Real error type
    /// Returns an empty digest of the specified type, validates code and length
    pub fn from_code_and_length(code: u8, length: usize) -> Result<Digest, &'static str> {
        Ok(match code {
            0x11 if length <= 20 => Sha1([0; 20]),
            0x12 if length <= 32 => Sha2_256([0; 32]),
            0x13 if length <= 64 => Sha2_512([0; 64]),
            0x14 if length <= 64 => Sha3_512([0; 64]),
            0x15 if length <= 48 => Sha3_384([0; 48]),
            0x16 if length <= 32 => Sha3_256([0; 32]),
            0x17 if length <= 28 => Sha3_224([0; 28]),
            0x18 if length <= 16 => Shake128([0; 16]),
            0x19 if length <= 32 => Shake256([0; 32]),

            0x40 if length <= 64 => Blake2B([0; 64]),
            0x41 if length <= 32 => Blake2S([0; 32]),

            _ if code < 0x10 && length < 0x7f =>
                ApplicationSpecific { code: code, bytes: vec![0; length] },
            _ if code > 0x7f => {
                return Err("Codes greater than 0x7f are an unsupported future feature")
            }
            _ => {
                return Err("Digest length exceeds allowed length for specified type")
            }
        })
    }

    pub fn code(&self) -> u8 {
        match *self {
            Sha1(_) => 0x11,
            Sha2_256(_) => 0x12,
            Sha2_512(_) => 0x13,
            Sha3_512(_) => 0x14,
            Sha3_384(_) => 0x15,
            Sha3_256(_) => 0x16,
            Sha3_224(_) => 0x17,
            Shake128(_) => 0x18,
            Shake256(_) => 0x19,
            Blake2B(_) => 0x40,
            Blake2S(_) => 0x41,

            ApplicationSpecific { code, .. } if code < 0x10 => code,

            // TODO: could just ignore them being invalid or return Error...
            ApplicationSpecific { code, .. } =>
                panic!("application specific code {:#02x} outside allowed range 0x00-0x0f", code),
        }
    }

    pub fn name(&self) -> &'static str {
        match *self {
            Sha1(_) => "sha1",
            Sha2_256(_) => "sha2-256",
            Sha2_512(_) => "sha2-512",
            Sha3_224(_) => "sha3-224",
            Sha3_256(_) => "sha3-256",
            Sha3_384(_) => "sha3-384",
            Sha3_512(_) => "sha3-512",
            Shake128(_) => "shake-128",
            Shake256(_) => "shake-256",
            Blake2B(_) => "blake2b",
            Blake2S(_) => "blake2s",

            ApplicationSpecific { code, .. } if code < 0x10 => "app-specific",
            _ => panic!("TODO: not panic"),
        }
    }

    pub fn bytes(&self) -> &[u8] {
        match *self {
            Sha1(ref bytes) => bytes,
            Sha2_256(ref bytes) => bytes,
            Sha2_512(ref bytes) => bytes,
            Sha3_224(ref bytes) => bytes,
            Sha3_256(ref bytes) => bytes,
            Sha3_384(ref bytes) => bytes,
            Sha3_512(ref bytes) => bytes,
            Shake128(ref bytes) => bytes,
            Shake256(ref bytes) => bytes,
            Blake2B(ref bytes) => bytes,
            Blake2S(ref bytes) => bytes,
            ApplicationSpecific { ref bytes, .. } => bytes,
        }
    }

    pub fn mut_bytes(&mut self) -> &mut [u8] {
        match *self {
            Sha1(ref mut bytes) => bytes,
            Sha2_256(ref mut bytes) => bytes,
            Sha2_512(ref mut bytes) => bytes,
            Sha3_224(ref mut bytes) => bytes,
            Sha3_256(ref mut bytes) => bytes,
            Sha3_384(ref mut bytes) => bytes,
            Sha3_512(ref mut bytes) => bytes,
            Shake128(ref mut bytes) => bytes,
            Shake256(ref mut bytes) => bytes,
            Blake2B(ref mut bytes) => bytes,
            Blake2S(ref mut bytes) => bytes,
            ApplicationSpecific { ref mut bytes, .. } => bytes,
        }
    }
}

impl fmt::Debug for Digest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple(self.name()).field(&self.bytes().to_vec()).finish()
    }
}

impl Clone for Digest {
    fn clone(&self) -> Digest {
        match *self {
            Sha1(ref bytes) => Sha1(*bytes),
            Sha2_256(ref bytes) => Sha2_256(*bytes),
            Sha2_512(ref bytes) => Sha2_512(*bytes),
            Sha3_224(ref bytes) => Sha3_224(*bytes),
            Sha3_256(ref bytes) => Sha3_256(*bytes),
            Sha3_384(ref bytes) => Sha3_384(*bytes),
            Sha3_512(ref bytes) => Sha3_512(*bytes),
            Shake128(ref bytes) => Shake128(*bytes),
            Shake256(ref bytes) => Shake256(*bytes),
            Blake2B(ref bytes) => Blake2B(*bytes),
            Blake2S(ref bytes) => Blake2S(*bytes),
            ApplicationSpecific { code, ref bytes } => ApplicationSpecific {
                code: code,
                bytes: bytes.clone(),
            },
        }
    }
}

impl Eq for Digest {}
impl PartialEq for Digest {
    fn eq(&self, other: &Digest) -> bool {
        match (self, other) {
            (&Sha1(ref left), &Sha1(ref right)) => left == right,
            (&Sha2_256(ref left), &Sha2_256(ref right)) => left == right,
            (&Sha2_512(ref left), &Sha2_512(ref right)) => left[..] == right[..],
            (&Sha3_224(ref left), &Sha3_224(ref right)) => left == right,
            (&Sha3_256(ref left), &Sha3_256(ref right)) => left == right,
            (&Sha3_384(ref left), &Sha3_384(ref right)) => left[..] == right[..],
            (&Sha3_512(ref left), &Sha3_512(ref right)) => left[..] == right[..],
            (&Shake128(ref left), &Shake128(ref right)) => left == right,
            (&Shake256(ref left), &Shake256(ref right)) => left == right,
            (&Blake2B(ref left), &Blake2B(ref right)) => left[..] == right[..],
            (&Blake2S(ref left), &Blake2S(ref right)) => left == right,
            (
                &ApplicationSpecific { code: left_code, bytes: ref left },
                &ApplicationSpecific { code: right_code, bytes: ref right }
            ) if left_code == right_code => left == right,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt;
use std::result;

use base64;
use serde_json;

/// A crate private constructor for `Error`.
pub(crate) fn new_error(kind: ErrorKind) -> Error {
    Error(Box::new(kind))
}

/// A type alias for `Result<T, jsonwebtoken::Error>`.
pub type Result<T> = result::Result<T, Error>;

/// An error that can occur when encoding/decoding JWTs
#[derive(Debug)]
pub struct Error(Box<ErrorKind>);

impl Error {
    /// Return the specific type of this error.
    pub fn kind(&self) -> &ErrorKind {
        &self.0
    }

    /// Unwrap this error into its underlying type.
    pub fn into_kind(self) -> ErrorKind {
        *self.0
    }
}

/// The specific type of an error.
#[derive(Debug)]
pub enum ErrorKind {
    /// When a token doesn't have a valid JWT shape
    InvalidToken,
    /// When the signature doesn't match
    InvalidSignature,
    /// When the secret given is not a valid ECDSA key
    InvalidEcdsaKey,
    /// When the secret given is not a valid RSA key
    InvalidRsaKey,
    /// When the algorithm from string doesn't match the one passed to `from_str`
    InvalidAlgorithmName,
    /// When a key is provided with an invalid format
    InvalidKeyFormat,

    // Validation errors
    /// When a token’s `exp` claim indicates that it has expired
    ExpiredSignature,
    /// When a token’s `iss` claim does not match the expected issuer
    InvalidIssuer,
    /// When a token’s `aud` claim does not match one of the expected audience values
    InvalidAudience,
    /// When a token’s `aud` claim does not match one of the expected audience values
    InvalidSubject,
    /// When a token’s nbf claim represents a time in the future
    ImmatureSignature,
    /// When the algorithm in the header doesn't match the one passed to `decode` or the encoding/decoding key
    /// used doesn't match the alg requested
    InvalidAlgorithm,

    // 3rd party errors
    /// An error happened when decoding some base64 text
    Base64(base64::DecodeError),
    /// An error happened while serializing/deserializing JSON
    Json(serde_json::Error),
    /// Some of the text was invalid UTF-8
    Utf8(::std::string::FromUtf8Error),
    /// Something unspecified went wrong with crypto
    Crypto(::ring::error::Unspecified),

    /// Hints that destructuring should not be exhaustive.
    ///
    /// This enum may grow additional variants, so this makes sure clients
    /// don't count on exhaustive matching. (Otherwise, adding a new variant
    /// could break existing code.)
    #[doc(hidden)]
    __Nonexhaustive,
}

impl StdError for Error {
    fn cause(&self) -> Option<&dyn StdError> {
        match *self.0 {
            ErrorKind::InvalidToken => None,
            ErrorKind::InvalidSignature => None,
            ErrorKind::InvalidEcdsaKey => None,
            ErrorKind::InvalidRsaKey => None,
            ErrorKind::ExpiredSignature => None,
            ErrorKind::InvalidIssuer => None,
            ErrorKind::InvalidAudience => None,
            ErrorKind::InvalidSubject => None,
            ErrorKind::ImmatureSignature => None,
            ErrorKind::InvalidAlgorithm => None,
            ErrorKind::InvalidAlgorithmName => None,
            ErrorKind::InvalidKeyFormat => None,
            ErrorKind::Base64(ref err) => Some(err),
            ErrorKind::Json(ref err) => Some(err),
            ErrorKind::Utf8(ref err) => Some(err),
            ErrorKind::Crypto(ref err) => Some(err),
            ErrorKind::__Nonexhaustive => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.0 {
            ErrorKind::InvalidToken
            | ErrorKind::InvalidSignature
            | ErrorKind::InvalidEcdsaKey
            | ErrorKind::InvalidRsaKey
            | ErrorKind::ExpiredSignature
            | ErrorKind::InvalidIssuer
            | ErrorKind::InvalidAudience
            | ErrorKind::InvalidSubject
            | ErrorKind::ImmatureSignature
            | ErrorKind::InvalidAlgorithm
            | ErrorKind::InvalidKeyFormat
            | ErrorKind::InvalidAlgorithmName => write!(f, "{}", self),
            ErrorKind::Json(ref err) => write!(f, "JSON error: {}", err),
            ErrorKind::Utf8(ref err) => write!(f, "UTF-8 error: {}", err),
            ErrorKind::Crypto(ref err) => write!(f, "Crypto error: {}", err),
            ErrorKind::Base64(ref err) => write!(f, "Base64 error: {}", err),
            ErrorKind::__Nonexhaustive => write!(f, "Unknown error"),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        new_error(ErrorKind::Base64(err))
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Error {
        new_error(ErrorKind::Json(err))
    }
}

impl From<::std::string::FromUtf8Error> for Error {
    fn from(err: ::std::string::FromUtf8Error) -> Error {
        new_error(ErrorKind::Utf8(err))
    }
}

impl From<::ring::error::Unspecified> for Error {
    fn from(err: ::ring::error::Unspecified) -> Error {
        new_error(ErrorKind::Crypto(err))
    }
}

impl From<::ring::error::KeyRejected> for Error {
    fn from(_err: ::ring::error::KeyRejected) -> Error {
        new_error(ErrorKind::InvalidEcdsaKey)
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Error {
        new_error(kind)
    }
}
<commit_msg>- Fix recursive `Display::fmt` implementation on `Error` to avoid stack overflow - Added unit test to cover a sample case<commit_after>use std::error::Error as StdError;
use std::fmt;
use std::result;

use base64;
use serde_json;

/// A crate private constructor for `Error`.
pub(crate) fn new_error(kind: ErrorKind) -> Error {
    Error(Box::new(kind))
}

/// A type alias for `Result<T, jsonwebtoken::Error>`.
pub type Result<T> = result::Result<T, Error>;

/// An error that can occur when encoding/decoding JWTs
#[derive(Debug)]
pub struct Error(Box<ErrorKind>);

impl Error {
    /// Return the specific type of this error.
    pub fn kind(&self) -> &ErrorKind {
        &self.0
    }

    /// Unwrap this error into its underlying type.
    pub fn into_kind(self) -> ErrorKind {
        *self.0
    }
}

/// The specific type of an error.
#[derive(Debug)]
pub enum ErrorKind {
    /// When a token doesn't have a valid JWT shape
    InvalidToken,
    /// When the signature doesn't match
    InvalidSignature,
    /// When the secret given is not a valid ECDSA key
    InvalidEcdsaKey,
    /// When the secret given is not a valid RSA key
    InvalidRsaKey,
    /// When the algorithm from string doesn't match the one passed to `from_str`
    InvalidAlgorithmName,
    /// When a key is provided with an invalid format
    InvalidKeyFormat,

    // Validation errors
    /// When a token’s `exp` claim indicates that it has expired
    ExpiredSignature,
    /// When a token’s `iss` claim does not match the expected issuer
    InvalidIssuer,
    /// When a token’s `aud` claim does not match one of the expected audience values
    InvalidAudience,
    /// When a token’s `aud` claim does not match one of the expected audience values
    InvalidSubject,
    /// When a token’s nbf claim represents a time in the future
    ImmatureSignature,
    /// When the algorithm in the header doesn't match the one passed to `decode` or the encoding/decoding key
    /// used doesn't match the alg requested
    InvalidAlgorithm,

    // 3rd party errors
    /// An error happened when decoding some base64 text
    Base64(base64::DecodeError),
    /// An error happened while serializing/deserializing JSON
    Json(serde_json::Error),
    /// Some of the text was invalid UTF-8
    Utf8(::std::string::FromUtf8Error),
    /// Something unspecified went wrong with crypto
    Crypto(::ring::error::Unspecified),

    /// Hints that destructuring should not be exhaustive.
    ///
    /// This enum may grow additional variants, so this makes sure clients
    /// don't count on exhaustive matching. (Otherwise, adding a new variant
    /// could break existing code.)
    #[doc(hidden)]
    __Nonexhaustive,
}

impl StdError for Error {
    fn cause(&self) -> Option<&dyn StdError> {
        match *self.0 {
            ErrorKind::InvalidToken => None,
            ErrorKind::InvalidSignature => None,
            ErrorKind::InvalidEcdsaKey => None,
            ErrorKind::InvalidRsaKey => None,
            ErrorKind::ExpiredSignature => None,
            ErrorKind::InvalidIssuer => None,
            ErrorKind::InvalidAudience => None,
            ErrorKind::InvalidSubject => None,
            ErrorKind::ImmatureSignature => None,
            ErrorKind::InvalidAlgorithm => None,
            ErrorKind::InvalidAlgorithmName => None,
            ErrorKind::InvalidKeyFormat => None,
            ErrorKind::Base64(ref err) => Some(err),
            ErrorKind::Json(ref err) => Some(err),
            ErrorKind::Utf8(ref err) => Some(err),
            ErrorKind::Crypto(ref err) => Some(err),
            ErrorKind::__Nonexhaustive => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.0 {
            ErrorKind::InvalidToken
            | ErrorKind::InvalidSignature
            | ErrorKind::InvalidEcdsaKey
            | ErrorKind::InvalidRsaKey
            | ErrorKind::ExpiredSignature
            | ErrorKind::InvalidIssuer
            | ErrorKind::InvalidAudience
            | ErrorKind::InvalidSubject
            | ErrorKind::ImmatureSignature
            | ErrorKind::InvalidAlgorithm
            | ErrorKind::InvalidKeyFormat
            | ErrorKind::InvalidAlgorithmName => write!(f, "{:?}", self.0),
            ErrorKind::Json(ref err) => write!(f, "JSON error: {}", err),
            ErrorKind::Utf8(ref err) => write!(f, "UTF-8 error: {}", err),
            ErrorKind::Crypto(ref err) => write!(f, "Crypto error: {}", err),
            ErrorKind::Base64(ref err) => write!(f, "Base64 error: {}", err),
            ErrorKind::__Nonexhaustive => write!(f, "Unknown error"),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        new_error(ErrorKind::Base64(err))
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Error {
        new_error(ErrorKind::Json(err))
    }
}

impl From<::std::string::FromUtf8Error> for Error {
    fn from(err: ::std::string::FromUtf8Error) -> Error {
        new_error(ErrorKind::Utf8(err))
    }
}

impl From<::ring::error::Unspecified> for Error {
    fn from(err: ::ring::error::Unspecified) -> Error {
        new_error(ErrorKind::Crypto(err))
    }
}

impl From<::ring::error::KeyRejected> for Error {
    fn from(_err: ::ring::error::KeyRejected) -> Error {
        new_error(ErrorKind::InvalidEcdsaKey)
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Error {
        new_error(kind)
    }
}

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

    #[test]
    fn test_error_rendering() {
        assert_eq!("InvalidAlgorithmName", Error::from(ErrorKind::InvalidAlgorithmName).to_string());
    }

}<|endoftext|>