<commit_before>const R: usize = 256;

/// 245-Way trie
pub struct Node<V> {
    val: Option<V>,
    next: Vec<Option<Node<V>>>
}

impl<V> Node<V> {
    fn new() -> Node<V> {
        Node {
            val: None,
            next: {
                let mut next = Vec::with_capacity(R);
                for _ in 0 .. R { next.push(None) }
                next
            }
        }
    }

    fn put(mut x: Option<Node<V>>, key: &str, val: V, d: usize) -> Option<Node<V>> {
        if x.is_none() {
            x = Some(Node::new());
        }
        if d == key.bytes().len() {
            x.as_mut().map(|n| n.val = Some(val));
            return x
        }
        let c = key.as_bytes()[d] as usize;
        x.as_mut().map(|n| n.next[c] = Node::put(n.next[c].take(), key, val, d+1).take());
        x
    }

    fn get<'a>(x: Option<&'a Node<V>>, key: &str, d: usize) -> Option<&'a Node<V>> {
        if x.is_none() {
            None
        } else if d == key.bytes().len() {
            x
        } else {
            let c = key.as_bytes()[d] as usize;
            x.as_ref().map(|n| Node::get(n.next[c].as_ref(), key, d+1)).unwrap()
        }
    }
}

pub struct TrieST<V> {
    root: Option<Node<V>>,
    n: usize
}

impl<V> TrieST<V> {
    pub fn new() -> TrieST<V> {
        TrieST {
            root: None,
            n: 0
        }
    }

    pub fn put(&mut self, key: &str, val: V) {
        self.root = Node::put(self.root.take(), key, val, 0);
        self.n += 1;
    }

    pub fn get(&self, key: &str) -> Option<&V> {
        Node::get(self.root.as_ref(), key, 0).map(|n| n.val.as_ref()).unwrap_or(None)
    }

    pub fn size(&self) -> usize {
        self.n
    }
}

#[test]
fn test_tries() {
    let mut t = TrieST::new();
    assert_eq!(t.size(), 0);
    t.put("name", "Andelf");
    t.put("tel", "110");
    t.put("addr1", "Shaanxi");
    t.put("addr long", "Beijing Haidian");
    t.put("addr2", "Beijing");

    assert_eq!(t.size(), 5);
    assert_eq!(t.get("addr2"), Some(&"Beijing"));
    assert_eq!(t.get("non-exists-key"), None);
}
<commit_msg>tries: add TrieST::delete()/contains()<commit_after>const R: usize = 256;

/// 245-Way trie
pub struct Node<V> {
    val: Option<V>,
    next: Vec<Option<Node<V>>>
}

impl<V> Node<V> {
    fn new() -> Node<V> {
        Node {
            val: None,
            next: {
                let mut next = Vec::with_capacity(R);
                for _ in 0 .. R { next.push(None) }
                next
            }
        }
    }

    fn put(mut x: Option<Node<V>>, key: &str, val: V, d: usize) -> Option<Node<V>> {
        if x.is_none() {
            x = Some(Node::new());
        }
        if d == key.bytes().len() {
            x.as_mut().map(|n| n.val = Some(val));
            return x
        }
        let c = key.as_bytes()[d] as usize;
        x.as_mut().map(|n| n.next[c] = Node::put(n.next[c].take(), key, val, d+1));
        x
    }

    fn get<'a>(x: Option<&'a Node<V>>, key: &str, d: usize) -> Option<&'a Node<V>> {
        if x.is_none() {
            None
        } else if d == key.bytes().len() {
            x
        } else {
            let c = key.as_bytes()[d] as usize;
            x.as_ref().map(|n| Node::get(n.next[c].as_ref(), key, d+1)).unwrap()
        }
    }

    fn delete(mut x: Option<Node<V>>, key: &str, d: usize) -> (Option<Node<V>>, Option<V>) {
        let mut deleted = None;
        if x.is_none() {
            return (None, deleted);
        }
        if d == key.bytes().len() {
            if x.as_ref().map_or(false, |n| n.val.is_some()) {
                deleted = x.as_mut().map(|n| n.val.take()).unwrap();
            }
        } else {
            let c = key.as_bytes()[d] as usize;
            x.as_mut().map(|n| {
                // FIXME: https://github.com/rust-lang/rfcs/issues/372
                let (nx, nd) = Node::delete(n.next[c].take(), key, d+1);
                n.next[c] = nx;
                deleted = nd;
            });
        }
        if x.as_ref().map_or(false, |n| n.val.is_some()) {
            return (x, deleted);
        }
        for c in 0 .. R {
            if x.as_ref().map_or(false, |n| n.next[c].is_some()) {
                return (x, deleted);
            }
        }
        return (None, deleted);
    }
}

pub struct TrieST<V> {
    root: Option<Node<V>>,
    n: usize
}

impl<V> TrieST<V> {
    pub fn new() -> TrieST<V> {
        TrieST {
            root: None,
            n: 0
        }
    }

    pub fn put(&mut self, key: &str, val: V) {
        self.root = Node::put(self.root.take(), key, val, 0);
        self.n += 1;
    }

    pub fn get(&self, key: &str) -> Option<&V> {
        Node::get(self.root.as_ref(), key, 0).map(|n| n.val.as_ref()).unwrap_or(None)
    }

    pub fn delete(&mut self, key: &str) {
        let (root, deleted) = Node::delete(self.root.take(), key, 0);
        self.root = root;
        if deleted.is_some() {
            self.n -= 1;
        }
    }

    pub fn size(&self) -> usize {
        self.n
    }

    pub fn is_empty(&self) -> bool {
        self.n == 0
    }

    pub fn contains(&self, key: &str) -> bool {
        Node::get(self.root.as_ref(), key, 0).map(|n| n.val.is_some()).unwrap_or(false)
    }
}

#[test]
fn test_tries() {
    let mut t = TrieST::new();
    assert_eq!(t.size(), 0);
    t.put("name", "Andelf");
    t.put("tel", "110");
    t.put("addr1", "Shaanxi");
    t.put("addr long", "Beijing Haidian");
    t.put("addr2", "Beijing");

    assert_eq!(t.size(), 5);
    assert_eq!(t.get("addr2"), Some(&"Beijing"));
    assert_eq!(t.get("non-exists-key"), None);
    assert!(t.contains("tel"));
    t.delete("tel");
    assert_eq!(t.size(), 4);
    assert!(!t.contains("tel"));
}
<|endoftext|>


<commit_before>//! Core type for communicating a collection of `D: Data` records.
//!
//! `Message<D>` is meant to be treated as a `Vec<D>`, with the caveat that it may wrap either
//! typed `Vec<D>` data or binary `Vec<u8>` data that have not yet been deserialized. The type
//! implements `Deref` and `DerefMut` with `Target = Vec<D>`, whose implementations accommodate
//! the possibly serialized representation.
use timely_communication::{Serialize, Push};
use std::ops::{Deref, DerefMut};
use abomonation::{Abomonation, encode, decode, verify};

#[derive(Clone)]
pub struct Message<T, D> {
    pub time: T,
    pub data: Content<D>,
}

// Implementation required to get different behavior out of communication fabric.
impl<T: Abomonation+Clone, D: Abomonation> Serialize for Message<T, D> {
    fn into_bytes(&mut self, bytes: &mut Vec<u8>) {
        encode(&self.time, bytes);
        let vec: &Vec<D> = self.data.deref();
        encode(vec, bytes);
    }
    fn from_bytes(bytes: &mut Vec<u8>) -> Self {
        let mut bytes = ::std::mem::replace(bytes, Vec::new());
        let x_len = bytes.len();
        let (time, offset) = {
            let (t,r) = decode::<T>(&mut bytes).unwrap();
            let o = x_len - r.len();
            ((*t).clone(), o)
        };

        let length = decode::<Vec<D>>(&mut bytes[offset..]).unwrap().0.len();
        Message { time: time, data: Content::Bytes(bytes, offset, length) }
    }
}

#[derive(Clone)]
pub enum Content<D> {
    Bytes(Vec<u8>, usize, usize),  // bytes[offset..] decodes to length elements
    Typed(Vec<D>),
}

// ALLOC : This Drop implementation gets *very* angry if we drop allocated data.
// ALLOC : It probably shouldn't be used in practice, but should help track down who is being
// ALLOC : bad about respecting allocated memory.
// impl<D> Drop for Message<D> {
//     match self.contents {
//         Content::Bytes(bytes, _, _) => { assert!(bytes.capacity() == 0); }
//         Content::Typed(typed) => { assert!(typed.capacity() == 0); }
//     }
// }

impl<D> Content<D> {
    /// Default number of elements in a typed allocated message. This could vary as a function of
    /// `std::mem::size_of::<D>()`, so is left as a method rather than a constant.
    pub fn default_length() -> usize { 256 }

    /// The length of the underlying typed vector.
    ///
    /// The length is tracked without needing to deserialize the data, so that this method can be
    /// called even for `D` that do not implement `Serializable`.
    pub fn len(&self) -> usize {
        match *self {
            Content::Bytes(_, _, length) => length,
            Content::Typed(ref data) => data.len(),
        }
    }

    /// Constructs a `Message` from typed data, replacing its argument with `Vec::new()`.
    pub fn from_typed(typed: &mut Vec<D>) -> Content<D> {
        Content::Typed(::std::mem::replace(typed, Vec::new()))
    }

