<commit_before>#![feature(plugin)]
#![plugin(clippy)]

#[macro_use]
extern crate nom;

use std::str;
use std::str::FromStr;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub struct Sexp {
    pub element:Element,
    pub meta:Meta,
}

pub struct Compact<'a> {
    what:&'a Sexp,
}

pub fn compact(e:&Sexp) -> Compact {
    Compact { what:e }
}

#[derive(Debug, Clone)]
pub enum Element {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

#[derive(Debug, Clone)]
pub struct Meta {
    indent:String,
    nl:usize,
    after_list:String,
}

pub type ERes<T> = Result<T, String>;

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp { element:Element::Empty, meta:Meta { indent:String::from(""), nl:0, after_list:String::from(""), } }
    }

    pub fn new(element:Element, indent:String, nl:usize, after_list:String) -> Sexp {
        Sexp { element:element, meta:Meta { indent:indent, nl:nl, after_list:after_list, }, }
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> ERes<&Vec<Sexp> > {
        match self.element {
            Element::List(ref v) => Ok(v),
            _ => Err(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> ERes<&String> {
        match self.element {
            Element::String(ref s) => Ok(s),
            _ => Err(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> ERes<f64> {
        let s = try!(self.string());
        match f64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> ERes<i64> {
        let s = try!(self.string());
        match i64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> ERes<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> ERes<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return Err(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> ERes<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> ERes<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> ERes<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return Err(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{}", self.meta.indent));
        try!(match self.element {
            Element::String(ref s) => {
                if s.contains("(") || s.contains(" ") {
                    write!(f,"\"{}\"", s)
                } else {
                    write!(f,"{}", s)
                }
            },
            Element::List(ref v) => {
                try!(write!(f, "("));
                let mut prev_nl = 0;
                for (i, x) in v.iter().enumerate() {
                    let s = if i == 0 || x.meta.indent.len() > 0 || prev_nl > 0 { "" } else { " " };
                    try!(write!(f, "{}{}", s, x));
                    prev_nl = x.meta.nl;
                }
                write!(f, "{})", self.meta.after_list)
            },
            Element::Empty => Ok(())
        });
        for _ in 0..self.meta.nl {
            try!(writeln!(f,""));
        }
        Ok(())
    }
}

impl<'a> fmt::Display for Compact<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.what.element {
            Element::String(ref s) => {
                if s.contains('(') || s.contains(' ') {
                    write!(f,"\"{}\"", s)
                } else {
                    write!(f,"{}", s)
                }
            },
            Element::List(ref v) => {
                try!(write!(f, "("));
                for (i, x) in v.iter().enumerate() {
                    let s = if i == 0 { "" } else { " " };
                    try!(write!(f, "{}{}", s, compact(x)));
                }
                write!(f, ")")
            },
            Element::Empty => Ok(())
        }
    }
}

pub fn display_string(s:&str) -> String {
    if s.contains('(') || s.contains(' ') || s.is_empty() {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

pub fn parse_str(sexp: &str) -> Result<Sexp, String> {
    if sexp.is_empty() {
        return Ok(Sexp::new_empty())
    }
    match parse_sexp(&sexp.as_bytes()[..]) {
        nom::IResult::Done(_, c) => Ok(c),
        nom::IResult::Error(err) => {
            match err {
                nom::Err::Position(kind,p) =>
                    Err(format!("parse error: {:?} |{}|", kind, str::from_utf8(p).unwrap())),
                _ => Err("parse error".to_string())
            }
        },
        nom::IResult::Incomplete(x) => Err(format!("incomplete: {:?}", x)),
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("{:?}", x))
    });
    parse_str(&s[..])
}

named!(parse_qstring<String>,
       map_res!(
           map_res!(
               delimited!(char!('\"'), is_not!("\""), char!('\"')),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_bare_string<String>,
       map_res!(
           map_res!(
               is_not!(b")( \r\n"),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_string<(Element,Option<&[u8]>) >,
       map!(alt!(parse_qstring | parse_bare_string), |x| (Element::String(x), None))
       );

named!(parse_list<(Element,Option<&[u8]>) >,
       chain!(
           char!('(') ~
               v: many0!(parse_sexp) ~
               after_list: opt!(nom::multispace) ~ // sometimes there is space after a closing bracket, this would not be caught by parse_sexp
               char!(')'),
           || (Element::List(v), after_list) )
       );

// TODO: consider lines with just spaces and a nl as also nl
named!(line_ending<usize>,
       chain!(
           opt!(nom::space) ~
               c: opt!(is_a!(b"\r\n"))
               , || match c { None => 0, Some(ref x) => x.len(), }
               )
       );

named!(parse_sexp<Sexp>,
           chain!(
               indent: opt!(nom::space) ~
                   s_after_list: alt!(parse_list | parse_string) ~
                   nl: line_ending
                   ,
               || {
                   let s = s_after_list.0.clone();
                   let after_list = s_after_list.1;
                   let indent = match indent {
                       None => String::new(),
                       Some(x) => String::from(str::from_utf8(x).unwrap()),
                   };
                   let after_list = match after_list {
                       None => String::new(),
                       Some(x) => String::from(str::from_utf8(x).unwrap()),
                   };
                   Sexp::new(s, indent, nl, after_list)
               })
       );

// internal tests
#[test]
fn test_qstring1() {
    let x = parse_string(&b"\"hello world\""[..]);
    match x {
        nom::IResult::Done(_,y) => {
            let (e, _) = y;
            match e {
                Element::String(f) => assert_eq!(String::from("hello world"), f),
                _ => panic!("not string"),
            }
        },
        _ => panic!("parser not done"),
    }
}

/*
#[test]
#[should_panic(expected="assertion failed: `(left == right)` (left: `Incomplete(Size(1))`, right: `Done([], \"hello\")`)")]
fn test_qstring2() {
    parse_string(&b"\"hello"[..]);
}
*/

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

    #[allow(dead_code)]
    fn check_parse_res(s: &str, o:&str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(o, t)
    }
    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t)
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }
    #[allow(dead_code)]
    fn check_compact(s: &str, o:&str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", compact(&e));
        assert_eq!(o, t)
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_empty_qstring() { check_parse("(hello \"\")") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring_a() { check_parse_res("\"hello\"", "hello") }

    #[test]
    fn test_qstring_a2() { check_parse("\"hello world\"") }

    #[test]
    fn test_qstring_a3() { check_parse("\"hello(world)\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse_res("(\"world\")", "(world)") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    fn test_number_string() { check_parse("567A_WZ") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(2)\"")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"parse error: Alt |)|\"")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="incomplete: Size")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }

    #[test]
    fn test_multiline() {
        check_parse("\
(hello \"test it\"
    (foo bar)
    (mars venus)
)
")
    }

    #[test]
    fn test_multiline2() {
        check_parse("\
(hello world
  mad
    (world)
  not)")
    }

    #[test]
    fn test_multiline_two_empty() {
        check_parse("\
(hello

world)")
    }

    #[test]
    fn test_compact1() {
        check_compact("( hello
world \"foo
  bar\"
     (baz)
)", "(hello world \"foo
  bar\" (baz))")
    }

    #[test]
    fn test_fail_pcb() {
        check_parse("\
(kicad_pcb (version 4) (host pcbnew \"(2015-05-31 BZR 5692)-product\")
  (general
  )
)")
    }
}

<commit_msg>simplify (remove indentation stuff...)<commit_after>#![feature(plugin)]
#![plugin(clippy)]

#[macro_use]
extern crate nom;

use std::str;
use std::str::FromStr;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

pub type ERes<T> = Result<T, String>;

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp::Empty
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> ERes<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => Err(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> ERes<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            _ => Err(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> ERes<f64> {
        let s = try!(self.string());
        match f64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> ERes<i64> {
        let s = try!(self.string());
        match i64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> ERes<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> ERes<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return Err(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> ERes<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> ERes<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> ERes<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return Err(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                if s.contains('(') || s.contains(' ') {
                    write!(f,"\"{}\"", s)
                } else {
                    write!(f,"{}", s)
                }
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                for x in v {
                    try!(write!(f, "{}", x));
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}

pub fn display_string(s:&str) -> String {
    if s.contains('(') || s.contains(' ') || s.is_empty() {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

pub fn parse_str(sexp: &str) -> Result<Sexp, String> {
    if sexp.is_empty() {
        return Ok(Sexp::new_empty())
    }
    match parse_sexp(&sexp.as_bytes()[..]) {
        nom::IResult::Done(_, c) => Ok(c),
        nom::IResult::Error(err) => {
            match err {
                nom::Err::Position(kind,p) =>
                    Err(format!("parse error: {:?} |{}|", kind, str::from_utf8(p).unwrap())),
                _ => Err("parse error".to_string())
            }
        },
        nom::IResult::Incomplete(x) => Err(format!("incomplete: {:?}", x)),
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("{:?}", x))
    });
    parse_str(&s[..])
}

named!(parse_qstring<String>,
       map_res!(
           map_res!(
               delimited!(char!('\"'), is_not!("\""), char!('\"')),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_bare_string<String>,
       map_res!(
           map_res!(
               is_not!(b")( \r\n"),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_string<Sexp>,
       map!(alt!(parse_qstring | parse_bare_string), |x| Sexp::String(x))
       );

named!(parse_list<Sexp>,
       chain!(
           char!('(') ~
               v: many0!(parse_sexp) ~
               _space: opt!(nom::multispace) ~ // sometimes there is space after a closing bracket, this would not be caught by parse_sexp
               char!(')'),
           || Sexp::List(v) )
       );

// TODO: consider lines with just spaces and a nl as also nl
named!(line_ending<usize>,
       chain!(
           opt!(nom::space) ~
               c: opt!(is_a!(b"\r\n"))
               , || match c { None => 0, Some(ref x) => x.len(), }
               )
       );

named!(parse_sexp<Sexp>,
           chain!(
               _indent: opt!(nom::space) ~
                   sexp: alt!(parse_list | parse_string) ~
                   _nl: line_ending
                   ,
               || {
                   sexp
               })
       );

// internal tests
#[test]
fn test_qstring1() {
    let x = parse_string(&b"\"hello world\""[..]);
    match x {
        nom::IResult::Done(_,y) => {
            match y {
                Sexp::String(f) => assert_eq!(String::from("hello world"), f),
                _ => panic!("not string"),
            }
        },
        _ => panic!("parser not done"),
    }
}

/*
#[test]
#[should_panic(expected="assertion failed: `(left == right)` (left: `Incomplete(Size(1))`, right: `Done([], \"hello\")`)")]
fn test_qstring2() {
    parse_string(&b"\"hello"[..]);
}
*/

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

    #[allow(dead_code)]
    fn check_parse_res(s: &str, o:&str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(o, t)
    }
    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t)
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_empty_qstring() { check_parse("(hello \"\")") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring_a() { check_parse_res("\"hello\"", "hello") }

    #[test]
    fn test_qstring_a2() { check_parse("\"hello world\"") }

    #[test]
    fn test_qstring_a3() { check_parse("\"hello(world)\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse_res("(\"world\")", "(world)") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    fn test_number_string() { check_parse("567A_WZ") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(2)\"")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"parse error: Alt |)|\"")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="incomplete: Size")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }

    #[test]
    fn test_multiline() {
        check_parse("\
(hello \"test it\"
    (foo bar)
    (mars venus)
)
")
    }

    #[test]
    fn test_multiline2() {
        check_parse("\
(hello world
  mad
    (world)
  not)")
    }

    #[test]
    fn test_multiline_two_empty() {
        check_parse("\
(hello

world)")
    }

    #[test]
    fn test_fail_pcb() {
        check_parse("\
(kicad_pcb (version 4) (host pcbnew \"(2015-05-31 BZR 5692)-product\")
  (general
  )
)")
    }
}

<|endoftext|>


<commit_before>#![feature(quote, plugin_registrar, rustc_private, plugin, convert)]
#![allow(unused_variables, unused_imports)]

extern crate syntax;
extern crate rustc;
extern crate rand;

use syntax::ext::base::{ItemModifier, ExtCtxt, Modifier};
use syntax::codemap::{Span, Spanned};
use syntax::ptr::P;
use syntax::ast::{Item, MetaItem, Expr, ExprLit, ExprIf, ExprWhile, ExprUnary, UnNot, Lit, LitBool, LitStr};
use syntax::parse::token::intern;
use syntax::fold::{Folder};
use rustc::plugin::Registry;

use rand::{thread_rng, Rng};

struct HeckleExpander;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let expander = Modifier(Box::new(HeckleExpander));
    reg.register_syntax_extension(intern("heckle"), expander);
}

impl ItemModifier for HeckleExpander {
    fn expand(&self, ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, item: P<Item>) -> P<Item> {
        let name = option_env!("HECKLE_MUTATION").unwrap_or("invert_boolean");
        match name {
            "invert_boolean" => {
                let mut mutation = InvertBooleanMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "random_string" => {
                let mut mutation = RandomStringMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "invert_while_cond" => {
                let mut mutation = InvertWhileExprCondMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "invert_if_cond" => {
                let mut mutation = InvertIfExprCondMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            _ => item
        }
    }
}

trait Mutation<'a, 'b:'a> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self;
}

struct InvertBooleanMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for InvertBooleanMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertBooleanMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b> InvertBooleanMutation<'a, 'b> {
    fn invert_boolean(&self, expr: P<Expr>) -> P<Expr> {
        match (*expr).node {
            ExprLit(ref spanned) => match spanned.node {
                LitBool(value) => quote_expr!(self.ecx, !$value),
                _ => quote_expr!(self.ecx, $expr)
            },

            _ => quote_expr!(self.ecx, $expr)
       }
    }
}

impl<'a, 'b> Folder for InvertBooleanMutation<'a, 'b> {
    fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
        self.invert_boolean(e)
    }
}

struct InvertIfExprCondMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for InvertIfExprCondMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertIfExprCondMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b:'a> Folder for InvertIfExprCondMutation<'a, 'b> {
    fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
        expr.clone().and_then(|e| match e.node {
            ExprIf(cond, thn, Some(els)) => {
                let new_thn = self.fold_block(thn);
                let new_els = self.fold_expr(els);
                quote_expr!(self.ecx, if !($cond) { $new_thn } else { $new_els })
            },
            ExprIf(cond, thn, None) => {
                let new_thn = self.fold_block(thn);
                quote_expr!(self.ecx, if !($cond) { $new_thn })
            }

            _ => quote_expr!(self.ecx, $expr)
        })
    }
}

struct RandomStringMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for RandomStringMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        RandomStringMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b> RandomStringMutation<'a, 'b> {
    fn mutate_string(&self, expr: P<Expr>) -> P<Expr> {
        let random_string: String = thread_rng().gen_ascii_chars().take(10).collect();
        let s = random_string.as_str();

        match (*expr).node {
            ExprLit(ref spanned) => match spanned.node {
                LitStr(_, str_ty) => quote_expr!(self.ecx, $s),
                _ => quote_expr!(self.ecx, $expr)
            },

            _ => quote_expr!(self.ecx, $expr)
        }
    }
}

impl<'a, 'b> Folder for RandomStringMutation<'a, 'b> {
    fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
        self.mutate_string(e)
    }
}

struct InvertWhileExprCondMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a,'b> for InvertWhileExprCondMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertWhileExprCondMutation { ecx: ecx }
    }
}

impl<'a, 'b> Folder for InvertWhileExprCondMutation<'a, 'b> {
    fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
        expr.clone().and_then(|e| match e.node {
            ExprWhile(cond, block, None) => {
                let new_block = self.fold_block(block);
                quote_expr!(self.ecx, while !$cond { $new_block })
            },
            _ => quote_expr!(self.ecx, $expr)
        })
    }
}
<commit_msg>RandomString mutation now recurses down into AST if current node isn't a string literal.<commit_after>#![feature(quote, plugin_registrar, rustc_private, plugin, convert)]
#![allow(unused_variables, unused_imports)]

extern crate syntax;
extern crate rustc;
extern crate rand;

use syntax::ext::base::{ItemModifier, ExtCtxt, Modifier};
use syntax::codemap::{Span, Spanned};
use syntax::ptr::P;
use syntax::ast::{Item, MetaItem, Expr, ExprLit, ExprIf, ExprWhile, ExprUnary, UnNot, Lit, LitBool, LitStr};
use syntax::parse::token::intern;
use syntax::fold::{Folder, noop_fold_expr};
use rustc::plugin::Registry;

use rand::{thread_rng, Rng};

struct HeckleExpander;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let expander = Modifier(Box::new(HeckleExpander));
    reg.register_syntax_extension(intern("heckle"), expander);
}

impl ItemModifier for HeckleExpander {
    fn expand(&self, ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, item: P<Item>) -> P<Item> {
        let name = option_env!("HECKLE_MUTATION").unwrap_or("invert_boolean");
        match name {
            "invert_boolean" => {
                let mut mutation = InvertBooleanMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "random_string" => {
                let mut mutation = RandomStringMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "invert_while_cond" => {
                let mut mutation = InvertWhileExprCondMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            "invert_if_cond" => {
                let mut mutation = InvertIfExprCondMutation::new(ecx);
                mutation.fold_item(item).pop().unwrap()
            },
            _ => item
        }
    }
}

trait Mutation<'a, 'b:'a> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self;
}

struct InvertBooleanMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for InvertBooleanMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertBooleanMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b> InvertBooleanMutation<'a, 'b> {
    fn invert_boolean(&self, expr: P<Expr>) -> P<Expr> {
        match (*expr).node {
            ExprLit(ref spanned) => match spanned.node {
                LitBool(value) => quote_expr!(self.ecx, !$value),
                _ => quote_expr!(self.ecx, $expr)
            },

            _ => quote_expr!(self.ecx, $expr)
       }
    }
}

impl<'a, 'b> Folder for InvertBooleanMutation<'a, 'b> {
    fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
        self.invert_boolean(e)
    }
}

struct InvertIfExprCondMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for InvertIfExprCondMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertIfExprCondMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b:'a> Folder for InvertIfExprCondMutation<'a, 'b> {
    fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
        expr.clone().and_then(|e| match e.node {
            ExprIf(cond, thn, Some(els)) => {
                let new_thn = self.fold_block(thn);
                let new_els = self.fold_expr(els);
                quote_expr!(self.ecx, if !($cond) { $new_thn } else { $new_els })
            },
            ExprIf(cond, thn, None) => {
                let new_thn = self.fold_block(thn);
                quote_expr!(self.ecx, if !($cond) { $new_thn })
            }

            _ => {
                let mut_expr = expr.map(|e| noop_fold_expr(e, self));
                quote_expr!(self.ecx, $mut_expr)
            }
        })
    }
}

struct RandomStringMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a, 'b> for RandomStringMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        RandomStringMutation {
            ecx: ecx
        }
    }
}

impl<'a, 'b> Folder for RandomStringMutation<'a, 'b> {
    fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
        let random_string: String = thread_rng().gen_ascii_chars().take(10).collect();
        let s = random_string.as_str();

        match expr.node {
            ExprLit(ref spanned) => match spanned.node {
                LitStr(_, str_ty) => quote_expr!(self.ecx, $s),
                _ => {
                    let mut_expr = expr.clone().map(|e| noop_fold_expr(e, self));
                    quote_expr!(self.ecx, $mut_expr)
                }
            },

            _ => {
                let mut_expr = expr.clone().map(|e| noop_fold_expr(e, self));
                quote_expr!(self.ecx, $mut_expr)
            }
        }
    }
}

struct InvertWhileExprCondMutation<'a, 'b:'a> {
    ecx: &'a mut ExtCtxt<'b>
}

impl<'a, 'b:'a> Mutation<'a,'b> for InvertWhileExprCondMutation<'a, 'b> {
    fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
        InvertWhileExprCondMutation { ecx: ecx }
    }
}

impl<'a, 'b> Folder for InvertWhileExprCondMutation<'a, 'b> {
    fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
        expr.clone().and_then(|e| match e.node {
            ExprWhile(cond, block, None) => {
                let new_block = self.fold_block(block);
                quote_expr!(self.ecx, while !$cond { $new_block })
            },
            _ => quote_expr!(self.ecx, $expr)
        })
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

include!(concat!(env!("OUT_DIR"), "/codegen.rs"));

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Types {}

impl Types {
    pub fn new() -> Result<Types, ()> {
        Ok(Types {})
    }

    pub fn get_extension(&self, name: &str) -> Option<&[&str]> {
        EXT_BY_TYPE.get(name).copied()
    }

    pub fn get_mime_type(&self, ext: &str) -> Option<&str> {
        TYPE_BY_EXT.get(ext).copied()
    }

    pub fn mime_for_path(&self, path: &Path) -> &str {
        path.extension()
            .and_then(|s| s.to_str())
            .and_then(|ext| self.get_mime_type(ext))
            .unwrap_or_else(|| "text/plain")
    }
}

#[cfg(test)]
mod test {
    use crate::Types;
    use std::path::Path;

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

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

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

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

    fn test_path(types: &Types, path: &str, expected: &str) {
        assert_eq!(types.mime_for_path(Path::new(path)), expected);
    }
}
<commit_msg>Remove obsolete `Types` struct<commit_after>use std::path::Path;

include!(concat!(env!("OUT_DIR"), "/codegen.rs"));

pub fn get_extension(name: &str) -> Option<&[&str]> {
    EXT_BY_TYPE.get(name).copied()
}

pub fn get_mime_type(ext: &str) -> Option<&str> {
    TYPE_BY_EXT.get(ext).copied()
}

pub fn mime_for_path(path: &Path) -> &str {
    path.extension()
        .and_then(|s| s.to_str())
        .and_then(|ext| get_mime_type(ext))
        .unwrap_or_else(|| "text/plain")
}

#[cfg(test)]
mod test {
    use crate::{get_extension, get_mime_type, mime_for_path};
    use std::path::Path;

    #[test]
    fn test_by_ext() {
        assert_eq!(get_extension("text/css").unwrap(), ["css"]);
    }

    #[test]
    fn test_by_type() {
        assert_eq!(get_mime_type("css"), Some("text/css"));
    }

    #[test]
    fn test_by_path() {
        test_path("foo", "text/plain");
        test_path("/path/to/foo", "text/plain");
        test_path("foo.css", "text/css");
        test_path("/path/to/foo.css", "text/css");
        test_path("foo.html.css", "text/css");
        test_path("/path/to/foo.html.css", "text/css");
        test_path("/path/to.html/foo.css", "text/css");
    }

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


<commit_before>// #![deny(warnings)]
#![allow(unknown_lints)]
#![allow(clone_on_ref_ptr)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;

use std::cell::RefCell;
use std::cmp;
use std::rc::Rc;

use error_chain::ChainedError;

pub mod cmd;
pub mod errors;
mod groups;
mod keys;
pub mod layout;
mod stack;
mod x;

use crate::errors::*;
use crate::groups::Group;
use crate::keys::{KeyCombo, KeyHandlers};
use crate::layout::Layout;
use crate::x::{Connection, Event, StrutPartial, WindowId, WindowType};

pub use crate::groups::GroupBuilder;
pub use crate::keys::ModKey;
pub use crate::stack::Stack;

pub mod keysym {
    pub use x11::keysym::*;
}

/// Initializes a logger using the default configuration.
///
/// Outputs to stdout and `$XDG_DATA/lanta/lanta.log` by default.
/// You should feel free to initialize your own logger, instead of using this.
pub fn intiailize_logger() -> Result<()> {
    log_panics::init();

    let xdg_dirs = xdg::BaseDirectories::with_prefix("lanta")?;
    let log_path = xdg_dirs
        .place_data_file("lanta.log")
        .chain_err(|| "Could not create log file")?;

    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}] [{}] {}",
                time::now().rfc3339(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Trace)
        .chain(std::io::stdout())
        .chain(fern::log_file(&log_path)?)
        .apply()?;

    Ok(())
}

#[macro_export]
macro_rules! keys {
    [ $( ([$( $mod:ident ),*], $key:ident, $cmd:expr) ),+ $(,)*] => (
        vec![
            $( (vec![$( $mod ),*],  $crate::keysym::$key, $cmd) ),+
        ]
    )
}

#[macro_export]
macro_rules! groups {
    {
        $keys:ident,
        $movemodkey:ident,
        [
            $(( [$( $modkey:ident ),+], $key:ident, $name:expr, $layout:expr )),+
            $(,)*
        ]
    }  => {{
        $keys.extend(keys![
            // Switch to group:
            $(
                ([$($modkey),+], $key, $crate::cmd::lazy::switch_group($name))
            ),+,
            // Move window to group:
            $(
                ([$($modkey),+, $movemodkey], $key,  $crate::cmd::lazy::move_window_to_group($name))
            ),+
        ]);
        vec![
            $(
                 $crate::GroupBuilder::new($name, $layout)
            ),+
        ]
    }}
}

#[macro_export]
macro_rules! layouts {
    [$( $layout:expr ),+ $(,)*] => (
        vec![
            $(
                Box::new($layout) as Box<$crate::layout::Layout>
            ),+
        ]
    )
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Viewport {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
}

struct Dock {
    window_id: WindowId,
    strut_partial: Option<StrutPartial>,
}

#[derive(Default)]
struct Screen {
    vec: RefCell<Vec<Dock>>,
}

impl Screen {
    pub fn add_dock(&mut self, conn: &Connection, window_id: WindowId) {
        let strut_partial = conn.get_strut_partial(&window_id);
        self.vec.borrow_mut().push(Dock {
            window_id,
            strut_partial,
        });
    }

    pub fn remove_dock(&mut self, window_id: &WindowId) {
        self.vec.borrow_mut().retain(|d| &d.window_id != window_id);
    }

    /// Figure out the usable area of the screen based on the STRUT_PARTIAL of
    /// all docks.
    pub fn viewport(&self, screen_width: u32, screen_height: u32) -> Viewport {
        let (left, right, top, bottom) = self
            .vec
            .borrow()
            .iter()
            .filter_map(|o| o.strut_partial.as_ref())
            .fold((0, 0, 0, 0), |(left, right, top, bottom), s| {
                // We don't bother looking at the start/end members of the
                // StrutPartial - treating it more like a Strut.
                (
                    cmp::max(left, s.left()),
                    cmp::max(right, s.right()),
                    cmp::max(top, s.top()),
                    cmp::max(bottom, s.bottom()),
                )
            });
        let viewport = Viewport {
            x: left,
            y: top,
            width: screen_width - left - right,
            height: screen_height - top - bottom,
        };
        debug!("Calculated Viewport as {:?}", viewport);
        viewport
    }
}

pub struct Lanta {
    connection: Rc<Connection>,
    keys: KeyHandlers,
    groups: Stack<Group>,
    screen: Screen,
}

impl Lanta {
    pub fn new<K>(keys: K, groups: Vec<GroupBuilder>, layouts: &[Box<dyn Layout>]) -> Result<Self>
    where
        K: Into<KeyHandlers>,
    {
        let keys = keys.into();
        let connection = Rc::new(Connection::connect()?);
        connection.install_as_wm(&keys)?;

        let groups = Stack::from(
            groups
                .into_iter()
                .map(|group: GroupBuilder| group.build(connection.clone(), layouts.to_owned()))
                .collect::<Vec<Group>>(),
        );

        let mut wm = Lanta {
            connection: connection.clone(),
            keys: keys,
            groups: groups,
            screen: Screen::default(),
        };

        // Learn about existing top-level windows.
        let existing_windows = connection.top_level_windows()?;
        for window in existing_windows {
            wm.manage_window(window);
        }
        let viewport = wm.viewport();
        wm.group_mut().activate(viewport);
        wm.connection.update_ewmh_desktops(&wm.groups);

        Ok(wm)
    }

    fn viewport(&self) -> Viewport {
        let (width, height) = self
            .connection
            .get_window_geometry(self.connection.root_window_id());
        self.screen.viewport(width, height)
    }

    pub fn group(&self) -> &Group {
        self.groups.focused().expect("Invariant: No active group!")
    }

    pub fn group_mut(&mut self) -> &mut Group {
        self.groups
            .focused_mut()
            .expect("Invariant: No active group!")
    }

    pub fn switch_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If we're already on this group, do nothing.
        if self.group().name() == name {
            return;
        }

        self.group_mut().deactivate();
        self.groups.focus(|group| group.name() == name);
        let viewport = self.viewport();
        self.group_mut().activate(viewport);
        self.connection.update_ewmh_desktops(&self.groups);
    }

    /// Move the focused window from the active group to another named group.
    ///
    /// If the other named group does not exist, then the window is
    /// (unfortunately) lost.
    pub fn move_focused_to_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If the group is currently active, then do nothing. This avoids flicker as we
        // unmap/remap.
        if name == self.group().name() {
            return;
        }

        let removed = self.group_mut().remove_focused();
        let new_group_opt = self.groups.iter_mut().find(|group| group.name() == name);
        match new_group_opt {
            Some(new_group) => {
                removed.map(|window| new_group.add_window(window));
            }
            None => {
                // It would be nice to put the window back in its group (or avoid taking it out
                // of its group until we've checked the new group exists), but it's difficult
                // to do this while keeping the borrow checker happy.
                error!("Moved window to non-existent group: {}", name);
            }
        }
    }

    /// Returns whether the window is a member of any group.
    fn is_window_managed(&self, window_id: &WindowId) -> bool {
        self.groups.iter().any(|g| g.contains(window_id))
    }

    pub fn manage_window(&mut self, window_id: WindowId) {
        debug!("Managing window: {}", window_id);

        // If we are already managing the window, then do nothing. We do not
        // want the window to end up in two groups at once. We shouldn't
        // be called in such cases, so treat it as an error.
        if self.is_window_managed(&window_id) {
            error!(
                "Asked to manage window that's already managed: {}",
                window_id
            );
            return;
        }

        let window_types = self.connection.get_window_types(&window_id);
        let dock = window_types.contains(&WindowType::Dock);

        self.connection
            .enable_window_key_events(&window_id, &self.keys);

        if dock {
            self.connection.map_window(&window_id);
            self.screen.add_dock(&self.connection, window_id);
            let viewport = self.viewport();
            self.group_mut().update_viewport(viewport);
        } else {
            self.connection.enable_window_tracking(&window_id);
            self.group_mut().add_window(window_id);
        }
    }

    pub fn unmanage_window(&mut self, window_id: &WindowId) {
        debug!("Unmanaging window: {}", window_id);

        // Remove the window from whichever Group it is in. Special case for
        // docks which aren't in any group.
        self.groups
            .iter_mut()
            .find(|group| group.contains(window_id))
            .map(|group| group.remove_window(window_id));
        self.screen.remove_dock(window_id);

        // The viewport may have changed.
        let viewport = self.viewport();
        self.group_mut().update_viewport(viewport);
    }

    pub fn run(mut self) {
        info!("Started WM, entering event loop.");
        let event_loop_connection = self.connection.clone();
        let event_loop = event_loop_connection.get_event_loop();
        for event in event_loop {
            match event {
                Event::MapRequest(window_id) => self.on_map_request(window_id),
                Event::UnmapNotify(window_id) => self.on_unmap_notify(&window_id),
                Event::DestroyNotify(window_id) => self.on_destroy_notify(&window_id),
                Event::KeyPress(key) => self.on_key_press(key),
                Event::EnterNotify(window_id) => self.on_enter_notify(&window_id),
            }
        }
        info!("Event loop exiting");
    }

    fn on_map_request(&mut self, window_id: WindowId) {
        if !self.is_window_managed(&window_id) {
            // If the window isn't in any group, then add it to the current group.
            // (This will have the side-effect of mapping the window, as new windows are focused
            // and focused windows are mapped).
            self.manage_window(window_id);
        } else if self.group().contains(&window_id) {
            // Otherwise, if the window is in the active group, focus it. The application probably
            // wants us to make it prominent. Log as there may be misbehaving applications that
            // constantly re-map windows and cause focus issues.
            info!(
                "Window {} asked to be mapped but is already mapped: focusing.",
                window_id
            );
            self.group_mut().focus(&window_id);
        }
    }

    fn on_unmap_notify(&mut self, window_id: &WindowId) {
        // We only receive an unmap notify event when the window is actually
        // unmapped by its application. When our layouts unmap windows, they
        // (should) do it by disabling event tracking first.
        self.unmanage_window(window_id);
    }

    fn on_destroy_notify(&mut self, window_id: &WindowId) {
        self.unmanage_window(window_id);
    }

    fn on_key_press(&mut self, key: KeyCombo) {
        self.keys.get(&key).map(move |handler| {
            if let Err(error) = (handler)(self) {
                error!(
                    "Error running command for key command {:?}: {}",
                    key,
                    error.display_chain().to_string()
                );
            }
        });
    }

    fn on_enter_notify(&mut self, window_id: &WindowId) {
        self.group_mut().focus(window_id);
    }
}
<commit_msg>Remove unused attributes.<commit_after>// #![deny(warnings)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;

use std::cell::RefCell;
use std::cmp;
use std::rc::Rc;

use error_chain::ChainedError;

pub mod cmd;
pub mod errors;
mod groups;
mod keys;
pub mod layout;
mod stack;
mod x;

use crate::errors::*;
use crate::groups::Group;
use crate::keys::{KeyCombo, KeyHandlers};
use crate::layout::Layout;
use crate::x::{Connection, Event, StrutPartial, WindowId, WindowType};

pub use crate::groups::GroupBuilder;
pub use crate::keys::ModKey;
pub use crate::stack::Stack;

pub mod keysym {
    pub use x11::keysym::*;
}

/// Initializes a logger using the default configuration.
///
/// Outputs to stdout and `$XDG_DATA/lanta/lanta.log` by default.
/// You should feel free to initialize your own logger, instead of using this.
pub fn intiailize_logger() -> Result<()> {
    log_panics::init();

    let xdg_dirs = xdg::BaseDirectories::with_prefix("lanta")?;
    let log_path = xdg_dirs
        .place_data_file("lanta.log")
        .chain_err(|| "Could not create log file")?;

    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}] [{}] {}",
                time::now().rfc3339(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Trace)
        .chain(std::io::stdout())
        .chain(fern::log_file(&log_path)?)
        .apply()?;

    Ok(())
}