    /// Returns the typed vector, cleared, or a Vec::new() if the data are binary (and drops them
    /// on the floor, I guess! Ouch.
    /// ALLOC : dropping of binary data. likely called only by persons who pushed typed data on,
    /// ALLOC : so perhaps not all that common. Could put a panic! here just for fun! :D
    /// ALLOC : casual dropping of contents of `data`, which might have allocated memory.
    pub fn into_typed(self) -> Vec<D> {
        match self {
            Content::Bytes(_,_,_) => Vec::new(),
            Content::Typed(mut data) => { data.clear(); data },
        }
    }

    pub fn push_at<T, P: Push<(T, Content<D>)>>(buffer: &mut Vec<D>, time: T, pusher: &mut P) {

        let data = Content::from_typed(buffer);
        let mut message = Some((time, data));

        pusher.push(&mut message);

        if let Some((_, Content::Typed(mut typed))) = message {
            typed.clear();
            *buffer = typed;
        }
        else {
            *buffer = Vec::with_capacity(Content::<D>::default_length());
        }

        assert!(buffer.capacity() == Content::<D>::default_length());
    }
}

impl<D: Abomonation> Deref for Content<D> {
    type Target = Vec<D>;
    fn deref(&self) -> &Vec<D> {
        match *self {
            Content::Bytes(ref bytes, offset, _length) => {
                verify::<Vec<D>>(&bytes[offset..]).unwrap().0
            },
            Content::Typed(ref data) => data,
        }
    }
}

// TODO : Rather than .clone() the decoded data, we should try and re-rig serialization so that the
// TODO : underlying byte array can just be handed to Vec::from_raw_parts, cloning any owned data.
// TODO : I think we would need to make sure that the byte array had the right alignment, so that
// TODO : when the Vec is eventually dropped we don't de-allocate the wrong number of bytes.
// TODO : This requires mucking with the Abomonation code, as it doesn't currently let you step in
// TODO : and skip copying the 24 byte Vec struct first. We'd also have to bake in the typed length
// TODO : somewhere outside of this serialized hunk of data.
impl<D: Clone+Abomonation> DerefMut for Content<D> {
    fn deref_mut(&mut self) -> &mut Vec<D> {
        let value = if let Content::Bytes(ref mut bytes, offset, _length) = *self {
            let (data, _) = verify::<Vec<D>>(&bytes[offset..]).unwrap();
            // ALLOC : clone() will allocate a Vec<D> and maybe more.
            Some(Content::Typed((*data).clone()))
        }
        else { None };

        if let Some(contents) = value {
            *self = contents;
        }

        if let Content::Typed(ref mut data) = *self {
            data
        }
        else { unreachable!() }
    }
}
<commit_msg>default message size bumped<commit_after>//! Core type for communicating a collection of `D: Data` records.
//!
//! `Message<D>` is meant to be treated as a `Vec<D>`, with the caveat that it may wrap either
//! typed `Vec<D>` data or binary `Vec<u8>` data that have not yet been deserialized. The type
//! implements `Deref` and `DerefMut` with `Target = Vec<D>`, whose implementations accommodate
//! the possibly serialized representation.
use timely_communication::{Serialize, Push};
use std::ops::{Deref, DerefMut};
use abomonation::{Abomonation, encode, decode, verify};

#[derive(Clone)]
pub struct Message<T, D> {
    pub time: T,
    pub data: Content<D>,
}

// Implementation required to get different behavior out of communication fabric.
impl<T: Abomonation+Clone, D: Abomonation> Serialize for Message<T, D> {
    fn into_bytes(&mut self, bytes: &mut Vec<u8>) {
        encode(&self.time, bytes);
        let vec: &Vec<D> = self.data.deref();
        encode(vec, bytes);
    }
    fn from_bytes(bytes: &mut Vec<u8>) -> Self {
        let mut bytes = ::std::mem::replace(bytes, Vec::new());
        let x_len = bytes.len();
        let (time, offset) = {
            let (t,r) = decode::<T>(&mut bytes).unwrap();
            let o = x_len - r.len();
            ((*t).clone(), o)
        };

        let length = decode::<Vec<D>>(&mut bytes[offset..]).unwrap().0.len();
        Message { time: time, data: Content::Bytes(bytes, offset, length) }
    }
}

#[derive(Clone)]
pub enum Content<D> {
    Bytes(Vec<u8>, usize, usize),  // bytes[offset..] decodes to length elements
    Typed(Vec<D>),
}

// ALLOC : This Drop implementation gets *very* angry if we drop allocated data.
// ALLOC : It probably shouldn't be used in practice, but should help track down who is being
// ALLOC : bad about respecting allocated memory.
// impl<D> Drop for Message<D> {
//     match self.contents {
//         Content::Bytes(bytes, _, _) => { assert!(bytes.capacity() == 0); }
//         Content::Typed(typed) => { assert!(typed.capacity() == 0); }
//     }
// }

impl<D> Content<D> {
    /// Default number of elements in a typed allocated message. This could vary as a function of
    /// `std::mem::size_of::<D>()`, so is left as a method rather than a constant.
    pub fn default_length() -> usize { 4096 }

    /// The length of the underlying typed vector.
    ///
    /// The length is tracked without needing to deserialize the data, so that this method can be
    /// called even for `D` that do not implement `Serializable`.
    pub fn len(&self) -> usize {
        match *self {
            Content::Bytes(_, _, length) => length,
            Content::Typed(ref data) => data.len(),
        }
    }

    /// Constructs a `Message` from typed data, replacing its argument with `Vec::new()`.
    pub fn from_typed(typed: &mut Vec<D>) -> Content<D> {
        Content::Typed(::std::mem::replace(typed, Vec::new()))
    }

    /// Returns the typed vector, cleared, or a Vec::new() if the data are binary (and drops them
    /// on the floor, I guess! Ouch.
    /// ALLOC : dropping of binary data. likely called only by persons who pushed typed data on,
    /// ALLOC : so perhaps not all that common. Could put a panic! here just for fun! :D
    /// ALLOC : casual dropping of contents of `data`, which might have allocated memory.
    pub fn into_typed(self) -> Vec<D> {
        match self {
            Content::Bytes(_,_,_) => Vec::new(),
            Content::Typed(mut data) => { data.clear(); data },
        }
    }

    pub fn push_at<T, P: Push<(T, Content<D>)>>(buffer: &mut Vec<D>, time: T, pusher: &mut P) {

        let data = Content::from_typed(buffer);
        let mut message = Some((time, data));

        pusher.push(&mut message);

        if let Some((_, Content::Typed(mut typed))) = message {
            typed.clear();
            *buffer = typed;
        }
        else {
            *buffer = Vec::with_capacity(Content::<D>::default_length());
        }

        assert!(buffer.capacity() == Content::<D>::default_length());
    }
}

impl<D: Abomonation> Deref for Content<D> {
    type Target = Vec<D>;
    fn deref(&self) -> &Vec<D> {
        match *self {
            Content::Bytes(ref bytes, offset, _length) => {
                verify::<Vec<D>>(&bytes[offset..]).unwrap().0
            },
            Content::Typed(ref data) => data,
        }
    }
}

// TODO : Rather than .clone() the decoded data, we should try and re-rig serialization so that the
// TODO : underlying byte array can just be handed to Vec::from_raw_parts, cloning any owned data.
// TODO : I think we would need to make sure that the byte array had the right alignment, so that
// TODO : when the Vec is eventually dropped we don't de-allocate the wrong number of bytes.
// TODO : This requires mucking with the Abomonation code, as it doesn't currently let you step in
// TODO : and skip copying the 24 byte Vec struct first. We'd also have to bake in the typed length
// TODO : somewhere outside of this serialized hunk of data.
impl<D: Clone+Abomonation> DerefMut for Content<D> {
    fn deref_mut(&mut self) -> &mut Vec<D> {
        let value = if let Content::Bytes(ref mut bytes, offset, _length) = *self {
            let (data, _) = verify::<Vec<D>>(&bytes[offset..]).unwrap();
            // ALLOC : clone() will allocate a Vec<D> and maybe more.
            Some(Content::Typed((*data).clone()))
        }
        else { None };

        if let Some(contents) = value {
            *self = contents;
        }

        if let Content::Typed(ref mut data) = *self {
            data
        }
        else { unreachable!() }
    }
}
<|endoftext|>


<commit_before>use crate::os::raw::c_int;

mod fs;
pub mod sockets;
pub use self::fs::*;

pub const SOLID_BP_PROGRAM_EXITED: usize = 15;
pub const SOLID_BP_CSABORT: usize = 16;

#[inline(always)]
pub fn breakpoint_program_exited(tid: usize) {
    unsafe {
        match () {
            #[cfg(target_arch = "arm")]
            () => asm!("bkpt #{}", const SOLID_BP_PROGRAM_EXITED, in("r0") tid),
            #[cfg(target_arch = "aarch64")]
            () => asm!("hlt #{}", const SOLID_BP_PROGRAM_EXITED, in("x0") tid),
        }
    }
}

#[inline(always)]
pub fn breakpoint_abort() {
    unsafe {
        match () {
            #[cfg(target_arch = "arm")]
            () => asm!("bkpt #{}", const SOLID_BP_CSABORT),
            #[cfg(target_arch = "aarch64")]
            () => asm!("hlt #{}", const SOLID_BP_CSABORT),
        }
    }
}