#[macro_export]
macro_rules! keys {
    [ $( ([$( $mod:ident ),*], $key:ident, $cmd:expr) ),+ $(,)*] => (
        vec![
            $( (vec![$( $mod ),*],  $crate::keysym::$key, $cmd) ),+
        ]
    )
}

#[macro_export]
macro_rules! groups {
    {
        $keys:ident,
        $movemodkey:ident,
        [
            $(( [$( $modkey:ident ),+], $key:ident, $name:expr, $layout:expr )),+
            $(,)*
        ]
    }  => {{
        $keys.extend(keys![
            // Switch to group:
            $(
                ([$($modkey),+], $key, $crate::cmd::lazy::switch_group($name))
            ),+,
            // Move window to group:
            $(
                ([$($modkey),+, $movemodkey], $key,  $crate::cmd::lazy::move_window_to_group($name))
            ),+
        ]);
        vec![
            $(
                 $crate::GroupBuilder::new($name, $layout)
            ),+
        ]
    }}
}

#[macro_export]
macro_rules! layouts {
    [$( $layout:expr ),+ $(,)*] => (
        vec![
            $(
                Box::new($layout) as Box<$crate::layout::Layout>
            ),+
        ]
    )
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Viewport {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
}

struct Dock {
    window_id: WindowId,
    strut_partial: Option<StrutPartial>,
}

#[derive(Default)]
struct Screen {
    vec: RefCell<Vec<Dock>>,
}

impl Screen {
    pub fn add_dock(&mut self, conn: &Connection, window_id: WindowId) {
        let strut_partial = conn.get_strut_partial(&window_id);
        self.vec.borrow_mut().push(Dock {
            window_id,
            strut_partial,
        });
    }

    pub fn remove_dock(&mut self, window_id: &WindowId) {
        self.vec.borrow_mut().retain(|d| &d.window_id != window_id);
    }

    /// Figure out the usable area of the screen based on the STRUT_PARTIAL of
    /// all docks.
    pub fn viewport(&self, screen_width: u32, screen_height: u32) -> Viewport {
        let (left, right, top, bottom) = self
            .vec
            .borrow()
            .iter()
            .filter_map(|o| o.strut_partial.as_ref())
            .fold((0, 0, 0, 0), |(left, right, top, bottom), s| {
                // We don't bother looking at the start/end members of the
                // StrutPartial - treating it more like a Strut.
                (
                    cmp::max(left, s.left()),
                    cmp::max(right, s.right()),
                    cmp::max(top, s.top()),
                    cmp::max(bottom, s.bottom()),
                )
            });
        let viewport = Viewport {
            x: left,
            y: top,
            width: screen_width - left - right,
            height: screen_height - top - bottom,
        };
        debug!("Calculated Viewport as {:?}", viewport);
        viewport
    }
}

pub struct Lanta {
    connection: Rc<Connection>,
    keys: KeyHandlers,
    groups: Stack<Group>,
    screen: Screen,
}

impl Lanta {
    pub fn new<K>(keys: K, groups: Vec<GroupBuilder>, layouts: &[Box<dyn Layout>]) -> Result<Self>
    where
        K: Into<KeyHandlers>,
    {
        let keys = keys.into();
        let connection = Rc::new(Connection::connect()?);
        connection.install_as_wm(&keys)?;

        let groups = Stack::from(
            groups
                .into_iter()
                .map(|group: GroupBuilder| group.build(connection.clone(), layouts.to_owned()))
                .collect::<Vec<Group>>(),
        );

        let mut wm = Lanta {
            connection: connection.clone(),
            keys: keys,
            groups: groups,
            screen: Screen::default(),
        };

        // Learn about existing top-level windows.
        let existing_windows = connection.top_level_windows()?;
        for window in existing_windows {
            wm.manage_window(window);
        }
        let viewport = wm.viewport();
        wm.group_mut().activate(viewport);
        wm.connection.update_ewmh_desktops(&wm.groups);

        Ok(wm)
    }

    fn viewport(&self) -> Viewport {
        let (width, height) = self
            .connection
            .get_window_geometry(self.connection.root_window_id());
        self.screen.viewport(width, height)
    }

    pub fn group(&self) -> &Group {
        self.groups.focused().expect("Invariant: No active group!")
    }

    pub fn group_mut(&mut self) -> &mut Group {
        self.groups
            .focused_mut()
            .expect("Invariant: No active group!")
    }

    pub fn switch_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If we're already on this group, do nothing.
        if self.group().name() == name {
            return;
        }

        self.group_mut().deactivate();
        self.groups.focus(|group| group.name() == name);
        let viewport = self.viewport();
        self.group_mut().activate(viewport);
        self.connection.update_ewmh_desktops(&self.groups);
    }

    /// Move the focused window from the active group to another named group.
    ///
    /// If the other named group does not exist, then the window is
    /// (unfortunately) lost.
    pub fn move_focused_to_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If the group is currently active, then do nothing. This avoids flicker as we
        // unmap/remap.
        if name == self.group().name() {
            return;
        }

        let removed = self.group_mut().remove_focused();
        let new_group_opt = self.groups.iter_mut().find(|group| group.name() == name);
        match new_group_opt {
            Some(new_group) => {
                removed.map(|window| new_group.add_window(window));
            }
            None => {
                // It would be nice to put the window back in its group (or avoid taking it out
                // of its group until we've checked the new group exists), but it's difficult
                // to do this while keeping the borrow checker happy.
                error!("Moved window to non-existent group: {}", name);
            }
        }
    }

    /// Returns whether the window is a member of any group.
    fn is_window_managed(&self, window_id: &WindowId) -> bool {
        self.groups.iter().any(|g| g.contains(window_id))
    }

    pub fn manage_window(&mut self, window_id: WindowId) {
        debug!("Managing window: {}", window_id);

        // If we are already managing the window, then do nothing. We do not
        // want the window to end up in two groups at once. We shouldn't
        // be called in such cases, so treat it as an error.
        if self.is_window_managed(&window_id) {
            error!(
                "Asked to manage window that's already managed: {}",
                window_id
            );
            return;
        }

        let window_types = self.connection.get_window_types(&window_id);
        let dock = window_types.contains(&WindowType::Dock);

        self.connection
            .enable_window_key_events(&window_id, &self.keys);

        if dock {
            self.connection.map_window(&window_id);
            self.screen.add_dock(&self.connection, window_id);
            let viewport = self.viewport();
            self.group_mut().update_viewport(viewport);
        } else {
            self.connection.enable_window_tracking(&window_id);
            self.group_mut().add_window(window_id);
        }
    }

    pub fn unmanage_window(&mut self, window_id: &WindowId) {
        debug!("Unmanaging window: {}", window_id);

        // Remove the window from whichever Group it is in. Special case for
        // docks which aren't in any group.
        self.groups
            .iter_mut()
            .find(|group| group.contains(window_id))
            .map(|group| group.remove_window(window_id));
        self.screen.remove_dock(window_id);

        // The viewport may have changed.
        let viewport = self.viewport();
        self.group_mut().update_viewport(viewport);
    }

    pub fn run(mut self) {
        info!("Started WM, entering event loop.");
        let event_loop_connection = self.connection.clone();
        let event_loop = event_loop_connection.get_event_loop();
        for event in event_loop {
            match event {
                Event::MapRequest(window_id) => self.on_map_request(window_id),
                Event::UnmapNotify(window_id) => self.on_unmap_notify(&window_id),
                Event::DestroyNotify(window_id) => self.on_destroy_notify(&window_id),
                Event::KeyPress(key) => self.on_key_press(key),
                Event::EnterNotify(window_id) => self.on_enter_notify(&window_id),
            }
        }
        info!("Event loop exiting");
    }

    fn on_map_request(&mut self, window_id: WindowId) {
        if !self.is_window_managed(&window_id) {
            // If the window isn't in any group, then add it to the current group.
            // (This will have the side-effect of mapping the window, as new windows are focused
            // and focused windows are mapped).
            self.manage_window(window_id);
        } else if self.group().contains(&window_id) {
            // Otherwise, if the window is in the active group, focus it. The application probably
            // wants us to make it prominent. Log as there may be misbehaving applications that
            // constantly re-map windows and cause focus issues.
            info!(
                "Window {} asked to be mapped but is already mapped: focusing.",
                window_id
            );
            self.group_mut().focus(&window_id);
        }
    }

    fn on_unmap_notify(&mut self, window_id: &WindowId) {
        // We only receive an unmap notify event when the window is actually
        // unmapped by its application. When our layouts unmap windows, they
        // (should) do it by disabling event tracking first.
        self.unmanage_window(window_id);
    }

    fn on_destroy_notify(&mut self, window_id: &WindowId) {
        self.unmanage_window(window_id);
    }

    fn on_key_press(&mut self, key: KeyCombo) {
        self.keys.get(&key).map(move |handler| {
            if let Err(error) = (handler)(self) {
                error!(
                    "Error running command for key command {:?}: {}",
                    key,
                    error.display_chain().to_string()
                );
            }
        });
    }

    fn on_enter_notify(&mut self, window_id: &WindowId) {
        self.group_mut().focus(window_id);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "bodyparser"]
#![feature(core, io)]

//! Body Parser Plugin for Iron
//!
//! This plugin parses JSON out of an incoming Request.

extern crate iron;
extern crate "rustc-serialize" as rustc_serialize;
extern crate plugin;
extern crate persistent;

use rustc_serialize::{json, Decodable};

use iron::mime;
use iron::prelude::*;
use iron::headers;
use iron::typemap::{Key};
use std::io::{ReadExt, Read};
use std::marker;

pub use self::errors::{BodyError, BodyErrorCause};
pub use self::limit_reader::{LimitReader};

mod errors;
mod limit_reader;

fn read_body_as_utf8(req: &mut Request, limit: usize) -> Result<String, errors::BodyError> {
    let mut bytes = Vec::new();
    match LimitReader::new(req.body.by_ref(), limit).read_to_end(&mut bytes) {
        Ok(_) => {
             match String::from_utf8(bytes) {
                Ok(e) => Ok(e),
                Err(err) => Err(errors::BodyError {
                    detail: "Invalid UTF-8 sequence".to_string(),
                    cause: errors::BodyErrorCause::Utf8Error(err.utf8_error())
                })
            }
        },
        Err(err) => Err(errors::BodyError {
            detail: "Can't read request body".to_string(),
            cause: errors::BodyErrorCause::IoError(err)
        })
    }
}