// `solid_types.h`
pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID};

pub const SOLID_ERR_NOTFOUND: ER = -1000;
pub const SOLID_ERR_NOTSUPPORTED: ER = -1001;
pub const SOLID_ERR_EBADF: ER = -1002;
pub const SOLID_ERR_INVALIDCONTENT: ER = -1003;
pub const SOLID_ERR_NOTUSED: ER = -1004;
pub const SOLID_ERR_ALREADYUSED: ER = -1005;
pub const SOLID_ERR_OUTOFBOUND: ER = -1006;
pub const SOLID_ERR_BADSEQUENCE: ER = -1007;
pub const SOLID_ERR_UNKNOWNDEVICE: ER = -1008;
pub const SOLID_ERR_BUSY: ER = -1009;
pub const SOLID_ERR_TIMEOUT: ER = -1010;
pub const SOLID_ERR_INVALIDACCESS: ER = -1011;
pub const SOLID_ERR_NOTREADY: ER = -1012;

// `solid_rtc.h`
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SOLID_RTC_TIME {
    pub tm_sec: c_int,
    pub tm_min: c_int,
    pub tm_hour: c_int,
    pub tm_mday: c_int,
    pub tm_mon: c_int,
    pub tm_year: c_int,
    pub tm_wday: c_int,
}

extern "C" {
    pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
}

// `solid_log.h`
extern "C" {
    pub fn SOLID_LOG_write(s: *const u8, l: usize);
}

// `solid_mem.h`
extern "C" {
    pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
}

// `solid_rng.h`
extern "C" {
    pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
}

// `rwlock.h`
extern "C" {
    pub fn rwl_loc_rdl(id: ID) -> ER;
    pub fn rwl_loc_wrl(id: ID) -> ER;
    pub fn rwl_ploc_rdl(id: ID) -> ER;
    pub fn rwl_ploc_wrl(id: ID) -> ER;
    pub fn rwl_unl_rwl(id: ID) -> ER;
    pub fn rwl_acre_rwl() -> ER_ID;
    pub fn rwl_del_rwl(id: ID) -> ER;
}
<commit_msg>kmc-solid: Avoid the use of `asm_const`<commit_after>use crate::os::raw::c_int;

mod fs;
pub mod sockets;
pub use self::fs::*;

#[inline(always)]
pub fn breakpoint_program_exited(tid: usize) {
    unsafe {
        match () {
            // SOLID_BP_PROGRAM_EXITED = 15
            #[cfg(target_arch = "arm")]
            () => asm!("bkpt #15", in("r0") tid),
            #[cfg(target_arch = "aarch64")]
            () => asm!("hlt #15", in("x0") tid),
        }
    }
}

#[inline(always)]
pub fn breakpoint_abort() {
    unsafe {
        match () {
            // SOLID_BP_CSABORT = 16
            #[cfg(target_arch = "arm")]
            () => asm!("bkpt #16"),
            #[cfg(target_arch = "aarch64")]
            () => asm!("hlt #16"),
        }
    }
}

// `solid_types.h`
pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID};

pub const SOLID_ERR_NOTFOUND: ER = -1000;
pub const SOLID_ERR_NOTSUPPORTED: ER = -1001;
pub const SOLID_ERR_EBADF: ER = -1002;
pub const SOLID_ERR_INVALIDCONTENT: ER = -1003;
pub const SOLID_ERR_NOTUSED: ER = -1004;
pub const SOLID_ERR_ALREADYUSED: ER = -1005;
pub const SOLID_ERR_OUTOFBOUND: ER = -1006;
pub const SOLID_ERR_BADSEQUENCE: ER = -1007;
pub const SOLID_ERR_UNKNOWNDEVICE: ER = -1008;
pub const SOLID_ERR_BUSY: ER = -1009;
pub const SOLID_ERR_TIMEOUT: ER = -1010;
pub const SOLID_ERR_INVALIDACCESS: ER = -1011;
pub const SOLID_ERR_NOTREADY: ER = -1012;

// `solid_rtc.h`
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SOLID_RTC_TIME {
    pub tm_sec: c_int,
    pub tm_min: c_int,
    pub tm_hour: c_int,
    pub tm_mday: c_int,
    pub tm_mon: c_int,
    pub tm_year: c_int,
    pub tm_wday: c_int,
}

extern "C" {
    pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
}

// `solid_log.h`
extern "C" {
    pub fn SOLID_LOG_write(s: *const u8, l: usize);
}

// `solid_mem.h`
extern "C" {
    pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
}

// `solid_rng.h`
extern "C" {
    pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
}

// `rwlock.h`
extern "C" {
    pub fn rwl_loc_rdl(id: ID) -> ER;
    pub fn rwl_loc_wrl(id: ID) -> ER;
    pub fn rwl_ploc_rdl(id: ID) -> ER;
    pub fn rwl_ploc_wrl(id: ID) -> ER;
    pub fn rwl_unl_rwl(id: ID) -> ER;
    pub fn rwl_acre_rwl() -> ER_ID;
    pub fn rwl_del_rwl(id: ID) -> ER;
}
<|endoftext|>


<commit_before>//! Toolbox to make text layout easier.

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

mod lines_iterator;
mod reader;

pub use self::lines_iterator::{LinesIterator, Row};
pub use self::reader::ProgressReader;

/// The length and width of a part of a string.
pub struct Prefix {
    /// The length (in bytes) of the string.
    pub length: usize,
    /// The unicode-width of the string.
    pub width: usize
}

/// Computes the length (number of bytes) and width of a prefix that fits in the given `width`.
///
/// Takes non-breakable elements from `iter`, while keeping the
/// string width under `width` (and adding the length of `delimiter`
/// between each element).
///
/// Given `total_text = iter.collect().join(delimiter)`, the result
/// is the length of the longest prefix of `width` or less cells,
/// without breaking inside an element.
///
/// Example:
///
/// ```
/// # extern crate cursive;
/// extern crate unicode_segmentation;
/// use unicode_segmentation::UnicodeSegmentation;
///
/// # use cursive::utils::prefix;
/// # fn main() {
/// let my_text = "blah...";
/// // This returns the number of bytes for a prefix of `my_text` that
/// // fits within 5 cells.
/// prefix(my_text.graphemes(true), 5, "");
/// # }
/// ```
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Prefix
    where I: Iterator<Item = &'a str>
{
    let delimiter_width = delimiter.width();
    let delimiter_len = delimiter.len();

    let mut current_width = 0;
    let sum = iter.take_while(|token| {
            let needed_width = if current_width == 0 {
                token.width()
            } else {
                // We also need to insert the delimiter, if not at the beginning.
                token.width() + delimiter_width
            };
            if current_width + needed_width > available_width {
                false
            } else {
                current_width += needed_width;
                true
            }
        })
        .map(|token| token.len() + delimiter_len)
        .fold(0, |a, b| a + b);

    // We counted delimiter once too many times,
    // but only if the iterator was non empty.
    let length = sum.saturating_sub(delimiter_len);

    debug_assert!(current_width <= available_width);

    Prefix {
        length: length,
        width: current_width
    }
}

/// Computes the length (number of bytes) and width of a suffix that fits in the given `width`.
///
/// Doesn't break inside elements returned by `iter`.
///
/// Returns the number of bytes of the longest
/// suffix from `text` that fits in `width`.
///
/// This is a shortcut for `prefix_length(iter.rev(), width, delimiter)`
pub fn suffix<'a, I>(iter: I, width: usize, delimiter: &str) -> Prefix
    where I: DoubleEndedIterator<Item = &'a str>
{
    prefix(iter.rev(), width, delimiter)
}

/// Computes the length (number of bytes) and width of a suffix that fits in the given `width`.
///
/// Breaks between any two graphemes.
pub fn simple_suffix(text: &str, width: usize) -> Prefix {
    suffix(text.graphemes(true), width, "")
}
<commit_msg>Fix utils::prefix<commit_after>//! Toolbox to make text layout easier.

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

mod lines_iterator;
mod reader;

pub use self::lines_iterator::{LinesIterator, Row};
pub use self::reader::ProgressReader;

/// The length and width of a part of a string.
pub struct Prefix {
    /// The length (in bytes) of the string.
    pub length: usize,
    /// The unicode-width of the string.
    pub width: usize
}

/// Computes the length (number of bytes) and width of a prefix that fits in the given `width`.
///
/// Takes non-breakable elements from `iter`, while keeping the
/// string width under `width` (and adding the length of `delimiter`
/// between each element).
///
/// Given `total_text = iter.collect().join(delimiter)`, the result
/// is the length of the longest prefix of `width` or less cells,
/// without breaking inside an element.
///
/// Example:
///
/// ```
/// # extern crate cursive;
/// extern crate unicode_segmentation;
/// use unicode_segmentation::UnicodeSegmentation;
///
/// # use cursive::utils::prefix;
/// # fn main() {
/// let my_text = "blah...";
/// // This returns the number of bytes for a prefix of `my_text` that
/// // fits within 5 cells.
/// prefix(my_text.graphemes(true), 5, "");
/// # }
/// ```
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Prefix
    where I: Iterator<Item = &'a str>
{
    let delimiter_width = delimiter.width();
    let delimiter_len = delimiter.len();

    let mut current_width = 0;
    let sum = iter.take_while(|token| {
            let width = token.width();
            if current_width + width > available_width {
                false
            } else {
                current_width += width;
                current_width += delimiter_width;
                true
            }
        })
        .map(|token| token.len() + delimiter_len)
        .fold(0, |a, b| a + b);

    // We counted delimiter once too many times,
    // but only if the iterator was non empty.
    let length = sum.saturating_sub(delimiter_len);

    debug_assert!(current_width <= available_width + delimiter_width);

    Prefix {
        length: length,
        width: current_width
    }
}