/// Use this key to modify the default body limit.
pub struct MaxBodyLength;
impl Key for MaxBodyLength {
    type Value = usize;
}

/// Raw is a plugin to read a request body into UTF-8 String.
/// Doesn't read `multipart/form-data`.
pub struct Raw;

impl Key for Raw {
    type Value = Option<String>;
}

const DEFAULT_BODY_LIMIT: usize = 1024 * 1024 * 100;

impl<'a> plugin::Plugin<Request<'a>> for Raw {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<String>, BodyError> {
        let need_read = req.headers.get::<headers::ContentType>().map(|header| {
            match **header {
                mime::Mime(mime::TopLevel::Multipart, mime::SubLevel::FormData, _) => false,
                _ => true
            }
        }).unwrap_or(false);

        if need_read {
            let max_length = req.get::<persistent::Read<MaxBodyLength>>()
                .ok().cloned().unwrap_or(DEFAULT_BODY_LIMIT);
            let body = try!(read_body_as_utf8(req, max_length));
            Ok(Some(body))
        } else {
            Ok(None)
        }
    }
}

/// Json is a plugin to parse a request body into JSON.
/// Uses Raw plugin to parse the body with limit.
#[derive(Clone)]
pub struct Json;
impl Key for Json {
    type Value = Option<json::Json>;
}

impl<'a> plugin::Plugin<Request<'a>> for Json {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<json::Json>, BodyError> {
        req.get::<Raw>()
            .and_then(|maybe_body| {
                reverse_option(maybe_body.map(|body| body.parse()))
                    .map_err(|err| {
                        BodyError {
                            detail: "Can't parse body to JSON".to_string(),
                            cause: BodyErrorCause::ParserError(err)
                        }
                    })
            })
    }
}

/// Struct is a plugin to parse a request body into a struct.
/// Uses Raw plugin to parse the body with limit.
#[derive(Clone)]
pub struct Struct<T: Decodable> {
    marker: marker::PhantomData<T>
}
impl<T: 'static + Decodable> Key for Struct<T> {
    type Value = Option<T>;
}

impl<'a, T: 'static + Decodable> plugin::Plugin<Request<'a>> for Struct<T> {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<T>, BodyError> {
        req.get::<Json>()
            .and_then(|maybe_body| {
                reverse_option(maybe_body.map(|body| Decodable::decode(&mut json::Decoder::new(body))))
                    .map_err(|err| BodyError {
                        detail: "Can't parse body to the struct".to_string(),
                        cause: BodyErrorCause::DecoderError(err)
                    })
            })
    }
}

fn reverse_option<T,E>(value: Option<Result<T, E>>) -> Result<Option<T>, E> {
    match value {
        Some(Ok(val)) => Ok(Some(val)),
        Some(Err(err)) => Err(err),
        None => Ok(None),
    }
}<commit_msg>remove non-existant std::io::ReadExt<commit_after>#![crate_name = "bodyparser"]
#![feature(core, io)]