/// Computes the length (number of bytes) and width of a suffix that fits in the given `width`.
///
/// Doesn't break inside elements returned by `iter`.
///
/// Returns the number of bytes of the longest
/// suffix from `text` that fits in `width`.
///
/// This is a shortcut for `prefix_length(iter.rev(), width, delimiter)`
pub fn suffix<'a, I>(iter: I, width: usize, delimiter: &str) -> Prefix
    where I: DoubleEndedIterator<Item = &'a str>
{
    prefix(iter.rev(), width, delimiter)
}

/// Computes the length (number of bytes) and width of a suffix that fits in the given `width`.
///
/// Breaks between any two graphemes.
pub fn simple_suffix(text: &str, width: usize) -> Prefix {
    suffix(text.graphemes(true), width, "")
}

#[cfg(test)]
mod tests {
    use utils;

    #[test]
    fn test_prefix() {
        assert_eq!(utils::prefix(" abra ".split(' '), 5, " ").length, 5);
        assert_eq!(utils::prefix("abra a".split(' '), 5, " ").length, 4);
        assert_eq!(utils::prefix("a a br".split(' '), 5, " ").length, 3);
    }
}
<|endoftext|>


<commit_before>//! lint on enum variants that are prefixed or suffixed by the same characters

use rustc::lint::*;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::symbol::InternedString;
use utils::{span_help_and_lint, span_lint};
use utils::{camel_case_from, camel_case_until, in_macro};

/// **What it does:** Detects enumeration variants that are prefixed or suffixed
/// by the same characters.
///
/// **Why is this bad?** Enumeration variant names should specify their variant,
/// not repeat the enumeration name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Cake {
///     BlackForestCake,
///     HummingbirdCake,
/// }
/// ```
declare_lint! {
    pub ENUM_VARIANT_NAMES,
    Warn,
    "enums where all variants share a prefix/postfix"
}

/// **What it does:** Detects enumeration variants that are prefixed or suffixed
/// by the same characters.
///
/// **Why is this bad?** Enumeration variant names should specify their variant,
/// not repeat the enumeration name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Cake {
///     BlackForestCake,
///     HummingbirdCake,
/// }
/// ```
declare_lint! {
    pub PUB_ENUM_VARIANT_NAMES,
    Allow,
    "enums where all variants share a prefix/postfix"
}

/// **What it does:** Detects type names that are prefixed or suffixed by the
/// containing module's name.
///
/// **Why is this bad?** It requires the user to type the module name twice.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// mod cake {
///     struct BlackForestCake;
/// }
/// ```
declare_lint! {
    pub STUTTER,
    Allow,
    "type names prefixed/postfixed with their containing module's name"
}

/// **What it does:** Checks for modules that have the same name as their
/// parent module
///
/// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and
/// again `mod foo { ..
/// }` in `foo.rs`.
/// The expectation is that items inside the inner `mod foo { .. }` are then
/// available
/// through `foo::x`, but they are only available through
/// `foo::foo::x`.
/// If this is done on purpose, it would be better to choose a more
/// representative module name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // lib.rs
/// mod foo;
/// // foo.rs
/// mod foo {
///     ...
/// }
/// ```
declare_lint! {
    pub MODULE_INCEPTION,
    Warn,
    "modules that have the same name as their parent module"
}

pub struct EnumVariantNames {
    modules: Vec<(InternedString, String)>,
    threshold: u64,
}

impl EnumVariantNames {
    pub fn new(threshold: u64) -> Self {
        Self {
            modules: Vec::new(),
            threshold: threshold,
        }
    }
}

impl LintPass for EnumVariantNames {
    fn get_lints(&self) -> LintArray {
        lint_array!(ENUM_VARIANT_NAMES, PUB_ENUM_VARIANT_NAMES, STUTTER, MODULE_INCEPTION)
    }
}

fn var2str(var: &Variant) -> InternedString {
    var.node.name.name.as_str()
}

/// Returns the number of chars that match from the start
fn partial_match(pre: &str, name: &str) -> usize {
    let mut name_iter = name.chars();
    let _ = name_iter.next_back(); // make sure the name is never fully matched
    pre.chars()
        .zip(name_iter)
        .take_while(|&(l, r)| l == r)
        .count()
}

/// Returns the number of chars that match from the end
fn partial_rmatch(post: &str, name: &str) -> usize {
    let mut name_iter = name.chars();
    let _ = name_iter.next(); // make sure the name is never fully matched
    post.chars()
        .rev()
        .zip(name_iter.rev())
        .take_while(|&(l, r)| l == r)
        .count()
}

// FIXME: #600
#[allow(while_let_on_iterator)]
fn check_variant(
    cx: &EarlyContext,
    threshold: u64,
    def: &EnumDef,
    item_name: &str,
    item_name_chars: usize,
    span: Span,
    lint: &'static Lint,
) {
    if (def.variants.len() as u64) < threshold {
        return;
    }
    for var in &def.variants {
        let name = var2str(var);
        if partial_match(item_name, &name) == item_name_chars
            && name.chars()
                .nth(item_name_chars)
                .map_or(false, |c| !c.is_lowercase())
        {
            span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
        }
        if partial_rmatch(item_name, &name) == item_name_chars {
            span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
        }
    }
    let first = var2str(&def.variants[0]);
    let mut pre = &first[..camel_case_until(&*first)];
    let mut post = &first[camel_case_from(&*first)..];
    for var in &def.variants {
        let name = var2str(var);

        let pre_match = partial_match(pre, &name);
        pre = &pre[..pre_match];
        let pre_camel = camel_case_until(pre);
        pre = &pre[..pre_camel];
        while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
            if next.is_lowercase() {
                let last = pre.len() - last.len_utf8();
                let last_camel = camel_case_until(&pre[..last]);
                pre = &pre[..last_camel];
            } else {
                break;
            }
        }

        let post_match = partial_rmatch(post, &name);
        let post_end = post.len() - post_match;
        post = &post[post_end..];
        let post_camel = camel_case_from(post);
        post = &post[post_camel..];
    }
    let (what, value) = match (pre.is_empty(), post.is_empty()) {
        (true, true) => return,
        (false, _) => ("pre", pre),
        (true, false) => ("post", post),
    };
    span_help_and_lint(
        cx,
        lint,
        span,
        &format!("All variants have the same {}fix: `{}`", what, value),
        &format!(
            "remove the {}fixes and use full paths to \
             the variants instead of glob imports",
            what
        ),
    );
}

fn to_camel_case(item_name: &str) -> String {
    let mut s = String::new();
    let mut up = true;
    for c in item_name.chars() {
        if c.is_uppercase() {
            // we only turn snake case text into CamelCase
            return item_name.to_string();
        }
        if c == '_' {
            up = true;
            continue;
        }
        if up {
            up = false;
            s.extend(c.to_uppercase());
        } else {
            s.push(c);
        }
    }
    s
}

impl EarlyLintPass for EnumVariantNames {
    fn check_item_post(&mut self, _cx: &EarlyContext, _item: &Item) {
        let last = self.modules.pop();
        assert!(last.is_some());
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
        let item_name = item.ident.name.as_str();
        let item_name_chars = item_name.chars().count();
        let item_camel = to_camel_case(&item_name);
        if !in_macro(item.span) {
            if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
                // constants don't have surrounding modules
                if !mod_camel.is_empty() {
                    if *mod_name == item_name {
                        if let ItemKind::Mod(..) = item.node {
                            span_lint(
                                cx,
                                MODULE_INCEPTION,
                                item.span,
                                "module has the same name as its containing module",
                            );
                        }
                    }
                    if item.vis == Visibility::Public {
                        let matching = partial_match(mod_camel, &item_camel);
                        let rmatching = partial_rmatch(mod_camel, &item_camel);
                        let nchars = mod_camel.chars().count();

                        let is_word_beginning = |c: char| {
                            c == '_' || c.is_uppercase() || c.is_numeric()
                        };

                        if matching == nchars {
                            match item_camel.chars().nth(nchars) {
                                Some(c) if is_word_beginning(c) =>
                                    span_lint(cx, STUTTER, item.span, "item name starts with its containing module's name"),
                                _ => ()
                            }
                        }
                        if rmatching == nchars {
                            span_lint(cx, STUTTER, item.span, "item name ends with its containing module's name");
                        }
                    }
                }
            }
        }
        if let ItemKind::Enum(ref def, _) = item.node {
            let lint = match item.vis {
                Visibility::Public => PUB_ENUM_VARIANT_NAMES,
                _ => ENUM_VARIANT_NAMES,
            };
            check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint);
        }
        self.modules.push((item_name, item_camel));
    }
}
<commit_msg>Update Rust to 063deba92e<commit_after>//! lint on enum variants that are prefixed or suffixed by the same characters

use rustc::lint::*;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::symbol::InternedString;
use utils::{span_help_and_lint, span_lint};
use utils::{camel_case_from, camel_case_until, in_macro};