//! Body Parser Plugin for Iron
//!
//! This plugin parses JSON out of an incoming Request.

extern crate iron;
extern crate "rustc-serialize" as rustc_serialize;
extern crate plugin;
extern crate persistent;

use rustc_serialize::{json, Decodable};

use iron::mime;
use iron::prelude::*;
use iron::headers;
use iron::typemap::{Key};
use std::io::Read;
use std::marker;

pub use self::errors::{BodyError, BodyErrorCause};
pub use self::limit_reader::{LimitReader};

mod errors;
mod limit_reader;

fn read_body_as_utf8(req: &mut Request, limit: usize) -> Result<String, errors::BodyError> {
    let mut bytes = Vec::new();
    match LimitReader::new(req.body.by_ref(), limit).read_to_end(&mut bytes) {
        Ok(_) => {
             match String::from_utf8(bytes) {
                Ok(e) => Ok(e),
                Err(err) => Err(errors::BodyError {
                    detail: "Invalid UTF-8 sequence".to_string(),
                    cause: errors::BodyErrorCause::Utf8Error(err.utf8_error())
                })
            }
        },
        Err(err) => Err(errors::BodyError {
            detail: "Can't read request body".to_string(),
            cause: errors::BodyErrorCause::IoError(err)
        })
    }
}

/// Use this key to modify the default body limit.
pub struct MaxBodyLength;
impl Key for MaxBodyLength {
    type Value = usize;
}

/// Raw is a plugin to read a request body into UTF-8 String.
/// Doesn't read `multipart/form-data`.
pub struct Raw;

impl Key for Raw {
    type Value = Option<String>;
}

const DEFAULT_BODY_LIMIT: usize = 1024 * 1024 * 100;

impl<'a> plugin::Plugin<Request<'a>> for Raw {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<String>, BodyError> {
        let need_read = req.headers.get::<headers::ContentType>().map(|header| {
            match **header {
                mime::Mime(mime::TopLevel::Multipart, mime::SubLevel::FormData, _) => false,
                _ => true
            }
        }).unwrap_or(false);

        if need_read {
            let max_length = req.get::<persistent::Read<MaxBodyLength>>()
                .ok().cloned().unwrap_or(DEFAULT_BODY_LIMIT);
            let body = try!(read_body_as_utf8(req, max_length));
            Ok(Some(body))
        } else {
            Ok(None)
        }
    }
}

/// Json is a plugin to parse a request body into JSON.
/// Uses Raw plugin to parse the body with limit.
#[derive(Clone)]
pub struct Json;
impl Key for Json {
    type Value = Option<json::Json>;
}

impl<'a> plugin::Plugin<Request<'a>> for Json {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<json::Json>, BodyError> {
        req.get::<Raw>()
            .and_then(|maybe_body| {
                reverse_option(maybe_body.map(|body| body.parse()))
                    .map_err(|err| {
                        BodyError {
                            detail: "Can't parse body to JSON".to_string(),
                            cause: BodyErrorCause::ParserError(err)
                        }
                    })
            })
    }
}

/// Struct is a plugin to parse a request body into a struct.
/// Uses Raw plugin to parse the body with limit.
#[derive(Clone)]
pub struct Struct<T: Decodable> {
    marker: marker::PhantomData<T>
}
impl<T: 'static + Decodable> Key for Struct<T> {
    type Value = Option<T>;
}

impl<'a, T: 'static + Decodable> plugin::Plugin<Request<'a>> for Struct<T> {
    type Error = BodyError;

    fn eval(req: &mut Request) -> Result<Option<T>, BodyError> {
        req.get::<Json>()
            .and_then(|maybe_body| {
                reverse_option(maybe_body.map(|body| Decodable::decode(&mut json::Decoder::new(body))))
                    .map_err(|err| BodyError {
                        detail: "Can't parse body to the struct".to_string(),
                        cause: BodyErrorCause::DecoderError(err)
                    })
            })
    }
}

fn reverse_option<T,E>(value: Option<Result<T, E>>) -> Result<Option<T>, E> {
    match value {
        Some(Ok(val)) => Ok(Some(val)),
        Some(Err(err)) => Err(err),
        None => Ok(None),
    }
}<|endoftext|>


<commit_before>#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![deny(unsafe_code)]

//! A library providing the written english form of a number.
//!
//! # Example
//!
//! ```rust
//! use numeral::Cardinal;
//!
//! let n = 127;
//! println!("{} is written {}", n, n.cardinal());
//! ```

const NUMBER: [&str; 20] = [
    "zero",
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
    "ten",
    "eleven",
    "twelve",
    "thirteen",
    "fourteen",
    "fifteen",
    "sixteen",
    "seventeen",
    "eighteen",
    "nineteen",
];

#[rustfmt::skip]
const TENS: [&str; 10] = [
    "",
    "",
    "twenty",
    "thirty",
    "forty",
    "fifty",
    "sixty",
    "seventy",
    "eighty",
    "ninety",
];

const MULTIPLIER: [&str; 9] = [
    "",
    "thousand",
    "million",
    "billion",
    "trillion",
    "quadrillion",
    "quintillion",
    "sextillion",
    "septillion",
];

/// Provides the cardinal written form of a number.
pub trait Cardinal {
    /// Returns the cardinal form of a number.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use numeral::Cardinal;
    /// let written_form = 127.cardinal();
    /// assert_eq!(written_form, "one hundred twenty-seven");
    /// ```
    fn cardinal(&self) -> String;
}

macro_rules! impl_numeral_signed {
    ($($numtype:ty),*) => {$(
        impl Cardinal for $numtype {
            fn cardinal(&self) -> String {
                cardinal_int(i64::from(*self).abs() as u64, self.is_negative())
            }
        }
    )*};
}

impl_numeral_signed!(i8, i16, i32);

macro_rules! impl_numeral_unsigned {
    ($($numtype:ty),*) => {$(
        impl Cardinal for $numtype {
            fn cardinal(&self) -> String {
                cardinal_int(u64::from(*self), false)
            }
        }
    )*};
}

impl_numeral_unsigned!(u8, u16, u32, u64);

impl Cardinal for i64 {
    fn cardinal(&self) -> String {
        let n_abs = if *self == i64::MIN { *self as u64 } else { self.abs() as u64 };
        cardinal_int(n_abs, self.is_negative())
    }
}

impl Cardinal for isize {
    fn cardinal(&self) -> String {
        let n_abs = if *self == isize::MIN { *self as usize } else { self.abs() as usize };
        cardinal_int(n_abs as u64, self.is_negative())
    }
}

impl Cardinal for usize {
    fn cardinal(&self) -> String {
        cardinal_int(*self as u64, false)
    }
}

/// Returns the written form of any 64-bit unsigned integer as a string.
fn cardinal_int(n: u64, negative: bool) -> String {
    if n == 0 {
        return String::from(NUMBER[0]);
    }
    let multiple_order = ((n as f32).log10() as u32) / 3;
    let max_len = multiple_order as usize * 8 + 6;
    let mut cardinal = Vec::with_capacity(max_len);
    if negative {
        cardinal.push("minus ");
    }
    compose_cardinal_int(n, multiple_order, &mut cardinal);
    debug_assert!(cardinal.len() <= max_len);
    cardinal.concat()
}