/// **What it does:** Detects enumeration variants that are prefixed or suffixed
/// by the same characters.
///
/// **Why is this bad?** Enumeration variant names should specify their variant,
/// not repeat the enumeration name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Cake {
///     BlackForestCake,
///     HummingbirdCake,
/// }
/// ```
declare_lint! {
    pub ENUM_VARIANT_NAMES,
    Warn,
    "enums where all variants share a prefix/postfix"
}

/// **What it does:** Detects enumeration variants that are prefixed or suffixed
/// by the same characters.
///
/// **Why is this bad?** Enumeration variant names should specify their variant,
/// not repeat the enumeration name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Cake {
///     BlackForestCake,
///     HummingbirdCake,
/// }
/// ```
declare_lint! {
    pub PUB_ENUM_VARIANT_NAMES,
    Allow,
    "enums where all variants share a prefix/postfix"
}

/// **What it does:** Detects type names that are prefixed or suffixed by the
/// containing module's name.
///
/// **Why is this bad?** It requires the user to type the module name twice.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// mod cake {
///     struct BlackForestCake;
/// }
/// ```
declare_lint! {
    pub STUTTER,
    Allow,
    "type names prefixed/postfixed with their containing module's name"
}

/// **What it does:** Checks for modules that have the same name as their
/// parent module
///
/// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and
/// again `mod foo { ..
/// }` in `foo.rs`.
/// The expectation is that items inside the inner `mod foo { .. }` are then
/// available
/// through `foo::x`, but they are only available through
/// `foo::foo::x`.
/// If this is done on purpose, it would be better to choose a more
/// representative module name.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // lib.rs
/// mod foo;
/// // foo.rs
/// mod foo {
///     ...
/// }
/// ```
declare_lint! {
    pub MODULE_INCEPTION,
    Warn,
    "modules that have the same name as their parent module"
}

pub struct EnumVariantNames {
    modules: Vec<(InternedString, String)>,
    threshold: u64,
}

impl EnumVariantNames {
    pub fn new(threshold: u64) -> Self {
        Self {
            modules: Vec::new(),
            threshold: threshold,
        }
    }
}

impl LintPass for EnumVariantNames {
    fn get_lints(&self) -> LintArray {
        lint_array!(ENUM_VARIANT_NAMES, PUB_ENUM_VARIANT_NAMES, STUTTER, MODULE_INCEPTION)
    }
}

fn var2str(var: &Variant) -> InternedString {
    var.node.name.name.as_str()
}

/// Returns the number of chars that match from the start
fn partial_match(pre: &str, name: &str) -> usize {
    let mut name_iter = name.chars();
    let _ = name_iter.next_back(); // make sure the name is never fully matched
    pre.chars()
        .zip(name_iter)
        .take_while(|&(l, r)| l == r)
        .count()
}

/// Returns the number of chars that match from the end
fn partial_rmatch(post: &str, name: &str) -> usize {
    let mut name_iter = name.chars();
    let _ = name_iter.next(); // make sure the name is never fully matched
    post.chars()
        .rev()
        .zip(name_iter.rev())
        .take_while(|&(l, r)| l == r)
        .count()
}

// FIXME: #600
#[allow(while_let_on_iterator)]
fn check_variant(
    cx: &EarlyContext,
    threshold: u64,
    def: &EnumDef,
    item_name: &str,
    item_name_chars: usize,
    span: Span,
    lint: &'static Lint,
) {
    if (def.variants.len() as u64) < threshold {
        return;
    }
    for var in &def.variants {
        let name = var2str(var);
        if partial_match(item_name, &name) == item_name_chars
            && name.chars()
                .nth(item_name_chars)
                .map_or(false, |c| !c.is_lowercase())
        {
            span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
        }
        if partial_rmatch(item_name, &name) == item_name_chars {
            span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
        }
    }
    let first = var2str(&def.variants[0]);
    let mut pre = &first[..camel_case_until(&*first)];
    let mut post = &first[camel_case_from(&*first)..];
    for var in &def.variants {
        let name = var2str(var);

        let pre_match = partial_match(pre, &name);
        pre = &pre[..pre_match];
        let pre_camel = camel_case_until(pre);
        pre = &pre[..pre_camel];
        while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
            if next.is_lowercase() {
                let last = pre.len() - last.len_utf8();
                let last_camel = camel_case_until(&pre[..last]);
                pre = &pre[..last_camel];
            } else {
                break;
            }
        }

        let post_match = partial_rmatch(post, &name);
        let post_end = post.len() - post_match;
        post = &post[post_end..];
        let post_camel = camel_case_from(post);
        post = &post[post_camel..];
    }
    let (what, value) = match (pre.is_empty(), post.is_empty()) {
        (true, true) => return,
        (false, _) => ("pre", pre),
        (true, false) => ("post", post),
    };
    span_help_and_lint(
        cx,
        lint,
        span,
        &format!("All variants have the same {}fix: `{}`", what, value),
        &format!(
            "remove the {}fixes and use full paths to \
             the variants instead of glob imports",
            what
        ),
    );
}

fn to_camel_case(item_name: &str) -> String {
    let mut s = String::new();
    let mut up = true;
    for c in item_name.chars() {
        if c.is_uppercase() {
            // we only turn snake case text into CamelCase
            return item_name.to_string();
        }
        if c == '_' {
            up = true;
            continue;
        }
        if up {
            up = false;
            s.extend(c.to_uppercase());
        } else {
            s.push(c);
        }
    }
    s
}

impl EarlyLintPass for EnumVariantNames {
    fn check_item_post(&mut self, _cx: &EarlyContext, _item: &Item) {
        let last = self.modules.pop();
        assert!(last.is_some());
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
        let item_name = item.ident.name.as_str();
        let item_name_chars = item_name.chars().count();
        let item_camel = to_camel_case(&item_name);
        if !in_macro(item.span) {
            if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
                // constants don't have surrounding modules
                if !mod_camel.is_empty() {
                    if *mod_name == item_name {
                        if let ItemKind::Mod(..) = item.node {
                            span_lint(
                                cx,
                                MODULE_INCEPTION,
                                item.span,
                                "module has the same name as its containing module",
                            );
                        }
                    }
                    if item.vis.node == VisibilityKind::Public {
                        let matching = partial_match(mod_camel, &item_camel);
                        let rmatching = partial_rmatch(mod_camel, &item_camel);
                        let nchars = mod_camel.chars().count();

                        let is_word_beginning = |c: char| {
                            c == '_' || c.is_uppercase() || c.is_numeric()
                        };

                        if matching == nchars {
                            match item_camel.chars().nth(nchars) {
                                Some(c) if is_word_beginning(c) =>
                                    span_lint(cx, STUTTER, item.span, "item name starts with its containing module's name"),
                                _ => ()
                            }
                        }
                        if rmatching == nchars {
                            span_lint(cx, STUTTER, item.span, "item name ends with its containing module's name");
                        }
                    }
                }
            }
        }
        if let ItemKind::Enum(ref def, _) = item.node {
            let lint = match item.vis.node {
                VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES,
                _ => ENUM_VARIANT_NAMES,
            };
            check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint);
        }
        self.modules.push((item_name, item_camel));
    }
}
<|endoftext|>


<commit_before>
//! Kind of like a routing table for JSON endpoints

use crate::internal_prelude::works_everywhere::*;

type ResponderFn<'a, T, D> = Box<dyn Fn(&mut T, D) -> Result<s::ResponsePayload> + 'a + Sync + Send>;
type MethodHandler<'a, T> = Box<dyn Fn(&mut T, &[u8]) -> (JsonResponse, std::result::Result<(), FlowError>) + 'a + Sync + Send>;

#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] // clippy is broke
#[derive(Default)]
pub struct MethodRouter<'a, T> {
    handlers: HashMap<&'static str, MethodHandler<'a, T>>,
    method_names: Vec<&'static str>,
}

impl<'a, T> MethodRouter<'a, T> {
    #[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] // clippy is broke
    pub fn new() -> MethodRouter<'a, T> {
        MethodRouter {
            handlers: HashMap::new(),
            method_names: vec![],
        }
    }
    /// Returns the replaced MethodHandler if one already existed for that method.
    pub fn add(&mut self,
               method: &'static str,
               handler: MethodHandler<'a, T>)
               -> Option<MethodHandler<'a, T>> {
        self.method_names.push(method);
        self.handlers.insert(method, handler)
    }

    pub fn add_responder<D>(&mut self,
                            method: &'static str,
                            responder: ResponderFn<'a, T, D>)
                            -> Option<MethodHandler<'a, T>>
        where D: serde::de::DeserializeOwned,
              D: 'a,
              T: 'a
    {
        self.method_names.push(method);
        self.handlers.insert(method, create_handler_over_responder(responder))
    }

    pub fn list(&self) -> &[&str] {
        &self.method_names
    }

    /// Responds with an JsonResponse even for client errors
    ///
    pub fn invoke(&self,
                  upon: &mut T,
                  method: &str,
                  json_request_body: &[u8])
                  -> (JsonResponse, Result<()>) {
        match self.handlers.get(method) {
            Some(handler) => handler(upon as &mut T, json_request_body),
            None => (JsonResponse::method_not_understood(), Err(nerror!(ErrorKind::InvalidMessageEndpoint)))
        }
    }
}
pub fn create_handler_over_responder<'a, T, D>(responder: ResponderFn<'a, T, D>)
                                               -> MethodHandler<'a, T>
    where D: serde::de::DeserializeOwned,
          D: 'a,
          T: 'a
{

    Box::new(move |upon: &mut T, json_request_bytes: &[u8]| {

        let parsed_maybe: std::result::Result<D, serde_json::Error> =
            serde_json::from_slice(json_request_bytes);
        match parsed_maybe {
            Ok(parsed) => {
                let payload_maybe = responder(upon, parsed);
                match payload_maybe {
                    Ok(payload) => {
                        (JsonResponse::success_with_payload(payload), Ok(())) //How about failures with payloads!?
                    }
                    Err(error) => {
                        (JsonResponse::from_flow_error(&error), Err(error))
                    }
                }
            }
            Err(e) => {
                let e = FlowError::from_serde(e, json_request_bytes);
                (JsonResponse::from_flow_error(&e), Err(e))
            }
        }

    })

}