macro_rules! push {
    ($vec:ident, $table:ident[$index:ident]) => {
        debug_assert!(
            ($index as usize) < $table.len(),
            "{} out of {}'s range",
            stringify!($index),
            stringify!($table)
        );
        $vec.push($table[$index as usize]);
    };
}

/// Pushes the strings composing the cardinal form of any 64-bit unsigned
/// integer on a `str` stack. Zero is ignored.
fn compose_cardinal_int(mut n: u64, mut multiple_order: u32, cardinal: &mut Vec<&str>) {
    debug_assert_ne!(n, 0, "n == 0 in compose_cardinal_int()");
    debug_assert_eq!(
        multiple_order,
        ((n as f32).log10() as u32) / 3,
        "wrong value for multiple_order in compose_cardinal_int()"
    );

    if multiple_order > 0 {
        let mut multiplier = 10u64.pow(multiple_order * 3);
        let mut multiplicand;
        loop {
            multiplicand = n / multiplier;
            n %= multiplier;
            if multiplicand != 0 {
                push_triplet(multiplicand, cardinal);
                cardinal.push(" ");
                push!(cardinal, MULTIPLIER[multiple_order]);
                if n != 0 {
                    cardinal.push(" ");
                } else {
                    return;
                }
            }
            multiple_order -= 1;
            if multiple_order == 0 {
                break;
            }
            multiplier /= 1000;
        }
    }
    push_triplet(n, cardinal);
}

/// Takes an integer in [1,999] and adds it's written form to a `str` stack.
fn push_triplet(n: u64, cardinal: &mut Vec<&str>) {
    debug_assert_ne!(n, 0, "n == 0 in push_triplet()");
    debug_assert!(n < 1000, "n >= 1000 in push_triplet()");

    let hundreds = n / 100;
    let rest = n % 100;
    if hundreds != 0 {
        push!(cardinal, NUMBER[hundreds]);
        if rest == 0 {
            cardinal.push(" hundred");
            return;
        } else {
            cardinal.push(" hundred ");
        }
    }
    push_doublet(rest, cardinal);
}

/// Takes an integer in [1,99] and adds it's written form to a `str` stack.
fn push_doublet(n: u64, cardinal: &mut Vec<&str>) {
    debug_assert_ne!(n, 0, "n == 0 in push_doublet()");
    debug_assert!(n < 100, "n >= 100 in push_doublet()");

    if n < 20 {
        push!(cardinal, NUMBER[n]);
    } else {
        let tens = n / 10;
        let ones = n % 10;
        push!(cardinal, TENS[tens]);
        if ones != 0 {
            cardinal.push("-");
            push!(cardinal, NUMBER[ones]);
        }
    }
}
<commit_msg>Use regular debug_assert over _eq/_ne variants<commit_after>#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![deny(unsafe_code)]

//! A library providing the written english form of a number.
//!
//! # Example
//!
//! ```rust
//! use numeral::Cardinal;
//!
//! let n = 127;
//! println!("{} is written {}", n, n.cardinal());
//! ```

const NUMBER: [&str; 20] = [
    "zero",
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
    "ten",
    "eleven",
    "twelve",
    "thirteen",
    "fourteen",
    "fifteen",
    "sixteen",
    "seventeen",
    "eighteen",
    "nineteen",
];

#[rustfmt::skip]
const TENS: [&str; 10] = [
    "",
    "",
    "twenty",
    "thirty",
    "forty",
    "fifty",
    "sixty",
    "seventy",
    "eighty",
    "ninety",
];

const MULTIPLIER: [&str; 9] = [
    "",
    "thousand",
    "million",
    "billion",
    "trillion",
    "quadrillion",
    "quintillion",
    "sextillion",
    "septillion",
];

/// Provides the cardinal written form of a number.
pub trait Cardinal {
    /// Returns the cardinal form of a number.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use numeral::Cardinal;
    /// let written_form = 127.cardinal();
    /// assert_eq!(written_form, "one hundred twenty-seven");
    /// ```
    fn cardinal(&self) -> String;
}

macro_rules! impl_numeral_signed {
    ($($numtype:ty),*) => {$(
        impl Cardinal for $numtype {
            fn cardinal(&self) -> String {
                cardinal_int(i64::from(*self).abs() as u64, self.is_negative())
            }
        }
    )*};
}

impl_numeral_signed!(i8, i16, i32);

macro_rules! impl_numeral_unsigned {
    ($($numtype:ty),*) => {$(
        impl Cardinal for $numtype {
            fn cardinal(&self) -> String {
                cardinal_int(u64::from(*self), false)
            }
        }
    )*};
}

impl_numeral_unsigned!(u8, u16, u32, u64);

impl Cardinal for i64 {
    fn cardinal(&self) -> String {
        let n_abs = if *self == i64::MIN { *self as u64 } else { self.abs() as u64 };
        cardinal_int(n_abs, self.is_negative())
    }
}

impl Cardinal for isize {
    fn cardinal(&self) -> String {
        let n_abs = if *self == isize::MIN { *self as usize } else { self.abs() as usize };
        cardinal_int(n_abs as u64, self.is_negative())
    }
}

impl Cardinal for usize {
    fn cardinal(&self) -> String {
        cardinal_int(*self as u64, false)
    }
}

/// Returns the written form of any 64-bit unsigned integer as a string.
fn cardinal_int(n: u64, negative: bool) -> String {
    if n == 0 {
        return String::from(NUMBER[0]);
    }
    let multiple_order = ((n as f32).log10() as u32) / 3;
    let max_len = multiple_order as usize * 8 + 6;
    let mut cardinal = Vec::with_capacity(max_len);
    if negative {
        cardinal.push("minus ");
    }
    compose_cardinal_int(n, multiple_order, &mut cardinal);
    debug_assert!(cardinal.len() <= max_len);
    cardinal.concat()
}

macro_rules! push {
    ($vec:ident, $table:ident[$index:ident]) => {
        debug_assert!(
            ($index as usize) < $table.len(),
            "{} out of {}'s range",
            stringify!($index),
            stringify!($table)
        );
        $vec.push($table[$index as usize]);
    };
}

/// Pushes the strings composing the cardinal form of any 64-bit unsigned
/// integer on a `str` stack. Zero is ignored.
fn compose_cardinal_int(mut n: u64, mut multiple_order: u32, cardinal: &mut Vec<&str>) {
    debug_assert!(n != 0, "n == 0 in compose_cardinal_int()");
    debug_assert!(multiple_order == ((n as f32).log10() as u32) / 3,
        "wrong value for multiple_order in compose_cardinal_int()"
    );

    if multiple_order > 0 {
        let mut multiplier = 10u64.pow(multiple_order * 3);
        let mut multiplicand;
        loop {
            multiplicand = n / multiplier;
            n %= multiplier;
            if multiplicand != 0 {
                push_triplet(multiplicand, cardinal);
                cardinal.push(" ");
                push!(cardinal, MULTIPLIER[multiple_order]);
                if n != 0 {
                    cardinal.push(" ");
                } else {
                    return;
                }
            }
            multiple_order -= 1;
            if multiple_order == 0 {
                break;
            }
            multiplier /= 1000;
        }
    }
    push_triplet(n, cardinal);
}

/// Takes an integer in [1,999] and adds it's written form to a `str` stack.
fn push_triplet(n: u64, cardinal: &mut Vec<&str>) {
    debug_assert!(n != 0, "n == 0 in push_triplet()");
    debug_assert!(n < 1000, "n >= 1000 in push_triplet()");

    let hundreds = n / 100;
    let rest = n % 100;
    if hundreds != 0 {
        push!(cardinal, NUMBER[hundreds]);
        if rest == 0 {
            cardinal.push(" hundred");
            return;
        } else {
            cardinal.push(" hundred ");
        }
    }
    push_doublet(rest, cardinal);
}