#[derive(Debug, Clone)]
pub struct JsonResponse {
    pub status_code: i64,
    pub response_json: Cow<'static, [u8]>,
}

impl JsonResponse {
    pub fn from_flow_error(err: &FlowError) -> JsonResponse{
        let message = format!("{}", err);
        JsonResponse::fail_with_message(i64::from(err.category().http_status_code()),
                                        &message)
    }

    pub fn from_response001(r: s::Response001) -> JsonResponse {
        JsonResponse {
            status_code: 400,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }
    pub fn success_with_payload(r: s::ResponsePayload) -> JsonResponse {
        let r = s::Response001 {
            success: true,
            code: 200,
            message: Some("OK".to_owned()),
            data: r,
        };
        JsonResponse {
            status_code: r.code,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }

    pub fn from_result(r: Result<s::ResponsePayload>) -> JsonResponse{
        match r {
            Ok(payload) => {
                JsonResponse::success_with_payload(payload) //How about failures with payloads!?
            }
            Err(error) => {
                JsonResponse::from_flow_error(&error)
            }
        }
    }

    pub fn status_2xx(&self) -> bool {
        self.status_code >= 200 && self.status_code < 300
    }
    pub fn assert_ok(&self) {
        if !self.status_2xx() {
            if let Ok(s) = std::str::from_utf8(self.response_json.as_ref()){
                if let Ok(s::Response001{ message, ..}) = serde_json::from_slice(self.response_json.as_ref()) {
                    if let Some(message) = message {
                        panic!("Json Status {}\n{}\n{}", self.status_code, &s, message);
                    }
                }
                panic!("Json Status {}\n{}", self.status_code, &s);

            }else{
                panic!("Json Status {} - payload invalid utf8", self.status_code);
            }
        }
    }
    pub fn unwrap_status200(&self) -> &JsonResponse {
        self.assert_ok();
        self
    }

    pub fn ok() -> JsonResponse {
        JsonResponse {
            status_code: 200,
            response_json: Cow::Borrowed(br#"{"success": "true","code": 200,"message": "OK"}"#),
        }
    }
    pub fn teapot() -> JsonResponse {
        JsonResponse {
            status_code: 418,
            response_json: /* HTTP 418 I'm a teapot per RFC 2324 */
            Cow::Borrowed(br#"{"success": "false","code": 418, "message": "I'm a little teapot, short and stout..."}"#)
        }
    }
    pub fn method_not_understood() -> JsonResponse {
        JsonResponse {
            status_code: 404,
            response_json: Cow::Borrowed(br#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Endpoint name not understood"}"#),
        }
    }

    pub fn fail_with_message(code: i64, message: &str) -> JsonResponse {
        let r = s::Response001 {
            success: false,
            code: code,
            message: Some(message.to_owned()),
            data: s::ResponsePayload::None,
        };
        JsonResponse {
            status_code: r.code,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }
}

// struct Meh{}
//
// #[derive(Deserialize,Clone)]
// struct Val{
//    i:i32
// }
//
// fn tryit(){
//    let mut m = Meh{};
//    let mut r = MethodRouter::new();
//    r.add("/api", create_handler_over_responder(
//       Box::new( move |upon: &mut Meh, v: Val|{
//           Ok(s::ResponsePayload::None)
//       })
//    ));
// }

// pub fn wrap<T,D>(responder: Box<Fn(&mut T, D) -> Vec<u8>>) -> Box<Fn(&mut T, &[u8]) -> Vec<u8>> where D: serde::Deserialize, D: 'static{
//    Box::new( move | upon: &mut T, json_request_bytes: & [u8] | {
//        responder(upon, serde_json::from_slice(json_request_bytes).unwrap())
//    })
// }
//
<commit_msg>Remove comment<commit_after>
//! Kind of like a routing table for JSON endpoints

use crate::internal_prelude::works_everywhere::*;

type ResponderFn<'a, T, D> = Box<dyn Fn(&mut T, D) -> Result<s::ResponsePayload> + 'a + Sync + Send>;
type MethodHandler<'a, T> = Box<dyn Fn(&mut T, &[u8]) -> (JsonResponse, std::result::Result<(), FlowError>) + 'a + Sync + Send>;

#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] // clippy is broke
#[derive(Default)]
pub struct MethodRouter<'a, T> {
    handlers: HashMap<&'static str, MethodHandler<'a, T>>,
    method_names: Vec<&'static str>,
}

impl<'a, T> MethodRouter<'a, T> {
    #[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] // clippy is broke
    pub fn new() -> MethodRouter<'a, T> {
        MethodRouter {
            handlers: HashMap::new(),
            method_names: vec![],
        }
    }
    /// Returns the replaced MethodHandler if one already existed for that method.
    pub fn add(&mut self,
               method: &'static str,
               handler: MethodHandler<'a, T>)
               -> Option<MethodHandler<'a, T>> {
        self.method_names.push(method);
        self.handlers.insert(method, handler)
    }

    pub fn add_responder<D>(&mut self,
                            method: &'static str,
                            responder: ResponderFn<'a, T, D>)
                            -> Option<MethodHandler<'a, T>>
        where D: serde::de::DeserializeOwned,
              D: 'a,
              T: 'a
    {
        self.method_names.push(method);
        self.handlers.insert(method, create_handler_over_responder(responder))
    }

    pub fn list(&self) -> &[&str] {
        &self.method_names
    }

    /// Responds with an JsonResponse even for client errors
    ///
    pub fn invoke(&self,
                  upon: &mut T,
                  method: &str,
                  json_request_body: &[u8])
                  -> (JsonResponse, Result<()>) {
        match self.handlers.get(method) {
            Some(handler) => handler(upon as &mut T, json_request_body),
            None => (JsonResponse::method_not_understood(), Err(nerror!(ErrorKind::InvalidMessageEndpoint)))
        }
    }
}
pub fn create_handler_over_responder<'a, T, D>(responder: ResponderFn<'a, T, D>)
                                               -> MethodHandler<'a, T>
    where D: serde::de::DeserializeOwned,
          D: 'a,
          T: 'a
{

    Box::new(move |upon: &mut T, json_request_bytes: &[u8]| {

        let parsed_maybe: std::result::Result<D, serde_json::Error> =
            serde_json::from_slice(json_request_bytes);
        match parsed_maybe {
            Ok(parsed) => {
                let payload_maybe = responder(upon, parsed);
                match payload_maybe {
                    Ok(payload) => {
                        (JsonResponse::success_with_payload(payload), Ok(())) //How about failures with payloads!?
                    }
                    Err(error) => {
                        (JsonResponse::from_flow_error(&error), Err(error))
                    }
                }
            }
            Err(e) => {
                let e = FlowError::from_serde(e, json_request_bytes);
                (JsonResponse::from_flow_error(&e), Err(e))
            }
        }

    })

}

#[derive(Debug, Clone)]
pub struct JsonResponse {
    pub status_code: i64,
    pub response_json: Cow<'static, [u8]>,
}

impl JsonResponse {
    pub fn from_flow_error(err: &FlowError) -> JsonResponse{
        let message = format!("{}", err);
        JsonResponse::fail_with_message(i64::from(err.category().http_status_code()),
                                        &message)
    }

    pub fn from_response001(r: s::Response001) -> JsonResponse {
        JsonResponse {
            status_code: 400,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }
    pub fn success_with_payload(r: s::ResponsePayload) -> JsonResponse {
        let r = s::Response001 {
            success: true,
            code: 200,
            message: Some("OK".to_owned()),
            data: r,
        };
        JsonResponse {
            status_code: r.code,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }

    pub fn from_result(r: Result<s::ResponsePayload>) -> JsonResponse{
        match r {
            Ok(payload) => {
                JsonResponse::success_with_payload(payload) //How about failures with payloads!?
            }
            Err(error) => {
                JsonResponse::from_flow_error(&error)
            }
        }
    }

    pub fn status_2xx(&self) -> bool {
        self.status_code >= 200 && self.status_code < 300
    }
    pub fn assert_ok(&self) {
        if !self.status_2xx() {
            if let Ok(s) = std::str::from_utf8(self.response_json.as_ref()){
                if let Ok(s::Response001{ message, ..}) = serde_json::from_slice(self.response_json.as_ref()) {
                    if let Some(message) = message {
                        panic!("Json Status {}\n{}\n{}", self.status_code, &s, message);
                    }
                }
                panic!("Json Status {}\n{}", self.status_code, &s);

            }else{
                panic!("Json Status {} - payload invalid utf8", self.status_code);
            }
        }
    }
    pub fn unwrap_status200(&self) -> &JsonResponse {
        self.assert_ok();
        self
    }

    pub fn ok() -> JsonResponse {
        JsonResponse {
            status_code: 200,
            response_json: Cow::Borrowed(br#"{"success": "true","code": 200,"message": "OK"}"#),
        }
    }
    pub fn teapot() -> JsonResponse {
        JsonResponse {
            status_code: 418,
            response_json: /* HTTP 418 I'm a teapot per RFC 2324 */
            Cow::Borrowed(br#"{"success": "false","code": 418, "message": "I'm a little teapot, short and stout..."}"#)
        }
    }
    pub fn method_not_understood() -> JsonResponse {
        JsonResponse {
            status_code: 404,
            response_json: Cow::Borrowed(br#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Endpoint name not understood"}"#),
        }
    }

    pub fn fail_with_message(code: i64, message: &str) -> JsonResponse {
        let r = s::Response001 {
            success: false,
            code,
            message: Some(message.to_owned()),
            data: s::ResponsePayload::None,
        };
        JsonResponse {
            status_code: r.code,
            response_json: Cow::Owned(serde_json::to_vec_pretty(&r).unwrap()),
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A middleware for authenticating requests based on
//! [JWT](https://jwt.io/introduction/).
//!
//! # The Auth Middleware
//!
//! Here _auth_ stands for _authentication_. A
//! [POST `/login`](https://github.com/fxbox/users/blob/master/doc/API.md#post-login)
//! request will authenticate a user. If so, a session JWT is returned
//! in the body of the response. This token must be sent with any further request to
//! keep track of the session.

use super::users_db::{ReadFilter, User, UsersDb};
use super::errors::*;

use crypto::sha2::Sha256;
use iron::{AroundMiddleware, Handler, headers, status};
use iron::method::Method;
use iron::prelude::*;
use jwt::{self, Error, Header, Token};

/// Structure representing [JWT claims section](https://jwt.io/introduction/).
///
/// Claims made by the authentication protocol includes `id` and `name` with
/// database unique id and username respectively.
#[derive(Default, RustcDecodable, RustcEncodable)]
pub struct SessionClaims{
    pub id: i32,
    pub name: String
}

/// Factory to create a session token `String` for a user in the database.
pub struct SessionToken;

impl SessionToken {
    pub fn for_user(user: &User) -> Result<String, Error> {
        let jwt_header: jwt::Header = Default::default();
        let claims = SessionClaims {
            id: user.id.unwrap(),
            name: user.name.to_owned()
        };
        let token = jwt::Token::new(jwt_header, claims);
        token.signed(
            user.secret.to_owned().as_bytes(),
            Sha256::new()
        )
    }
}

/// Represents an authorized endpoint.
///
/// When initializing [AuthMiddleware](./struct.AuthMiddleware.html) you need to
/// pass some routes to be authenticated, these are instances of `AuthEndpoint`.
///
/// `AuthEndpoints` take a vector of methods and a string representing the path
/// of the endpoint to be authenticated. This path can contain wildcard
/// parts. For example:
///
/// AuthEndpoint(vec![Method::Get, Method::Post], "/a/path/:foo/bar/:baz")
///
/// would match with a GET or POST request to /a/path/whatever/bar/whatever
#[derive(Debug)]
pub struct AuthEndpoint(pub Vec<Method>, pub String);

impl PartialEq for AuthEndpoint {
    fn eq(&self, other: &AuthEndpoint) -> bool {
        let AuthEndpoint(ref self_method, ref self_path) = *self;
        let AuthEndpoint(ref other_method, ref other_path) = *other;

        let self_path: Vec<&str> = if self_path.starts_with('/') {
            self_path[1..].split('/').collect()
        } else {
            self_path[0..].split('/').collect()
        };
        let other_path: Vec<&str> = if other_path.starts_with('/') {
            other_path[1..].split('/').collect()
        } else {
            other_path[0..].split('/').collect()
        };

        if self_path.len() != other_path.len() {
            return false;
        }

        let mut contains_method = false;
        for (_, method) in self_method.iter().enumerate() {
            if other_method.contains(method) {
                contains_method = true;
            }
        }

        if !contains_method {
            return false;
        }

        for (i, path) in self_path.iter().enumerate() {
            if &other_path[i] != path && !path.starts_with(':') {
                return false;
            }
        }
        true
    }
}

struct AuthHandler<H: Handler> {
    handler: H,
    auth_endpoints: Vec<AuthEndpoint>
}

impl<H: Handler> Handler for AuthHandler<H> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        {
            let endpoint = AuthEndpoint(vec![req.method.clone()],
                                        req.url.path.join("/"));
            // If this is not an authenticated endpoint, just proceed with the
            // original request.
            if !self.auth_endpoints.contains(&endpoint) {
                return self.handler.handle(req);
            }
        }
        // Otherwise, we need to verify the authorization header.
        match req.headers.get::<headers::Authorization<headers::Bearer>>() {
            Some(&headers::Authorization(headers::Bearer { ref token })) => {
                let token = match Token::<Header, SessionClaims>::parse(token) {
                    Ok(token) => token,
                    Err(_) => return EndpointError::with(status::Unauthorized, 401)
                };

                // To verify the token we need to get the secret associated to
                // user id contained in the token claim.
                let db = UsersDb::new();
                match db.read(ReadFilter::Id(token.claims.id)) {
                    Ok(users) => {
                        if users.len() != 1 {
                            return EndpointError::with(status::Unauthorized, 401)
                        }
                        if !token.verify(users[0].secret.to_owned().as_bytes(),
                                         Sha256::new()) {
                            return EndpointError::with(status::Unauthorized, 401)
                        }
                    },
                    Err(_) => return EndpointError::with(status::Unauthorized, 401)
                }
            },
            _ => return EndpointError::with(status::Unauthorized, 401)
        };
        self.handler.handle(req)
    }
}

/// Handle JWT authentication on specified endpoints.
///
/// # Examples
///
/// Before passing it as middleware, you should specify the authenticated
/// endpoints:
///
/// ```
/// extern crate iron;
/// extern crate foxbox_users;
///
/// fn main() {
///     use foxbox_users::users_router::UsersRouter;
///     use foxbox_users::auth_middleware::AuthMiddleware;
///     use iron::prelude::{Chain, Iron};
///
///     let router = UsersRouter::init();
///     let mut chain = Chain::new(router);
///     chain.around(AuthMiddleware{
///         auth_endpoints: vec![]
///     });
/// # if false {
///     Iron::new(chain).http("localhost:3000").unwrap();
/// # }
/// }
/// ```
pub struct AuthMiddleware {
    /// `Vec<AuthEndpoint>` containing the set of endpoints to be authenticated.
    pub auth_endpoints: Vec<AuthEndpoint>
}

impl AroundMiddleware for AuthMiddleware {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(AuthHandler {
            handler: handler,
            auth_endpoints: self.auth_endpoints
        }) as Box<Handler>
    }
}

#[cfg(test)]
describe! auth_middleware_tests {
    before_each {
        use iron::headers::Headers;
        use iron::prelude::*;
        use iron::method::Method;
        use iron::status::Status;
        use iron_test::request;
        use router::Router;

        fn not_implemented(_: &mut Request) -> IronResult<Response> {
            Ok(Response::with(Status::NotImplemented))
        }

        let mut router = Router::new();
        router.get("/authenticated", not_implemented);
        router.get("/authenticated/:foo/bar/:baz", not_implemented);
        router.delete("/authenticated", not_implemented);
        router.get("/not_authenticated", not_implemented);

        let mut chain = Chain::new(router);
        chain.around(AuthMiddleware {
            auth_endpoints: vec![
                AuthEndpoint(vec![Method::Get, Method::Delete],
                             "/authenticated".to_owned()),
                AuthEndpoint(vec![Method::Get],
                             "/authenticated/:foo/bar/:baz".to_owned())
            ]
        });
    }

    it "should allow request to not authenticated endpoint" {
        match request::get("http://localhost:3000/not_authenticated",
                           Headers::new(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        }
    }

    it "should reject request to authenticated endpoint" {
        match request::get("http://localhost:3000/authenticated",
                           Headers::new(), &chain) {
            Ok(_) => assert!(false),
            Err(err) => {
                assert_eq!(err.response.status.unwrap(), Status::Unauthorized)
            }
        }
    }

    it "should allow request to authenticated endpoint" {
        use super::super::users_db::{UserBuilder, UsersDb};

        use iron::headers::{Authorization, Bearer};
        use crypto::sha2::Sha256;
        use jwt;

        let db = UsersDb::new();
        db.clear().ok();
        let user = UserBuilder::new()
            .id(1).name(String::from("username"))
            .password(String::from("password"))
            .email(String::from("username@example.com"))
            .secret(String::from("secret"))
            .finalize().unwrap();
        db.create(&user).ok();
        let jwt_header: jwt::Header = Default::default();
        let claims = SessionClaims {
            id: user.id.unwrap(),
            name: user.name.to_owned()
        };
        let token = jwt::Token::new(jwt_header, claims);
        let signed = token.signed(
            user.secret.to_owned().as_bytes(),
            Sha256::new()
        ).ok().unwrap();
        let mut headers = Headers::new();
        headers.set(Authorization(Bearer { token: signed.to_owned() }));
        match request::get("http://localhost:3000/authenticated",
                           headers.clone(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        };
        match request::get("http://localhost:3000/authenticated/afoo/bar/baz",
                           headers.clone(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        };
        match request::delete("http://localhost:3000/authenticated",
                              headers, &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        }

    }
}
<commit_msg>Fix check for endpoints with wildcards on auth middleware. r=arcturus<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A middleware for authenticating requests based on
//! [JWT](https://jwt.io/introduction/).
//!
//! # The Auth Middleware
//!
//! Here _auth_ stands for _authentication_. A
//! [POST `/login`](https://github.com/fxbox/users/blob/master/doc/API.md#post-login)
//! request will authenticate a user. If so, a session JWT is returned
//! in the body of the response. This token must be sent with any further request to
//! keep track of the session.

use super::users_db::{ReadFilter, User, UsersDb};
use super::errors::*;

use crypto::sha2::Sha256;
use iron::{AroundMiddleware, Handler, headers, status};
use iron::method::Method;
use iron::prelude::*;
use jwt::{self, Error, Header, Token};

/// Structure representing [JWT claims section](https://jwt.io/introduction/).
///
/// Claims made by the authentication protocol includes `id` and `name` with
/// database unique id and username respectively.
#[derive(Default, RustcDecodable, RustcEncodable)]
pub struct SessionClaims{
    pub id: i32,
    pub name: String
}

/// Factory to create a session token `String` for a user in the database.
pub struct SessionToken;

impl SessionToken {
    pub fn for_user(user: &User) -> Result<String, Error> {
        let jwt_header: jwt::Header = Default::default();
        let claims = SessionClaims {
            id: user.id.unwrap(),
            name: user.name.to_owned()
        };
        let token = jwt::Token::new(jwt_header, claims);
        token.signed(
            user.secret.to_owned().as_bytes(),
            Sha256::new()
        )
    }
}

/// Represents an authorized endpoint.
///
/// When initializing [AuthMiddleware](./struct.AuthMiddleware.html) you need to
/// pass some routes to be authenticated, these are instances of `AuthEndpoint`.
///
/// `AuthEndpoints` take a vector of methods and a string representing the path
/// of the endpoint to be authenticated. This path can contain wildcard
/// parts. For example:
///
/// AuthEndpoint(vec![Method::Get, Method::Post], "/a/path/:foo/bar/:baz")
///
/// would match with a GET or POST request to /a/path/whatever/bar/whatever
#[derive(Debug)]
pub struct AuthEndpoint(pub Vec<Method>, pub String);

impl PartialEq for AuthEndpoint {
    fn eq(&self, other: &AuthEndpoint) -> bool {
        let AuthEndpoint(ref self_method, ref self_path) = *self;
        let AuthEndpoint(ref other_method, ref other_path) = *other;

        let self_path: Vec<&str> = if self_path.starts_with('/') {
            self_path[1..].split('/').collect()
        } else {
            self_path[0..].split('/').collect()
        };
        let other_path: Vec<&str> = if other_path.starts_with('/') {
            other_path[1..].split('/').collect()
        } else {
            other_path[0..].split('/').collect()
        };

        if self_path.len() != other_path.len() {
            return false;
        }

        let mut contains_method = false;
        for (_, method) in self_method.iter().enumerate() {
            if other_method.contains(method) {
                contains_method = true;
            }
        }

        if !contains_method {
            return false;
        }

        for (i, path) in self_path.iter().enumerate() {
            if &other_path[i] != path && !other_path[i].starts_with(':') {
                return false;
            }
        }
        true
    }
}

struct AuthHandler<H: Handler> {
    handler: H,
    auth_endpoints: Vec<AuthEndpoint>
}

impl<H: Handler> Handler for AuthHandler<H> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        {
            let endpoint = AuthEndpoint(vec![req.method.clone()],
                                        req.url.path.join("/"));
            // If this is not an authenticated endpoint, just proceed with the
            // original request.
            if !self.auth_endpoints.contains(&endpoint) {
                return self.handler.handle(req);
            }
        }
        // Otherwise, we need to verify the authorization header.
        match req.headers.get::<headers::Authorization<headers::Bearer>>() {
            Some(&headers::Authorization(headers::Bearer { ref token })) => {
                let token = match Token::<Header, SessionClaims>::parse(token) {
                    Ok(token) => token,
                    Err(_) => return EndpointError::with(status::Unauthorized, 401)
                };

                // To verify the token we need to get the secret associated to
                // user id contained in the token claim.
                let db = UsersDb::new();
                match db.read(ReadFilter::Id(token.claims.id)) {
                    Ok(users) => {
                        if users.len() != 1 {
                            return EndpointError::with(status::Unauthorized, 401)
                        }
                        if !token.verify(users[0].secret.to_owned().as_bytes(),
                                         Sha256::new()) {
                            return EndpointError::with(status::Unauthorized, 401)
                        }
                    },
                    Err(_) => return EndpointError::with(status::Unauthorized, 401)
                }
            },
            _ => return EndpointError::with(status::Unauthorized, 401)
        };
        self.handler.handle(req)
    }
}

/// Handle JWT authentication on specified endpoints.
///
/// # Examples
///
/// Before passing it as middleware, you should specify the authenticated
/// endpoints:
///
/// ```
/// extern crate iron;
/// extern crate foxbox_users;
///
/// fn main() {
///     use foxbox_users::users_router::UsersRouter;
///     use foxbox_users::auth_middleware::AuthMiddleware;
///     use iron::prelude::{Chain, Iron};
///
///     let router = UsersRouter::init();
///     let mut chain = Chain::new(router);
///     chain.around(AuthMiddleware{
///         auth_endpoints: vec![]
///     });
/// # if false {
///     Iron::new(chain).http("localhost:3000").unwrap();
/// # }
/// }
/// ```
pub struct AuthMiddleware {
    /// `Vec<AuthEndpoint>` containing the set of endpoints to be authenticated.
    pub auth_endpoints: Vec<AuthEndpoint>
}

impl AroundMiddleware for AuthMiddleware {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(AuthHandler {
            handler: handler,
            auth_endpoints: self.auth_endpoints
        }) as Box<Handler>
    }
}

#[cfg(test)]
describe! auth_middleware_tests {
    before_each {
        use iron::headers::Headers;
        use iron::prelude::*;
        use iron::method::Method;
        use iron::status::Status;
        use iron_test::request;
        use router::Router;

        fn not_implemented(_: &mut Request) -> IronResult<Response> {
            Ok(Response::with(Status::NotImplemented))
        }

        let mut router = Router::new();
        router.get("/authenticated", not_implemented);
        router.get("/authenticated/:foo/bar/:baz", not_implemented);
        router.delete("/authenticated", not_implemented);
        router.get("/not_authenticated", not_implemented);

        let mut chain = Chain::new(router);
        chain.around(AuthMiddleware {
            auth_endpoints: vec![
                AuthEndpoint(vec![Method::Get, Method::Delete],
                             "/authenticated".to_owned()),
                AuthEndpoint(vec![Method::Get],
                             "/authenticated/:foo/bar/:baz".to_owned())
            ]
        });
    }

    it "should allow request to not authenticated endpoint" {
        match request::get("http://localhost:3000/not_authenticated",
                           Headers::new(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        }
    }

    it "should reject request to authenticated endpoint" {
        match request::get("http://localhost:3000/authenticated",
                           Headers::new(), &chain) {
            Ok(_) => assert!(false),
            Err(err) => {
                assert_eq!(err.response.status.unwrap(), Status::Unauthorized)
            }
        };
        match request::get("http://localhost:3000/authenticated/afoo/bar/abaz",
                           Headers::new(), &chain) {
            Ok(_) => assert!(false),
            Err(err) => {
                assert_eq!(err.response.status.unwrap(), Status::Unauthorized)
            }
        };
        match request::delete("http://localhost:3000/authenticated",
                              Headers::new(), &chain) {
            Ok(_) => assert!(false),
            Err(err) => {
                assert_eq!(err.response.status.unwrap(), Status::Unauthorized)
            }
        }
    }

    it "should allow request to authenticated endpoint" {
        use super::super::users_db::{UserBuilder, UsersDb};

        use iron::headers::{Authorization, Bearer};
        use crypto::sha2::Sha256;
        use jwt;

        let db = UsersDb::new();
        db.clear().ok();
        let user = UserBuilder::new()
            .id(1).name(String::from("username"))
            .password(String::from("password"))
            .email(String::from("username@example.com"))
            .secret(String::from("secret"))
            .finalize().unwrap();
        db.create(&user).ok();
        let jwt_header: jwt::Header = Default::default();
        let claims = SessionClaims {
            id: user.id.unwrap(),
            name: user.name.to_owned()
        };
        let token = jwt::Token::new(jwt_header, claims);
        let signed = token.signed(
            user.secret.to_owned().as_bytes(),
            Sha256::new()
        ).ok().unwrap();
        let mut headers = Headers::new();
        headers.set(Authorization(Bearer { token: signed.to_owned() }));
        match request::get("http://localhost:3000/authenticated",
                           headers.clone(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        };
        match request::get("http://localhost:3000/authenticated/afoo/bar/abaz",
                           headers.clone(), &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        };
        match request::delete("http://localhost:3000/authenticated",
                              headers, &chain) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::NotImplemented)
            },
            Err(_) => assert!(false)
        }
    }
}
<|endoftext|>