/// Takes an integer in [1,99] and adds it's written form to a `str` stack.
fn push_doublet(n: u64, cardinal: &mut Vec<&str>) {
    debug_assert!(n != 0, "n == 0 in push_doublet()");
    debug_assert!(n < 100, "n >= 100 in push_doublet()");

    if n < 20 {
        push!(cardinal, NUMBER[n]);
    } else {
        let tens = n / 10;
        let ones = n % 10;
        push!(cardinal, TENS[tens]);
        if ones != 0 {
            cardinal.push("-");
            push!(cardinal, NUMBER[ones]);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Nikita Pekin and the xkcd_rs contributors
// See the README.md file at the top-level directory of this distribution.
//
// 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.

#![deny(missing_docs)]
#![deny(non_camel_case_types)]
#![deny(warnings)]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

//! A library providing Rust bindings for the XKCD web API.
//!
//! The library provides a `XkcdRequestSender` trait which can be implemented by
//! various request senders. These implementations may then be used to execute
//! requests to the API.
//!
//! If the `hyper` feature is enabled during compilation, then this library
//! provides an implementation of the `XkcdRequestSender` trait for the
//! `hyper::Client` of the [`hyper`](https://github.com/hyperium/hyper) library.
//!
//! Response bodies are deserialized from JSON into structs via the
//! [`serde_json`](https://github.com/serde-rs/json) library.

#[cfg(feature = "hyper")]
extern crate hyper;

#[macro_use]
extern crate log;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate url;
extern crate url_serde;

mod error;
#[cfg(test)]
mod util;

pub use self::error::{Error, HttpRequestError, HttpRequestResult, Result};
pub use self::model::XkcdResponse;

pub mod comics;
pub mod model;
pub mod random;

use serde::Deserialize;
use std::fmt::Debug;

fn parse_xkcd_response<T>(response: &str) -> Result<T>
    where T: Debug + Deserialize,
{
    let parsed_response = serde_json::from_str(response)?;
    trace!("Parsed response: {:?}", parsed_response);
    Ok(parsed_response)
}

/// Functionality for sending requests to the XKCD API via HTTPS.
///
/// Should be implemented for clients to send requests to the XKCD API.
pub trait XkcdRequestSender {
    /// Performs an API call to the XKCD web API.
    fn send(&self, method: &str) -> HttpRequestResult<String>;
}

#[cfg(feature = "hyper")]
mod hyper_support {
    use error::{HttpRequestError, HttpRequestResult};
    use hyper;
    use hyper::status::StatusCode;
    use std::io::Read;
    use super::XkcdRequestSender;
    use url::Url;

    impl XkcdRequestSender for hyper::Client {
        fn send(&self, method: &str) -> HttpRequestResult<String> {
            let url_string = format!("https://xkcd.com/{}", method);
            let url = url_string.parse::<Url>().expect("Unable to parse URL");

            trace!("Sending query to url: {}", url);
            let mut response = self.get(url.clone()).send()?;

            trace!("Status code: {}", response.status);
            // Ensure we got a valid status code.
            if let StatusCode::NotFound = response.status {
                return Err(HttpRequestError::not_found(url));
            }

            let mut result = String::new();
            response.read_to_string(&mut result)?;
            trace!("Query result: {}", result);

            Ok(result)
        }
    }

    impl From<hyper::Error> for HttpRequestError {
        fn from(error: hyper::Error) -> HttpRequestError {
            match error {
                hyper::Error::Io(e) => HttpRequestError::Io(e),
                e => HttpRequestError::Other(Box::new(e)),
            }
        }
    }
}

#[cfg(feature = "hyper")]
pub use hyper_support::*;

#[cfg(test)]
mod test_helpers {
    use super::{HttpRequestError, XkcdRequestSender};

    pub struct MockXkcdRequestSender {
        response: String,
    }

    impl MockXkcdRequestSender {
        pub fn respond_with<S: Into<String>>(response: S) -> Self {
            MockXkcdRequestSender { response: response.into(), }
        }
    }

    impl XkcdRequestSender for MockXkcdRequestSender {
        fn send(&self, _: &str) -> Result<String, HttpRequestError> {
            Ok(self.response.clone())
        }
    }
}

#[cfg(test)]
mod tests {
    use model::XkcdResponse;
    use super::parse_xkcd_response;
    use url::Url;
    use util::read_sample_data_from_path;

    #[test]
    fn test_parse_xkcd_response() {
        let result = read_sample_data_from_path("tests/sample-data/example.json");
        let response = parse_xkcd_response::<XkcdResponse>(result.as_str()).unwrap();
        assert_eq!(response,
                   XkcdResponse {
                       month: 9,
                       num: 1572,
                       link: "http://goo.gl/forms/pj0OhX6wfO".to_owned(),
                       year: 2015,
                       news: "".to_owned(),
                       safe_title: "xkcd Survey".to_owned(),
                       transcript: "Introducing the XKCD SURVEY! A search for weird correlations.\n\nNOTE: This survey is anonymous, but all responses will be posted publicly so people can play with the data.\n\nClick here to take the survey.\n\nhttp:\n\ngoo.gl\nforms\nlzZr7P9Qlm\n\nOr click here, or here. The whole comic is a link because I still haven't gotten the hang of HTML imagemaps.\n\n{{Title text: The xkcd Survey: Big Data for a Big Planet}}".to_owned(),
                       alt: "The xkcd Survey: Big Data for a Big Planet".to_owned(),
                       img: "http://imgs.xkcd.com/comics/xkcd_survey.png".to_owned().parse::<Url>().unwrap(),
                       title: "xkcd Survey".to_owned(),
                       day: 1,
                   }
        );
    }
}
<commit_msg>style: combine attribute statements<commit_after>// Copyright (c) 2016-2017 Nikita Pekin and the xkcd_rs contributors
// See the README.md file at the top-level directory of this distribution.
//
// 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.

#![deny(missing_docs, non_camel_case_types, warnings)]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

//! A library providing Rust bindings for the XKCD web API.
//!
//! The library provides a `XkcdRequestSender` trait which can be implemented by
//! various request senders. These implementations may then be used to execute
//! requests to the API.
//!
//! If the `hyper` feature is enabled during compilation, then this library
//! provides an implementation of the `XkcdRequestSender` trait for the
//! `hyper::Client` of the [`hyper`](https://github.com/hyperium/hyper) library.
//!
//! Response bodies are deserialized from JSON into structs via the
//! [`serde_json`](https://github.com/serde-rs/json) library.

#[cfg(feature = "hyper")]
extern crate hyper;

#[macro_use]
extern crate log;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate url;
extern crate url_serde;

mod error;
#[cfg(test)]
mod util;

pub use self::error::{Error, HttpRequestError, HttpRequestResult, Result};
pub use self::model::XkcdResponse;

pub mod comics;
pub mod model;
pub mod random;

use serde::Deserialize;
use std::fmt::Debug;

fn parse_xkcd_response<T>(response: &str) -> Result<T>
    where T: Debug + Deserialize,
{
    let parsed_response = serde_json::from_str(response)?;
    trace!("Parsed response: {:?}", parsed_response);
    Ok(parsed_response)
}

/// Functionality for sending requests to the XKCD API via HTTPS.
///
/// Should be implemented for clients to send requests to the XKCD API.
pub trait XkcdRequestSender {
    /// Performs an API call to the XKCD web API.
    fn send(&self, method: &str) -> HttpRequestResult<String>;
}

#[cfg(feature = "hyper")]
mod hyper_support {
    use error::{HttpRequestError, HttpRequestResult};
    use hyper;
    use hyper::status::StatusCode;
    use std::io::Read;
    use super::XkcdRequestSender;
    use url::Url;

    impl XkcdRequestSender for hyper::Client {
        fn send(&self, method: &str) -> HttpRequestResult<String> {
            let url_string = format!("https://xkcd.com/{}", method);
            let url = url_string.parse::<Url>().expect("Unable to parse URL");

            trace!("Sending query to url: {}", url);
            let mut response = self.get(url.clone()).send()?;

            trace!("Status code: {}", response.status);
            // Ensure we got a valid status code.
            if let StatusCode::NotFound = response.status {
                return Err(HttpRequestError::not_found(url));
            }

            let mut result = String::new();
            response.read_to_string(&mut result)?;
            trace!("Query result: {}", result);

            Ok(result)
        }
    }

    impl From<hyper::Error> for HttpRequestError {
        fn from(error: hyper::Error) -> HttpRequestError {
            match error {
                hyper::Error::Io(e) => HttpRequestError::Io(e),
                e => HttpRequestError::Other(Box::new(e)),
            }
        }
    }
}

#[cfg(feature = "hyper")]
pub use hyper_support::*;

#[cfg(test)]
mod test_helpers {
    use super::{HttpRequestError, XkcdRequestSender};

    pub struct MockXkcdRequestSender {
        response: String,
    }

    impl MockXkcdRequestSender {
        pub fn respond_with<S: Into<String>>(response: S) -> Self {
            MockXkcdRequestSender { response: response.into(), }
        }
    }

    impl XkcdRequestSender for MockXkcdRequestSender {
        fn send(&self, _: &str) -> Result<String, HttpRequestError> {
            Ok(self.response.clone())
        }
    }
}

#[cfg(test)]
mod tests {
    use model::XkcdResponse;
    use super::parse_xkcd_response;
    use url::Url;
    use util::read_sample_data_from_path;

    #[test]
    fn test_parse_xkcd_response() {
        let result = read_sample_data_from_path("tests/sample-data/example.json");
        let response = parse_xkcd_response::<XkcdResponse>(result.as_str()).unwrap();
        assert_eq!(response,
                   XkcdResponse {
                       month: 9,
                       num: 1572,
                       link: "http://goo.gl/forms/pj0OhX6wfO".to_owned(),
                       year: 2015,
                       news: "".to_owned(),
                       safe_title: "xkcd Survey".to_owned(),
                       transcript: "Introducing the XKCD SURVEY! A search for weird correlations.\n\nNOTE: This survey is anonymous, but all responses will be posted publicly so people can play with the data.\n\nClick here to take the survey.\n\nhttp:\n\ngoo.gl\nforms\nlzZr7P9Qlm\n\nOr click here, or here. The whole comic is a link because I still haven't gotten the hang of HTML imagemaps.\n\n{{Title text: The xkcd Survey: Big Data for a Big Planet}}".to_owned(),
                       alt: "The xkcd Survey: Big Data for a Big Planet".to_owned(),
                       img: "http://imgs.xkcd.com/comics/xkcd_survey.png".to_owned().parse::<Url>().unwrap(),
                       title: "xkcd Survey".to_owned(),
                       day: 1,
                   }
        );
    }
}
<|endoftext|>