<commit_before>extern crate byteorder;
extern crate chrono;

use std::ascii::{AsciiExt};
use std::io::{Write};

use byteorder::{WriteBytesExt};

use chrono::{DateTime, UTC};

#[derive(Clone)]
pub struct ObjectIdentifier {
    data: Vec<u8>
}

fn encode_base128_int(data: &mut Vec<u8>, n: u32) {
    if n == 0 {
        data.push(0);
        return;
    }
    let mut l = 0;
    let mut i = n;
    while i > 0 {
        l += 1;
        i >>= 7;
    }

    for i in (0..l).rev() {
        let mut o = (n >> (i * 7)) as u8;
        o &= 0x7f;
        if i != 0 {
            o |= 0x80;
        }
        data.push(o);
    }
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        let mut data = Vec::new();
        encode_base128_int(&mut data, 40 * oid[0] + oid[1]);
        for el in oid.iter().skip(2) {
            encode_base128_int(&mut data, *el);
        }
        return Some(ObjectIdentifier {
            data: data
        });
    }
}

pub struct Serializer<'a> {
    writer: &'a mut Vec<u8>
}

impl<'a> Serializer<'a> {
    pub fn new(writer: &'a mut Vec<u8>) -> Serializer<'a> {
        return Serializer {
            writer: writer,
        }
    }

    fn _length_length(&self, length: usize) -> usize {
        let mut i = length;
        let mut num_bytes = 1;
        while i > 255 {
            num_bytes += 1;
            i >>= 8;
        }
        return num_bytes;
    }

    fn _write_length(&mut self, length: usize) {
        if length >= 128 {
            let n = self._length_length(length);
            self.writer.write_u8(0x80 | (n as u8)).unwrap();
            for i in (1..n+1).rev() {
                self.writer.write_u8((length >> ((i - 1) * 8)) as u8).unwrap();
            }
        } else {
            self.writer.write_u8(length as u8).unwrap();
        }
    }

    fn _write_with_tag<F>(&mut self, tag: u8, body: F) where F: Fn() -> Vec<u8> {
        self.writer.write_u8(tag).unwrap();
        let body = body();
        self._write_length(body.len());
        self.writer.write_all(&body).unwrap();
    }

    pub fn write_bool(&mut self, v: bool) {
        return self._write_with_tag(1, || {
            if v {
                return b"\xff".to_vec();
            } else {
                return b"\x00".to_vec();
            }
        })
    }

    fn _int_length(&self, v: i64) -> usize {
        let mut num_bytes = 1;
        let mut i = v;

        while i > 127 || i < -128 {
            num_bytes += 1;
            i >>= 8;
        }
        return num_bytes;
    }

    pub fn write_int(&mut self, v: i64) {
        let n = self._int_length(v);
        return self._write_with_tag(2, || {
            let mut result = Vec::with_capacity(n);
            for i in (1..n+1).rev() {
                result.push((v >> ((i - 1) * 8)) as u8);
            }
            return result;
        })
    }

    pub fn write_octet_string(&mut self, v: &Vec<u8>) {
        return self._write_with_tag(4, || {
            return v.to_vec();
        })
    }

    pub fn write_printable_string(&mut self, v: String) {
        for c in v.chars() {
            if !c.is_ascii() || (
                !c.is_uppercase() &&
                !c.is_lowercase() &&
                !c.is_digit(10) &&
                ![' ', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'].contains(&c)
            ) {
                panic!("Non-printable characters.")
            }
        }
        return self._write_with_tag(19, || {
            return v.as_bytes().to_vec();
        });
    }

    pub fn write_object_identifier(&mut self, v: ObjectIdentifier) {
        return self._write_with_tag(6, || {
            return v.data.to_vec();
        })
    }

    pub fn write_utctime(&mut self, v: DateTime<UTC>) {
        return self._write_with_tag(23, || {
            return format!("{}", v.format("%y%m%d%H%M%SZ")).into_bytes();
        });
    }

    pub fn write_sequence<F>(&mut self, v: F) where F: Fn(&mut Serializer) {
        return self._write_with_tag(48, || {
            return to_vec(&v);
        });
    }
}

pub fn to_vec<F>(f: F) -> Vec<u8> where F: Fn(&mut Serializer) {
    let mut out = Vec::new();
    {
        let mut serializer = Serializer::new(&mut out);
        f(&mut serializer);
    }
    return out;
}

#[cfg(test)]
mod tests {
    use chrono::{TimeZone, UTC};

    use super::{ObjectIdentifier, Serializer, to_vec};

    fn assert_serializes<T, F>(values: Vec<(T, Vec<u8>)>, f: F)
            where T: Clone,  F: Fn(&mut Serializer, T) {
        for (value, expected) in values {
            let out = to_vec(|s| f(s, value.clone()));
            assert_eq!(out, expected);
        }
    }

    #[test]
    fn test_write_bool() {
        assert_serializes(vec![
            (true, b"\x01\x01\xff".to_vec()),
            (false, b"\x01\x01\x00".to_vec()),
        ], |serializer, v| {
            serializer.write_bool(v);
        });
    }

    #[test]
    fn test_write_int() {
        assert_serializes(vec![
            (0, b"\x02\x01\x00".to_vec()),
            (127, b"\x02\x01\x7f".to_vec()),
            (128, b"\x02\x02\x00\x80".to_vec()),
            (256, b"\x02\x02\x01\x00".to_vec()),
            (-128, b"\x02\x01\x80".to_vec()),
            (-129, b"\x02\x02\xff\x7f".to_vec()),
        ], |serializer, v| {
            serializer.write_int(v);
        })
    }

    #[test]
    fn test_write_octet_string() {
        assert_serializes(vec![
            (b"\x01\x02\x03".to_vec(), b"\x04\x03\x01\x02\x03".to_vec()),
        ], |serializer, v| {
            serializer.write_octet_string(&v);
        })
    }

    #[test]
    fn test_write_printable_string() {
        assert_serializes(vec![
            (
                "Test User 1".to_string(),
                b"\x13\x0b\x54\x65\x73\x74\x20\x55\x73\x65\x72\x20\x31".to_vec()
            ),
            (
                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".to_string(),
                b"\x13\x81\x80\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78".to_vec()
            ),
        ], |serializer, v| {
            serializer.write_printable_string(v);
        })
    }

    #[test]
    fn test_write_object_identifier() {
        assert_serializes(vec![
            (
                ObjectIdentifier::new(vec![1, 2, 840, 113549]).unwrap(),
                b"\x06\x06\x2a\x86\x48\x86\xf7\x0d".to_vec()
            ),
            (
                ObjectIdentifier::new(vec![1, 2, 3, 4]).unwrap(),
                b"\x06\x03\x2a\x03\x04".to_vec(),
            ),
            (
                ObjectIdentifier::new(vec![1, 2, 840, 133549, 1, 1, 5]).unwrap(),
                b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05".to_vec(),
            ),
            (
                ObjectIdentifier::new(vec![2, 100, 3]).unwrap(),
                b"\x06\x03\x81\x34\x03".to_vec(),
            ),
        ], |serializer, oid| {
            serializer.write_object_identifier(oid);
        })
    }

    #[test]
    fn test_write_utctime() {
        assert_serializes(vec![
            (
                UTC.ymd(1991, 5, 6).and_hms(23, 45, 40),
                b"\x17\x0d\x39\x31\x30\x35\x30\x36\x32\x33\x34\x35\x34\x30\x5a".to_vec(),
            ),
            (
                UTC.timestamp(0, 0),
                b"\x17\x0d\x37\x30\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30\x5a".to_vec(),
            ),
            (
                UTC.timestamp(1258325776, 0),
                b"\x17\x0d\x30\x39\x31\x31\x31\x35\x32\x32\x35\x36\x31\x36\x5a".to_vec(),
            ),
        ], |serializer, v| {
            serializer.write_utctime(v);
        });
    }

    #[test]
    fn test_write_sequence() {
        assert_serializes(vec![
            ((1, 2), b"\x30\x06\x02\x01\x01\x02\x01\x02".to_vec()),
        ], |serializer, (x, y)| {
            serializer.write_sequence(|s| {
                s.write_int(x);
                s.write_int(y);
            });
        })
    }
}
<commit_msg>fixed the return type on this<commit_after>extern crate byteorder;
extern crate chrono;

use std::ascii::{AsciiExt};
use std::io::{Write};

use byteorder::{WriteBytesExt};

use chrono::{DateTime, UTC};

#[derive(Clone)]
pub struct ObjectIdentifier {
    data: Vec<u8>
}

fn encode_base128_int(data: &mut Vec<u8>, n: u32) {
    if n == 0 {
        data.push(0);
        return;
    }
    let mut l = 0;
    let mut i = n;
    while i > 0 {
        l += 1;
        i >>= 7;
    }

    for i in (0..l).rev() {
        let mut o = (n >> (i * 7)) as u8;
        o &= 0x7f;
        if i != 0 {
            o |= 0x80;
        }
        data.push(o);
    }
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        let mut data = Vec::new();
        encode_base128_int(&mut data, 40 * oid[0] + oid[1]);
        for el in oid.iter().skip(2) {
            encode_base128_int(&mut data, *el);
        }
        return Some(ObjectIdentifier {
            data: data
        });
    }
}

pub struct Serializer<'a> {
    writer: &'a mut Vec<u8>
}

impl<'a> Serializer<'a> {
    pub fn new(writer: &'a mut Vec<u8>) -> Serializer<'a> {
        return Serializer {
            writer: writer,
        }
    }

    fn _length_length(&self, length: usize) -> u8 {
        let mut i = length;
        let mut num_bytes = 1;
        while i > 255 {
            num_bytes += 1;
            i >>= 8;
        }
        return num_bytes;
    }

    fn _write_length(&mut self, length: usize) {
        if length >= 128 {
            let n = self._length_length(length);
            self.writer.write_u8(0x80 | n).unwrap();
            for i in (1..n+1).rev() {
                self.writer.write_u8((length >> ((i - 1) * 8)) as u8).unwrap();
            }
        } else {
            self.writer.write_u8(length as u8).unwrap();
        }
    }

    fn _write_with_tag<F>(&mut self, tag: u8, body: F) where F: Fn() -> Vec<u8> {
        self.writer.write_u8(tag).unwrap();
        let body = body();
        self._write_length(body.len());
        self.writer.write_all(&body).unwrap();
    }

    pub fn write_bool(&mut self, v: bool) {
        return self._write_with_tag(1, || {
            if v {
                return b"\xff".to_vec();
            } else {
                return b"\x00".to_vec();
            }
        })
    }

    fn _int_length(&self, v: i64) -> usize {
        let mut num_bytes = 1;
        let mut i = v;

        while i > 127 || i < -128 {
            num_bytes += 1;
            i >>= 8;
        }
        return num_bytes;
    }

    pub fn write_int(&mut self, v: i64) {
        let n = self._int_length(v);
        return self._write_with_tag(2, || {
            let mut result = Vec::with_capacity(n);
            for i in (1..n+1).rev() {
                result.push((v >> ((i - 1) * 8)) as u8);
            }
            return result;
        })
    }

    pub fn write_octet_string(&mut self, v: &Vec<u8>) {
        return self._write_with_tag(4, || {
            return v.to_vec();
        })
    }

    pub fn write_printable_string(&mut self, v: String) {
        for c in v.chars() {
            if !c.is_ascii() || (
                !c.is_uppercase() &&
                !c.is_lowercase() &&
                !c.is_digit(10) &&
                ![' ', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'].contains(&c)
            ) {
                panic!("Non-printable characters.")
            }
        }
        return self._write_with_tag(19, || {
            return v.as_bytes().to_vec();
        });
    }

    pub fn write_object_identifier(&mut self, v: ObjectIdentifier) {
        return self._write_with_tag(6, || {
            return v.data.to_vec();
        })
    }

    pub fn write_utctime(&mut self, v: DateTime<UTC>) {
        return self._write_with_tag(23, || {
            return format!("{}", v.format("%y%m%d%H%M%SZ")).into_bytes();
        });
    }

    pub fn write_sequence<F>(&mut self, v: F) where F: Fn(&mut Serializer) {
        return self._write_with_tag(48, || {
            return to_vec(&v);
        });
    }
}

pub fn to_vec<F>(f: F) -> Vec<u8> where F: Fn(&mut Serializer) {
    let mut out = Vec::new();
    {
        let mut serializer = Serializer::new(&mut out);
        f(&mut serializer);
    }
    return out;
}

#[cfg(test)]
mod tests {
    use chrono::{TimeZone, UTC};

    use super::{ObjectIdentifier, Serializer, to_vec};

    fn assert_serializes<T, F>(values: Vec<(T, Vec<u8>)>, f: F)
            where T: Clone,  F: Fn(&mut Serializer, T) {
        for (value, expected) in values {
            let out = to_vec(|s| f(s, value.clone()));
            assert_eq!(out, expected);
        }
    }

    #[test]
    fn test_write_bool() {
        assert_serializes(vec![
            (true, b"\x01\x01\xff".to_vec()),
            (false, b"\x01\x01\x00".to_vec()),
        ], |serializer, v| {
            serializer.write_bool(v);
        });
    }

    #[test]
    fn test_write_int() {
        assert_serializes(vec![
            (0, b"\x02\x01\x00".to_vec()),
            (127, b"\x02\x01\x7f".to_vec()),
            (128, b"\x02\x02\x00\x80".to_vec()),
            (256, b"\x02\x02\x01\x00".to_vec()),
            (-128, b"\x02\x01\x80".to_vec()),
            (-129, b"\x02\x02\xff\x7f".to_vec()),
        ], |serializer, v| {
            serializer.write_int(v);
        })
    }

    #[test]
    fn test_write_octet_string() {
        assert_serializes(vec![
            (b"\x01\x02\x03".to_vec(), b"\x04\x03\x01\x02\x03".to_vec()),
        ], |serializer, v| {
            serializer.write_octet_string(&v);
        })
    }

    #[test]
    fn test_write_printable_string() {
        assert_serializes(vec![
            (
                "Test User 1".to_string(),
                b"\x13\x0b\x54\x65\x73\x74\x20\x55\x73\x65\x72\x20\x31".to_vec()
            ),
            (
                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".to_string(),
                b"\x13\x81\x80\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78".to_vec()
            ),
        ], |serializer, v| {
            serializer.write_printable_string(v);
        })
    }

    #[test]
    fn test_write_object_identifier() {
        assert_serializes(vec![
            (
                ObjectIdentifier::new(vec![1, 2, 840, 113549]).unwrap(),
                b"\x06\x06\x2a\x86\x48\x86\xf7\x0d".to_vec()
            ),
            (
                ObjectIdentifier::new(vec![1, 2, 3, 4]).unwrap(),
                b"\x06\x03\x2a\x03\x04".to_vec(),
            ),
            (
                ObjectIdentifier::new(vec![1, 2, 840, 133549, 1, 1, 5]).unwrap(),
                b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05".to_vec(),
            ),
            (
                ObjectIdentifier::new(vec![2, 100, 3]).unwrap(),
                b"\x06\x03\x81\x34\x03".to_vec(),
            ),
        ], |serializer, oid| {
            serializer.write_object_identifier(oid);
        })
    }

    #[test]
    fn test_write_utctime() {
        assert_serializes(vec![
            (
                UTC.ymd(1991, 5, 6).and_hms(23, 45, 40),
                b"\x17\x0d\x39\x31\x30\x35\x30\x36\x32\x33\x34\x35\x34\x30\x5a".to_vec(),
            ),
            (
                UTC.timestamp(0, 0),
                b"\x17\x0d\x37\x30\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30\x5a".to_vec(),
            ),
            (
                UTC.timestamp(1258325776, 0),
                b"\x17\x0d\x30\x39\x31\x31\x31\x35\x32\x32\x35\x36\x31\x36\x5a".to_vec(),
            ),
        ], |serializer, v| {
            serializer.write_utctime(v);
        });
    }

    #[test]
    fn test_write_sequence() {
        assert_serializes(vec![
            ((1, 2), b"\x30\x06\x02\x01\x01\x02\x01\x02".to_vec()),
        ], |serializer, (x, y)| {
            serializer.write_sequence(|s| {
                s.write_int(x);
                s.write_int(y);
            });
        })
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

#![cfg_attr(feature = "use_clippy", feature(plugin))]
#![cfg_attr(feature = "use_clippy", plugin(clippy))]

extern crate rustc_serialize;
extern crate symbolic_expressions;

use std::fmt;

pub use symbolic_expressions::Sexp;
pub use error::*;

pub trait FromSexp {
    fn from_sexp(&Sexp) -> Self;
}

pub use util::read_file;
pub use util::write_file;

pub fn parse_split_quote_aware(s:&str) -> Vec<String> {
    let mut v:Vec<String> = vec![];
    let mut in_q:bool = false;
    let mut q_seen:bool = false;
    let mut s2:String = String::from("");
    for c in s.chars() {
        if !in_q && c == '\"' {
            in_q = true;
            //s2.push(c);
            continue
        }
        if in_q && c == '\"' {
            in_q = false;
            //s2.push(c);
            q_seen = true;
            continue
        }
        if !in_q && c == ' ' {
            if !s2.is_empty() || q_seen {
                v.push(s2.clone());
                s2.clear();
            }
            q_seen = false;
            continue;
        }
        s2.push(c);
    }
    if !s2.is_empty() || q_seen {
        v.push(s2.clone())
    }
    v
}

pub fn parse_split_quote_aware_n(n:usize, s:&str) -> Result<Vec<String>> {
    let mut i = 0;
    let mut v:Vec<String> = vec![];
    let mut in_q:bool = false;
    let mut q_seen:bool = false;
    let mut s2:String = String::from("");
    for c in s.chars() {
        if i == n { // if we're in the nth. just keep collecting in current string
            s2.push(c);
            continue;
        }
        if !in_q && c == '\"' {
            in_q = true;
            //s2.push(c);
            continue
        }
        if in_q && c == '\"' {
            in_q = false;
            //s2.push(c);
            q_seen = true;
            continue
        }
        if !in_q && c == ' ' {
            if !s2.is_empty() || q_seen {
                i += 1;
                v.push(s2.clone());
                s2.clear();
            }
            q_seen = false;
            continue;
        }
        s2.push(c);
    }
    if !s2.is_empty() || q_seen {
        v.push(s2.clone())
    }
    if v.len() < n {
        return str_error(format!("expecting {} elements in {}", n, s))
    }
    Ok(v)
}

#[derive(Debug)]
pub enum KicadFile {
    Unknown(String),
    Module(footprint::Module),
    Schematic(schematic::Schematic),
    Layout(layout::Layout),
    SymbolLib(symbol_lib::SymbolLib),
    Project(project::Project),
}

#[derive(PartialEq)]
pub enum Expected {
    Module,
    Schematic,
    Layout,
    SymbolLib,
    Project,
    Any,
}

impl fmt::Display for KicadFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        match *self {
            KicadFile::Unknown(ref x)   => write!(f, "unknown: {}", x),
            KicadFile::Module(_)    => write!(f, "module"),
            KicadFile::Schematic(_) => write!(f, "schematic"),
            KicadFile::Layout(_)    => write!(f, "layout"),
            KicadFile::SymbolLib(_) => write!(f, "symbollib"),
            KicadFile::Project(_)   => write!(f, "project"),
        }
    }
}

pub fn read_kicad_file(name: &str, expected:Expected) -> Result<KicadFile> {
    let pb = std::path::PathBuf::from(name);
    let data = try!(read_file(name));
    let mut msg = String::new();
    match footprint::parse(&data) {
        Ok(module) => return Ok(KicadFile::Module(module)),
        Err(x) => { if expected == Expected::Module { msg = format!("{}", x) } },
    }
    match schematic::parse(Some(pb), &data) {
        Ok(sch) => return Ok(KicadFile::Schematic(sch)),
        Err(x) => { if expected == Expected::Schematic { msg = format!("{}", x) } },
    }
    match layout::parse(&data) {
        Ok(layout) => return Ok(KicadFile::Layout(layout)),
        Err(x) => { if expected == Expected::Layout { msg = format!("{}", x) } },
    }
    match symbol_lib::parse_str(&data) {
        Ok(sl) => return Ok(KicadFile::SymbolLib(sl)),
        Err(x) => { if expected == Expected::SymbolLib { msg = format!("{}", x) } },
    }
    match project::parse_str(&data) {
        Ok(p) => return Ok(KicadFile::Project(p)),
        Err(x) => { if expected == Expected::Project { msg = format!("{}", x) } },
    }
    Ok(KicadFile::Unknown(format!("{}: {}", name, msg)))
}

pub fn read_module(name: &str) -> Result<footprint::Module> {
    match try!(read_kicad_file(name, Expected::Module)) {
        KicadFile::Module(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_schematic(name: &str) -> Result<schematic::Schematic> {
    match try!(read_kicad_file(name, Expected::Schematic)) {
        KicadFile::Schematic(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_layout(name: &str) -> Result<layout::Layout> {
    match try!(read_kicad_file(name, Expected::Layout)) {
        KicadFile::Layout(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_symbol_lib(name: &str) -> Result<symbol_lib::SymbolLib> {
    match try!(read_kicad_file(name, Expected::SymbolLib)) {
        KicadFile::SymbolLib(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_project(name: &str) -> Result<project::Project> {
    match try!(read_kicad_file(name, Expected::Project)) {
        KicadFile::Project(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub mod error;
pub mod footprint;
pub mod schematic;
pub mod layout;
pub mod symbol_lib;
pub mod project;
pub mod util;
mod formatter;
<commit_msg>add write_layout<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

#![cfg_attr(feature = "use_clippy", feature(plugin))]
#![cfg_attr(feature = "use_clippy", plugin(clippy))]

extern crate rustc_serialize;
extern crate symbolic_expressions;

use std::fmt;

pub use symbolic_expressions::Sexp;
pub use error::*;

pub trait FromSexp {
    fn from_sexp(&Sexp) -> Self;
}

pub use util::read_file;
pub use util::write_file;

pub fn parse_split_quote_aware(s:&str) -> Vec<String> {
    let mut v:Vec<String> = vec![];
    let mut in_q:bool = false;
    let mut q_seen:bool = false;
    let mut s2:String = String::from("");
    for c in s.chars() {
        if !in_q && c == '\"' {
            in_q = true;
            //s2.push(c);
            continue
        }
        if in_q && c == '\"' {
            in_q = false;
            //s2.push(c);
            q_seen = true;
            continue
        }
        if !in_q && c == ' ' {
            if !s2.is_empty() || q_seen {
                v.push(s2.clone());
                s2.clear();
            }
            q_seen = false;
            continue;
        }
        s2.push(c);
    }
    if !s2.is_empty() || q_seen {
        v.push(s2.clone())
    }
    v
}

pub fn parse_split_quote_aware_n(n:usize, s:&str) -> Result<Vec<String>> {
    let mut i = 0;
    let mut v:Vec<String> = vec![];
    let mut in_q:bool = false;
    let mut q_seen:bool = false;
    let mut s2:String = String::from("");
    for c in s.chars() {
        if i == n { // if we're in the nth. just keep collecting in current string
            s2.push(c);
            continue;
        }
        if !in_q && c == '\"' {
            in_q = true;
            //s2.push(c);
            continue
        }
        if in_q && c == '\"' {
            in_q = false;
            //s2.push(c);
            q_seen = true;
            continue
        }
        if !in_q && c == ' ' {
            if !s2.is_empty() || q_seen {
                i += 1;
                v.push(s2.clone());
                s2.clear();
            }
            q_seen = false;
            continue;
        }
        s2.push(c);
    }
    if !s2.is_empty() || q_seen {
        v.push(s2.clone())
    }
    if v.len() < n {
        return str_error(format!("expecting {} elements in {}", n, s))
    }
    Ok(v)
}

#[derive(Debug)]
pub enum KicadFile {
    Unknown(String),
    Module(footprint::Module),
    Schematic(schematic::Schematic),
    Layout(layout::Layout),
    SymbolLib(symbol_lib::SymbolLib),
    Project(project::Project),
}

#[derive(PartialEq)]
pub enum Expected {
    Module,
    Schematic,
    Layout,
    SymbolLib,
    Project,
    Any,
}

impl fmt::Display for KicadFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        match *self {
            KicadFile::Unknown(ref x)   => write!(f, "unknown: {}", x),
            KicadFile::Module(_)    => write!(f, "module"),
            KicadFile::Schematic(_) => write!(f, "schematic"),
            KicadFile::Layout(_)    => write!(f, "layout"),
            KicadFile::SymbolLib(_) => write!(f, "symbollib"),
            KicadFile::Project(_)   => write!(f, "project"),
        }
    }
}

pub fn read_kicad_file(name: &str, expected:Expected) -> Result<KicadFile> {
    let pb = std::path::PathBuf::from(name);
    let data = try!(read_file(name));
    let mut msg = String::new();
    match footprint::parse(&data) {
        Ok(module) => return Ok(KicadFile::Module(module)),
        Err(x) => { if expected == Expected::Module { msg = format!("{}", x) } },
    }
    match schematic::parse(Some(pb), &data) {
        Ok(sch) => return Ok(KicadFile::Schematic(sch)),
        Err(x) => { if expected == Expected::Schematic { msg = format!("{}", x) } },
    }
    match layout::parse(&data) {
        Ok(layout) => return Ok(KicadFile::Layout(layout)),
        Err(x) => { if expected == Expected::Layout { msg = format!("{}", x) } },
    }
    match symbol_lib::parse_str(&data) {
        Ok(sl) => return Ok(KicadFile::SymbolLib(sl)),
        Err(x) => { if expected == Expected::SymbolLib { msg = format!("{}", x) } },
    }
    match project::parse_str(&data) {
        Ok(p) => return Ok(KicadFile::Project(p)),
        Err(x) => { if expected == Expected::Project { msg = format!("{}", x) } },
    }
    Ok(KicadFile::Unknown(format!("{}: {}", name, msg)))
}

pub fn read_module(name: &str) -> Result<footprint::Module> {
    match try!(read_kicad_file(name, Expected::Module)) {
        KicadFile::Module(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_schematic(name: &str) -> Result<schematic::Schematic> {
    match try!(read_kicad_file(name, Expected::Schematic)) {
        KicadFile::Schematic(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_layout(name: &str) -> Result<layout::Layout> {
    match try!(read_kicad_file(name, Expected::Layout)) {
        KicadFile::Layout(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn write_layout(layout:&layout::Layout, name:&str) -> Result<()> {
    let s = try!(layout::layout_to_string(layout, 0));
    write_file(name, &s)
}

pub fn read_symbol_lib(name: &str) -> Result<symbol_lib::SymbolLib> {
    match try!(read_kicad_file(name, Expected::SymbolLib)) {
        KicadFile::SymbolLib(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub fn read_project(name: &str) -> Result<project::Project> {
    match try!(read_kicad_file(name, Expected::Project)) {
        KicadFile::Project(mo) => Ok(mo),
        x => str_error(format!("unexpected {} in {}", x, name)),
    }
}

pub mod error;
pub mod footprint;
pub mod schematic;
pub mod layout;
pub mod symbol_lib;
pub mod project;
pub mod util;
mod formatter;
<|endoftext|>


<commit_before>mod problem27 {
    // palindrome detector for Strings
    pub fn palindrome(s: &str) -> bool {
        let c = s.chars();
        // c.collect::<Vec<_>>() == c.rev().collect::<Vec<_>>()
        c.clone().eq(c.rev())
    }
}

mod problem28 {
    // flatten for vectors
    #[derive(Debug,Clone)]
    pub enum Seq<T> {
        Item(T),
        Sequence(Vec<Seq<T>>)
    }

    pub fn flatten<T>(s: Seq<T>) -> Vec<T> {
        let mut res = vec![];
        fn inner<T>(s: Seq<T>, acc: &mut Vec<T>) {
            match s {
                Seq::Item(a) => acc.push(a),
                Seq::Sequence(ss) => { for i in ss { inner(i, acc) } },
            }
        }
        inner(s, &mut res);
        res
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn problem27() {
        use problem27::palindrome;
        assert_eq!(true, palindrome("racecar"));
        assert_eq!(false, palindrome("adad"));
        assert_eq!(true, palindrome("人人為我,我為人人"));
        assert_eq!(true, palindrome("아들딸들아"));
        assert_eq!(false, palindrome("Я иду с мечем, судия"));
    }

    #[test]
    fn problem28() {
        use problem28::{flatten, Seq};
        // ["a", ["b", "c"], "d"]
        let s1 = Seq::Sequence(vec![Seq::Item("a"), Seq::Sequence(vec![Seq::Item("b"), Seq::Item("c")]), Seq::Item("d")]);
        println!("flatten({:?}) = {:?}", s1, flatten(s1.clone()));
        assert_eq!(flatten(s1),
                   vec!["a", "b", "c", "d"])
    }
}
<commit_msg>Problem 29<commit_after>mod problem27 {
    // palindrome detector for Strings
    pub fn palindrome(s: &str) -> bool {
        let c = s.chars();
        // c.collect::<Vec<_>>() == c.rev().collect::<Vec<_>>()
        c.clone().eq(c.rev())
    }
}

mod problem28 {
    // flatten for vectors
    #[derive(Debug,Clone)]
    pub enum Seq<T> {
        Item(T),
        Sequence(Vec<Seq<T>>)
    }

    pub fn flatten<T>(s: Seq<T>) -> Vec<T> {
        let mut res = vec![];
        fn inner<T>(s: Seq<T>, acc: &mut Vec<T>) {
            match s {
                Seq::Item(a) => acc.push(a),
                Seq::Sequence(ss) => { for i in ss { inner(i, acc) } },
            }
        }
        inner(s, &mut res);
        res
    }
}

mod problem29 {
    // Given a string, keep its capital letters:
    // "Hello World!" -> "HW"
    fn is_capital(&c: &char) -> bool {
        c >= 'A' && c <= 'Z'
    }

    pub fn capitals(s:&str) -> String {
        s.chars().filter(is_capital).collect()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn problem27() {
        use problem27::palindrome;
        assert_eq!(true, palindrome("racecar"));
        assert_eq!(false, palindrome("adad"));
        assert_eq!(true, palindrome("人人為我,我為人人"));
        assert_eq!(true, palindrome("아들딸들아"));
        assert_eq!(false, palindrome("Я иду с мечем, судия"));
    }

    #[test]
    fn problem28() {
        use problem28::{flatten, Seq};
        // ["a", ["b", "c"], "d"]
        let s1 = Seq::Sequence(vec![Seq::Item("a"), Seq::Sequence(vec![Seq::Item("b"), Seq::Item("c")]), Seq::Item("d")]);
        println!("flatten({:?}) = {:?}", s1, flatten(s1.clone()));
        assert_eq!(flatten(s1),
                   vec!["a", "b", "c", "d"])
    }

    #[test]
    fn problem29() {
        use problem29::capitals;
        assert_eq!(capitals("HeLlO, WoRlD!"), "HLOWRD".to_owned());
        assert_eq!(capitals("nothing"), "".to_owned());
        assert_eq!(capitals("$#A(*&987Zf"), "AZ".to_owned());
    }
}
<|endoftext|>


<commit_before>#![crate_name = "msgpack"]

/// Unstable: this library is still in rapid development and everything may change until 1.0 comes.

extern crate byteorder;
extern crate rustc_serialize as serialize;

pub const MSGPACK_VERSION : u32 = 5;

pub use init::Marker;

pub use decode::serialize::{
    Decoder,
};

pub use encode::serialize::{
    Encoder,
};

mod init;
pub mod encode;
pub mod decode;
pub mod value;

// Suppressed due to instability.
// #[cfg(test)]
// mod bench;

// NOTE: Write about strict integer typing. Sized integers always encoded as sized even if they are
// fit in unsized, i.e 100 -> i32 -> posfix.

// Write about error style.
<commit_msg>[Doc] Root module documentation.<commit_after>//! # The Rust MessagePack Library
//!
//! RMP is a pure Rust [MessagePack](http://msgpack.org) implementation.
//!
//! MessagePack is an efficient binary serialization format.
//!
//! ## Usage
//!
//! To use `rmp`, first add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies.msgpack]
//! rmp = "0.1.0"
//! ```
//!
//! Then, add this to your crate root:
//!
//! ```rust
//! extern crate msgpack;
//! ```
//!
//! ## Features
//!
//! - **Convenient API**
//!
//!   RMP is designed to be lightweight and straightforward. There are low-level API, which gives you
//!   full control on data encoding/decoding process and makes no heap allocations. On the other hand
//!   there are high-level API, which provides you convenient interface using Rust standard library and
//!   compiler reflection, allowing to encode/decode structures using `derive` attribute.
//!
//! - **Clear error handling**
//!
//!   RMP's error system guarantees that you never receive an error enum with unreachable variant.
//!
//! - **Robust and tested**
//!
//!   This project is developed using TDD and CI, so any found bugs will be fixed without breaking
//!   existing functionality.
//!
//! ## Examples
//!
//! Let's try to encode a tuple of int and string.
//!
//! ```rust
//! extern crate msgpack;
//! extern crate rustc_serialize;
//!
//! use rustc_serialize::Encodable;
//! use msgpack::Encoder;
//!
//! fn main() {
//!     let val = (42u8, "the Answer");
//!
//!     // The encoder borrows the bytearray buffer.
//!     let mut buf = [0u8; 13];
//!
//!     val.encode(&mut Encoder::new(&mut &mut buf[..]));
//!
//!     assert_eq!([0x92, 0x2a, 0xaa, 0x74, 0x68, 0x65, 0x20, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72], buf);
//! }
//! ```
//!
//! RMP also allows to automatically serialize/deserialize custom structures using rustc_serialize
//! reflection. To enable this feature, derive RustcEncodable and RustcDecodable attributes as
//! shown in the following example:
//!
//! ```
//! extern crate msgpack;
//! extern crate rustc_serialize;
//!
//! use rustc_serialize::Encodable;
//! use msgpack::Encoder;
//!
//! #[derive(RustcEncodable)]
//! struct Custom {
//!     id: u32,
//!     key: String,
//! }
//!
//! fn main() {
//!     let val = Custom { id: 42u32, key: "the Answer".to_string() };
//!
//!     let mut buf = [0u8; 13];
//!
//!     val.encode(&mut Encoder::new(&mut &mut buf[..]));
//!
//!     assert_eq!([0x92, 0x2a, 0xaa, 0x74, 0x68, 0x65, 0x20, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72], buf);
//! }
//! ```

#![crate_name = "msgpack"]

/// Unstable: this library is still in rapid development and everything may change until 1.0 comes.

extern crate byteorder;
extern crate rustc_serialize as serialize;

pub const MSGPACK_VERSION : u32 = 5;

pub use init::Marker;

pub use decode::serialize::{
    Decoder,
};

pub use encode::serialize::{
    Encoder,
};

mod init;
pub mod encode;
pub mod decode;
pub mod value;

// Suppressed due to instability.
// #[cfg(test)]
// mod bench;

// NOTE: Write about strict integer typing. Sized integers always encoded as sized even if they are
// fit in unsized, i.e 100 -> i32 -> posfix.

// Write about error style.
<|endoftext|>


<commit_before>//! Cookie factory
#![feature(trace_macros)]
//macros:
//* write_vec!(vec, ...) -> Result(nb of bytes)
//* write_slice!(s, ...) -> Result(nb of bytes)
//* write_iterator!(it, ...) -> Result(nb of bytes)
//
//* config can be endianness
// combinator: c!(writer, config, conbinator/serializer)

#[macro_export]
macro_rules! write_vec (
  ($vec: expr,  $submac:ident!($($args:tt)*)) => (
    {
      $submac!(write_vec_impl!($vec), $($args)*)
    }
  )
);

#[macro_export]
macro_rules! write_vec_impl (
  ($vec:expr, $config:expr, Slice, $value: expr) => (
    {
      ($vec).extend(($value).iter().cloned());
      Ok(($value).len())
    }
  );
  ($vec:expr, $config:expr, Byte, $value: expr) => (
    {
      ($vec).push($value);
      let res:Result<usize,()> = Ok(1usize);
      res
    }
  );
);

#[macro_export]
macro_rules! write_slice (
  ($sl:expr, $submac:ident!($($args:tt)*)) => (
    {
      let mut pos = 0;
      $submac!(write_slice_impl!(($sl, pos)), $($args)*)
    }
  );
);

#[macro_export]
macro_rules! write_slice_impl (
  (($sl:expr, $pos:expr), $config:expr, Slice, $value: expr) => (
    {
      if ($value).len() < $sl.len() - $pos {
        std::slice::bytes::copy_memory($value, &mut ($sl)[$pos..($value.length)]);
        $pos += value.length;
        Ok(($value).len())
      } else {
        Err(())
      }
    }
  );
  (($sl:expr, $pos:expr), $config:expr, Byte, $value: expr) => (
    {
      if $sl.len() - $pos > 0 {
        $sl[$pos] = $value as u8;
        $pos += 1;
        Ok(1)
      } else {
        Err(())
      }
    }
  );
);

#[macro_export]
macro_rules! opt (
  ($writer:ident!($($wargs:tt)*), $config:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!($writer!($($wargs)*), $config, $($args)*) {
        Ok(nb) => Ok(nb),
        Err(_) => Ok(0)
      }
    }
  )
);

#[macro_export]
macro_rules! s_many (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr, $submac:ident!( $($args:tt)* )) => (
    {
      for el in $value {
        $submac:ident!( $writer!($($wargs)*), $config, $($args:tt)*, el )
      }
    }
  );
);

#[macro_export]
macro_rules! s_u8 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, $value)
    }
  );
);

#[macro_export]
macro_rules! s_u16 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

#[macro_export]
macro_rules! s_u32 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 24) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 16) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

#[cfg(test)]
mod tests {
  use std::iter;

  #[test]
  fn writer_vec() {
    trace_macros!(true);
    let mut v = Vec::new();
    let res:Result<usize,()> = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_slice() {
    trace_macros!(true);
    let mut v   = Vec::with_capacity(4);
    v.extend(iter::repeat(0).take(4));
    let res = write_slice!(&mut v[..], s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_array() {
    trace_macros!(true);
    let mut v = Vec::new();
    let input = vec![0x7f, 0xff, 0xff, 0xff];

    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
    //assert!(false);
  }
}
<commit_msg>implement serializer chaining<commit_after>//! Cookie factory
#![feature(trace_macros)]
//macros:
//* write_vec!(vec, ...) -> Result(nb of bytes)
//* write_slice!(s, ...) -> Result(nb of bytes)
//* write_iterator!(it, ...) -> Result(nb of bytes)
//
//* config can be endianness
// combinator: c!(writer, config, conbinator/serializer)

#[macro_export]
macro_rules! write_vec (
  ($vec: expr,  $submac:ident!($($args:tt)*)) => (
    {
      $submac!(write_vec_impl!($vec), $($args)*)
    }
  )
);

#[macro_export]
macro_rules! write_vec_impl (
  ($vec:expr, $config:expr, Slice, $value: expr) => (
    {
      ($vec).extend(($value).iter().cloned());
      Ok(($value).len())
    }
  );
  ($vec:expr, $config:expr, Byte, $value: expr) => (
    {
      ($vec).push($value);
      let res:Result<usize,()> = Ok(1usize);
      res
    }
  );
);

#[macro_export]
macro_rules! write_slice (
  ($sl:expr, $submac:ident!($($args:tt)*)) => (
    {
      let mut pos = 0;
      $submac!(write_slice_impl!(($sl, pos)), $($args)*)
    }
  );
);

#[macro_export]
macro_rules! write_slice_impl (
  (($sl:expr, $pos:expr), $config:expr, Slice, $value: expr) => (
    {
      if ($value).len() < $sl.len() - $pos {
        std::slice::bytes::copy_memory($value, &mut ($sl)[$pos..($value.length)]);
        $pos += value.length;
        Ok(($value).len())
      } else {
        Err(())
      }
    }
  );
  (($sl:expr, $pos:expr), $config:expr, Byte, $value: expr) => (
    {
      if $sl.len() - $pos > 0 {
        $sl[$pos] = $value as u8;
        $pos += 1;
        Ok(1)
      } else {
        Err(())
      }
    }
  );
);

#[macro_export]
macro_rules! opt (
  ($writer:ident!($($wargs:tt)*), $config:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!($writer!($($wargs)*), $config, $($args)*) {
        Ok(nb) => Ok(nb),
        Err(_) => Ok(0)
      }
    }
  )
);

#[macro_export]
macro_rules! s_many (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr, $submac:ident!( $($args:tt)* )) => (
    {
      for el in $value {
        $submac:ident!( $writer!($($wargs)*), $config, $($args:tt)*, el )
      }
      Ok(42usize)
    }
  );
);

#[macro_export]
macro_rules! s_chain (
  ($writer:ident!($($wargs:tt)*), $config:expr, $($rest:tt)* ) => (
    s_chain_impl!($writer!($($wargs)*), $config, 0, $($rest)* )
  );
);

#[macro_export]
macro_rules! s_chain_impl (
  ($writer:ident!($($wargs:tt)*), $config:expr, $acc:expr, $submac:ident!( $($args:tt)* ) ~ $($rest:tt)*) => (
    {
      match $submac!( $writer!($($wargs)*), $config, $($args)*) {
        Err(e) => Err(e),
        Ok(i)  => s_chain_impl!($writer!($($wargs)*), $config, $acc + i, $($rest)*)
      }
    }
  );
  ($writer:ident!($($wargs:tt)*), $config:expr, $acc:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!( $writer!($($wargs)*), $config, $($args)*) {
        Err(e) => Err(e),
        Ok(i)  => Ok($acc + i)
      }
    }
  );
);

#[macro_export]
macro_rules! s_apply (
  ($writer:ident!($($wargs:tt)*), $config:expr, $($args:tt)*) => (
    {
      $writer!($($wargs)*, $config, $($args)*)
    }
  );
);

#[macro_export]
macro_rules! s_u8 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, $value)
    }
  );
);

#[macro_export]
macro_rules! s_u16 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      s_chain!($writer!($($wargs)*), $config,
        s_apply!(Byte, ($value >> 8) as u8)  ~
        s_apply!(Byte, $value as u8)
      )
    }
  );
);

#[macro_export]
macro_rules! s_u32 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      s_chain!($writer!($($wargs)*), $config,
        s_apply!(Byte, ($value >> 24) as u8) ~
        s_apply!(Byte, ($value >> 16) as u8) ~
        s_apply!(Byte, ($value >> 8) as u8)  ~
        s_apply!(Byte, $value as u8)
      )
    }
  );
);

#[cfg(test)]
mod tests {
  use std::iter;

  #[test]
  fn writer_vec() {
    trace_macros!(true);
    let mut v = Vec::new();
    let res:Result<usize,()> = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_slice() {
    trace_macros!(true);
    let mut v   = Vec::with_capacity(4);
    v.extend(iter::repeat(0).take(4));
    let res = write_slice!(&mut v[..], s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_array() {
    trace_macros!(true);
    let mut v = Vec::new();
    let input = vec![0x7f, 0xff, 0xff, 0xff];

    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
    //assert!(false);
  }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq, Eq)]
struct GitStatus {
    branch: Option<BranchInfo>,
    upstream: Option<UpstreamInfo>,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

#[derive(Debug, PartialEq, Eq)]
struct BranchInfo {
    object_id: String,
    branch: Option<String>,
}

#[derive(Debug, PartialEq, Eq)]
struct UpstreamInfo {
    upstream: String,
    ahead: bool,
    behind: bool,
}

impl GitStatus {
    fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            branch: None,
            upstream: None,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                },
                Some("1") | Some("2") => {
                    if let Some(changes) = words.next() {
                        let mut changes = changes.chars();
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.staged = true,
                            _ => {},
                        }
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.unstaged = true,
                            _ => {},
                        }
                    }
                },
                Some("?") => {
                    s.untracked = true;
                },
                Some("!") => {
                    s.ignored = true;
                },
                _ => {},
            }
        }
        Ok(s)
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\n";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                branch: None,
                upstream: None,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_branch() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().branch,
            Some(BranchInfo {
                object_id: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: Some(String::from("master")),
            })
        );
    }

    #[test]
    fn parse_detached_head() {
        let test_status = "\
# branch.oid 2a7bb916bba69a5fc9d428acc80b1bce64e3e0bc
# branch.head (detached)
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().branch,
            Some(BranchInfo {
                object_id: String::from("2a7bb916bba69a5fc9d428acc80b1bce64e3e0bc"),
                branch: None
            })
        );
    }

    #[test]
    fn parse_upstream() {
        let test_status = "\
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().upstream,
            Some(UpstreamInfo {
                upstream: String::from("origin/master"),
                ahead: true,
                behind: true,
            })
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "? foo.txt\n";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "! node_modules/\n";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                branch: Some(BranchInfo {
                    object_id: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                    branch: Some(String::from("master")),
                }),
                upstream: Some(UpstreamInfo {
                    upstream: String::from("origin/master"),
                    ahead: true,
                    behind: true,
                }),
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }
}
<commit_msg>Include deletion in dirty test<commit_after>#[derive(Debug, PartialEq, Eq)]
struct GitStatus {
    branch: Option<BranchInfo>,
    upstream: Option<UpstreamInfo>,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

#[derive(Debug, PartialEq, Eq)]
struct BranchInfo {
    object_id: String,
    branch: Option<String>,
}

#[derive(Debug, PartialEq, Eq)]
struct UpstreamInfo {
    upstream: String,
    ahead: bool,
    behind: bool,
}

impl GitStatus {
    fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            branch: None,
            upstream: None,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                },
                Some("1") | Some("2") => {
                    if let Some(changes) = words.next() {
                        let mut changes = changes.chars();
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.staged = true,
                            _ => {},
                        }
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.unstaged = true,
                            _ => {},
                        }
                    }
                },
                Some("?") => {
                    s.untracked = true;
                },
                Some("!") => {
                    s.ignored = true;
                },
                _ => {},
            }
        }
        Ok(s)
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\n";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                branch: None,
                upstream: None,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_branch() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().branch,
            Some(BranchInfo {
                object_id: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: Some(String::from("master")),
            })
        );
    }

    #[test]
    fn parse_detached_head() {
        let test_status = "\
# branch.oid 2a7bb916bba69a5fc9d428acc80b1bce64e3e0bc
# branch.head (detached)
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().branch,
            Some(BranchInfo {
                object_id: String::from("2a7bb916bba69a5fc9d428acc80b1bce64e3e0bc"),
                branch: None
            })
        );
    }

    #[test]
    fn parse_upstream() {
        let test_status = "\
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap().upstream,
            Some(UpstreamInfo {
                upstream: String::from("origin/master"),
                ahead: true,
                behind: true,
            })
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md\n";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "? foo.txt\n";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "! node_modules/\n";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                branch: Some(BranchInfo {
                    object_id: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                    branch: Some(String::from("master")),
                }),
                upstream: Some(UpstreamInfo {
                    upstream: String::from("origin/master"),
                    ahead: true,
                    behind: true,
                }),
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }
}
<|endoftext|>


<commit_before>use crate::config::*;
use crate::errors::*;
use crate::ptrace_control::*;
use crate::statemachine::*;
use crate::test_loader::*;
use crate::traces::*;
use cargo::core::{compiler::CompileMode, Package, Shell, Workspace};
use cargo::ops;
use cargo::ops::{CompileOptions, CompileFilter, Packages, CleanOptions, clean, compile, TestOptions};
use cargo::util::{homedir, Config as CargoConfig};
use log::{debug, info, trace, warn};
use nix::unistd::*;
use std::env;
use std::ffi::CString;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

pub mod breakpoint;
pub mod config;
pub mod errors;
pub mod report;
mod source_analysis;
mod statemachine;
pub mod test_loader;
pub mod traces;

/// Should be unnecessary with a future nix crate release.
mod personality;
mod ptrace_control;

static DOCTEST_FOLDER: &str = "target/doctests";

pub fn run(config: &Config) -> Result<(), RunError> {
    let (tracemap, ret) = launch_tarpaulin(config)?;
    report_coverage(config, &tracemap)?;

    if ret == 0 {
        Ok(())
    } else {
        Err(RunError::TestFailed)
    }
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(config: &Config) -> Result<(TraceMap, i32), RunError> {
    setup_environment(&config);
    cargo::core::enable_nightly_features();
    let cwd = match config.manifest.parent() {
        Some(p) => p.to_path_buf(),
        None => PathBuf::new(),
    };
    let home = match homedir(&cwd) {
        Some(h) => h,
        None => {
            warn!("Warning failed to find home directory.");
            PathBuf::new()
        }
    };
    let mut cargo_config = CargoConfig::new(Shell::new(), cwd, home);
    let flag_quiet = if config.verbose { None } else { Some(true) };

    // This shouldn't fail so no checking the error.
    let _ = cargo_config.configure(0u32, flag_quiet, &None, false, false, &None, &[]);

    let workspace = Workspace::new(config.manifest.as_path(), &cargo_config)
        .map_err(|e| RunError::Manifest(e.to_string()))?;

    let mut compile_options = get_compile_options(&config, &cargo_config)?;

    info!("Running Tarpaulin");

    if config.force_clean {
        debug!("Cleaning project");
        // Clean isn't expected to fail and if it does it likely won't have an effect
        let clean_opt = CleanOptions {
            config: &cargo_config,
            spec: vec![],
            target: None,
            release: false,
            doc: false,
        };
        let _ = clean(&workspace, &clean_opt);
    }
    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    info!("Building project");
    for copt in compile_options.drain(..) {
        let run_result = match copt.build_config.mode {
            CompileMode::Test => run_tests(&workspace, copt, config),
            CompileMode::Doctest => run_doctests(&workspace, copt, config),
            e => {
                debug!("Internal tarpaulin error. Unsupported compile mode {:?}", e);
                Err(RunError::Internal)
            },
        }?;
        result.merge(&run_result.0);
        return_code |= run_result.1;
    }
    Ok((result, return_code))
}

fn run_tests(workspace: &Workspace, compile_options: CompileOptions, config: &Config) -> Result<(TraceMap, i32), RunError> {
    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    let compilation = compile(&workspace, &compile_options);
    match compilation {
        Ok(comp) => {
            for &(ref package, _, ref name, ref path) in &comp.tests {
                debug!("Processing {}", name);
                if let Some(res) =
                    get_test_coverage(&workspace, Some(package), path.as_path(), config, false)?
                {
                    result.merge(&res.0);
                    return_code |= res.1;
                }
                if config.run_ignored {
                    if let Some(res) =
                        get_test_coverage(&workspace, Some(package), path.as_path(), config, true)?
                    {
                        result.merge(&res.0);
                        return_code |= res.1;
                    }
                }
            }
            result.dedup();
            Ok((result, return_code))
        }
        Err(e) => return Err(RunError::TestCompile(e.to_string())),
    }
}

fn run_doctests(workspace: &Workspace,
                compile_options: CompileOptions,
                config: &Config) -> Result<(TraceMap, i32), RunError> {
    let mut result = TraceMap::new();
    let mut return_code = 0i32;

    let opts = TestOptions {
        no_run: false,
        no_fail_fast: false,
        compile_opts: compile_options,
    };
    let _ = ops::run_tests(workspace, &opts, &[]);

    // go over all the doc tests and run them.
    let doctest_dir = match config.manifest.parent() {
        Some(p) => p.join(DOCTEST_FOLDER),
        None => PathBuf::from(DOCTEST_FOLDER)
    };
    let walker = WalkDir::new(&doctest_dir).into_iter();
    for dt in walker.filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) {
        trace!("Processing {}", config.strip_project_path(dt.path()).display());
        if let Some(res) = get_test_coverage(&workspace, None, dt.path(), config, false)?
        {
            result.merge(&res.0);
            return_code |= res.1;
        }

    }
    Ok((result, return_code))
}

fn get_compile_options<'a>(config: &Config,
                       cargo_config: &'a CargoConfig) -> Result<Vec<CompileOptions<'a>>, RunError> {
    let mut result = Vec::new();
    for run_type in &config.run_types {
        let mut copt = CompileOptions::new(cargo_config, (*run_type).into())
            .map_err(|e| RunError::Cargo(e.to_string()))?;
        if run_type == &RunType::Tests {
            if let CompileFilter::Default { ref mut required_features_filterable } = copt.filter
            {
                *required_features_filterable = true;
            }
        } else if run_type == &RunType::Doctests {
            copt.filter = CompileFilter::new(true, vec![], false, vec![], false, vec![], false, vec![], false, false);
        }

        copt.features = config.features.clone();
        copt.all_features = config.all_features;
        copt.no_default_features = config.no_default_features;
        copt.build_config.release = config.release;
        copt.spec = match Packages::from_flags(
            config.all,
            config.exclude.clone(),
            config.packages.clone(),
        ) {
            Ok(spec) => spec,
            Err(e) => {
                return Err(RunError::Packages(e.to_string()));
            }
        };
        result.push(copt);
    }
    Ok(result)
}

fn setup_environment(config: &Config) {
    let rustflags = "RUSTFLAGS";
    let mut value =
        " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 -C debuginfo=2 "
            .to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!("--persist-doctests {} -Z unstable-options ", DOCTEST_FOLDER);
    if let Ok(vtemp) = env::var(rustdoc) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustdoc, value);
}

fn accumulate_lines(
    (mut acc, mut group): (Vec<String>, Vec<u64>),
    next: u64,
) -> (Vec<String>, Vec<u64>) {
    if let Some(last) = group.last().cloned() {
        if next == last + 1 {
            group.push(next);
            (acc, group)
        } else {
            match (group.first(), group.last()) {
                (Some(first), Some(last)) if first == last => {
                    acc.push(format!("{}", first));
                }
                (Some(first), Some(last)) => {
                    acc.push(format!("{}-{}", first, last));
                }
                (Some(_), None) | (None, _) => (),
            };
            (acc, vec![next])
        }
    } else {
        group.push(next);
        (acc, group)
    }
}

/// Reports the test coverage using the users preferred method. See config.rs
/// or help text for details.
pub fn report_coverage(config: &Config, result: &TraceMap) -> Result<(), RunError> {
    if !result.is_empty() {
        info!("Coverage Results:");
        if config.verbose {
            println!("|| Uncovered Lines:");
            for (ref key, ref value) in result.iter() {
                let path = config.strip_project_path(key);
                let mut uncovered_lines = vec![];
                for v in value.iter() {
                    match v.stats {
                        traces::CoverageStat::Line(count) if count == 0 => {
                            uncovered_lines.push(v.line);
                        }
                        _ => (),
                    }
                }
                uncovered_lines.sort();
                let (groups, last_group) = uncovered_lines
                    .into_iter()
                    .fold((vec![], vec![]), accumulate_lines);
                let (groups, _) = accumulate_lines((groups, last_group), u64::max_value());
                if !groups.is_empty() {
                    println!("|| {}: {}", path.display(), groups.join(", "));
                }
            }
        }
        println!("|| Tested/Total Lines:");
        for file in result.files() {
            let path = config.strip_project_path(file);
            println!(
                "|| {}: {}/{}",
                path.display(),
                result.covered_in_path(&file),
                result.coverable_in_path(&file)
            );
        }
        let percent = result.coverage_percentage() * 100.0f64;
        // Put file filtering here
        println!(
            "|| \n{:.2}% coverage, {}/{} lines covered",
            percent,
            result.total_covered(),
            result.total_coverable()
        );
        if config.is_coveralls() {
            report::coveralls::export(result, config)?;
            info!("Coverage data sent");
        }

        for g in &config.generate {
            match *g {
                OutputFile::Xml => {
                    report::cobertura::report(result, config).map_err(|e| RunError::XML(e))?;
                }
                OutputFile::Html => {
                    report::html::export(result, config)?;
                }
                _ => {
                    return Err(RunError::OutFormat(
                        "Format currently unsupported".to_string(),
                    ));
                }
            }
        }

        Ok(())
    } else {
        Err(RunError::CovReport(
            "No coverage results collected.".to_string(),
        ))
    }
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    project: &Workspace,
    package: Option<&Package>,
    test: &Path,
    config: &Config,
    ignored: bool,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    if !test.exists() {
        return Ok(None);
    }
    match fork() {
        Ok(ForkResult::Parent { child }) => match collect_coverage(project, test, child, config) {
            Ok(t) => Ok(Some(t)),
            Err(e) => Err(RunError::TestCoverage(e.to_string())),
        },
        Ok(ForkResult::Child) => {
            info!("Launching test");
            execute_test(test, package, ignored, config)?;
            Ok(None)
        }
        Err(err) => Err(RunError::TestCoverage(format!(
            "Failed to run test {}, Error: {}",
            test.display(),
            err.to_string()
        ))),
    }
}

/// Collects the coverage data from the launched test
fn collect_coverage(
    project: &Workspace,
    test_path: &Path,
    test: Pid,
    config: &Config,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(project, test_path, config)?;
    {
        trace!("Test PID is {}", test);
        let (mut state, mut data) = create_state_machine(test, &mut traces, config);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(
    test: &Path,
    package: Option<&Package>,
    ignored: bool,
    config: &Config,
) -> Result<(), RunError> {
    let exec_path = CString::new(test.to_str().unwrap()).unwrap();
    match personality::disable_aslr() {
        Ok(_) => {}
        Err(e) => return Err(RunError::TestRuntime(format!("ASLR disable failed: {}", e))),
    }
    match request_trace() {
        Ok(_) => {}
        Err(e) => return Err(RunError::Trace(e.to_string())),
    }
    info!("running {}", test.display());
    if let Some(pack) = package {
        if let Some(parent) = pack.manifest_path().parent() {
            let _ = env::set_current_dir(parent);
        }
    }

    let mut envars: Vec<CString> = vec![CString::new("RUST_TEST_THREADS=1").unwrap()];
    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    } else {
        argv.push(CString::new("--quiet").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }
    execve(&exec_path, &argv, envars.as_slice()).unwrap();

    Ok(())
}
<commit_msg>Fixed result merging.<commit_after>use crate::config::*;
use crate::errors::*;
use crate::ptrace_control::*;
use crate::statemachine::*;
use crate::test_loader::*;
use crate::traces::*;
use cargo::core::{compiler::CompileMode, Package, Shell, Workspace};
use cargo::ops;
use cargo::ops::{CompileOptions, CompileFilter, Packages, CleanOptions, clean, compile, TestOptions};
use cargo::util::{homedir, Config as CargoConfig};
use log::{debug, info, trace, warn};
use nix::unistd::*;
use std::env;
use std::ffi::CString;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

pub mod breakpoint;
pub mod config;
pub mod errors;
pub mod report;
mod source_analysis;
mod statemachine;
pub mod test_loader;
pub mod traces;

/// Should be unnecessary with a future nix crate release.
mod personality;
mod ptrace_control;

static DOCTEST_FOLDER: &str = "target/doctests";

pub fn run(config: &Config) -> Result<(), RunError> {
    let (tracemap, ret) = launch_tarpaulin(config)?;
    report_coverage(config, &tracemap)?;

    if ret == 0 {
        Ok(())
    } else {
        Err(RunError::TestFailed)
    }
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(config: &Config) -> Result<(TraceMap, i32), RunError> {
    setup_environment(&config);
    cargo::core::enable_nightly_features();
    let cwd = match config.manifest.parent() {
        Some(p) => p.to_path_buf(),
        None => PathBuf::new(),
    };
    let home = match homedir(&cwd) {
        Some(h) => h,
        None => {
            warn!("Warning failed to find home directory.");
            PathBuf::new()
        }
    };
    let mut cargo_config = CargoConfig::new(Shell::new(), cwd, home);
    let flag_quiet = if config.verbose { None } else { Some(true) };

    // This shouldn't fail so no checking the error.
    let _ = cargo_config.configure(0u32, flag_quiet, &None, false, false, &None, &[]);

    let workspace = Workspace::new(config.manifest.as_path(), &cargo_config)
        .map_err(|e| RunError::Manifest(e.to_string()))?;

    let mut compile_options = get_compile_options(&config, &cargo_config)?;

    info!("Running Tarpaulin");

    if config.force_clean {
        debug!("Cleaning project");
        // Clean isn't expected to fail and if it does it likely won't have an effect
        let clean_opt = CleanOptions {
            config: &cargo_config,
            spec: vec![],
            target: None,
            release: false,
            doc: false,
        };
        let _ = clean(&workspace, &clean_opt);
    }
    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    info!("Building project");
    for copt in compile_options.drain(..) {
        let run_result = match copt.build_config.mode {
            CompileMode::Test => run_tests(&workspace, copt, config),
            CompileMode::Doctest => run_doctests(&workspace, copt, config),
            e => {
                debug!("Internal tarpaulin error. Unsupported compile mode {:?}", e);
                Err(RunError::Internal)
            },
        }?;
        result.merge(&run_result.0);
        return_code |= run_result.1;
    }
    result.dedup();
    Ok((result, return_code))
}

fn run_tests(workspace: &Workspace, compile_options: CompileOptions, config: &Config) -> Result<(TraceMap, i32), RunError> {
    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    let compilation = compile(&workspace, &compile_options);
    match compilation {
        Ok(comp) => {
            for &(ref package, _, ref name, ref path) in &comp.tests {
                debug!("Processing {}", name);
                if let Some(res) =
                    get_test_coverage(&workspace, Some(package), path.as_path(), config, false)?
                {
                    result.merge(&res.0);
                    return_code |= res.1;
                }
                if config.run_ignored {
                    if let Some(res) =
                        get_test_coverage(&workspace, Some(package), path.as_path(), config, true)?
                    {
                        result.merge(&res.0);
                        return_code |= res.1;
                    }
                }
            }
            result.dedup();
            Ok((result, return_code))
        }
        Err(e) => return Err(RunError::TestCompile(e.to_string())),
    }
}

fn run_doctests(workspace: &Workspace,
                compile_options: CompileOptions,
                config: &Config) -> Result<(TraceMap, i32), RunError> {
    info!("Running doctests");
    let mut result = TraceMap::new();
    let mut return_code = 0i32;

    let opts = TestOptions {
        no_run: false,
        no_fail_fast: false,
        compile_opts: compile_options,
    };
    let _ = ops::run_tests(workspace, &opts, &[]);

    // go over all the doc tests and run them.
    let doctest_dir = match config.manifest.parent() {
        Some(p) => p.join(DOCTEST_FOLDER),
        None => PathBuf::from(DOCTEST_FOLDER)
    };
    let walker = WalkDir::new(&doctest_dir).into_iter();
    for dt in walker.filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) {
        if let Some(res) = get_test_coverage(&workspace, None, dt.path(), config, false)?
        {
            result.merge(&res.0);
            return_code |= res.1;
        }

    }
    result.dedup();
    Ok((result, return_code))
}

fn get_compile_options<'a>(config: &Config,
                       cargo_config: &'a CargoConfig) -> Result<Vec<CompileOptions<'a>>, RunError> {
    let mut result = Vec::new();
    for run_type in &config.run_types {
        let mut copt = CompileOptions::new(cargo_config, (*run_type).into())
            .map_err(|e| RunError::Cargo(e.to_string()))?;
        if run_type == &RunType::Tests {
            if let CompileFilter::Default { ref mut required_features_filterable } = copt.filter
            {
                *required_features_filterable = true;
            }
        } else if run_type == &RunType::Doctests {
            copt.filter = CompileFilter::new(true, vec![], false, vec![], false, vec![], false, vec![], false, false);
        }

        copt.features = config.features.clone();
        copt.all_features = config.all_features;
        copt.no_default_features = config.no_default_features;
        copt.build_config.release = config.release;
        copt.spec = match Packages::from_flags(
            config.all,
            config.exclude.clone(),
            config.packages.clone(),
        ) {
            Ok(spec) => spec,
            Err(e) => {
                return Err(RunError::Packages(e.to_string()));
            }
        };
        result.push(copt);
    }
    Ok(result)
}

fn setup_environment(config: &Config) {
    let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 -C debuginfo=2 ";
    let rustflags = "RUSTFLAGS";
    let mut value = common_opts.to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!("{} --persist-doctests {} -Z unstable-options ", common_opts, DOCTEST_FOLDER);
    if let Ok(vtemp) = env::var(rustdoc) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustdoc, value);
}

fn accumulate_lines(
    (mut acc, mut group): (Vec<String>, Vec<u64>),
    next: u64,
) -> (Vec<String>, Vec<u64>) {
    if let Some(last) = group.last().cloned() {
        if next == last + 1 {
            group.push(next);
            (acc, group)
        } else {
            match (group.first(), group.last()) {
                (Some(first), Some(last)) if first == last => {
                    acc.push(format!("{}", first));
                }
                (Some(first), Some(last)) => {
                    acc.push(format!("{}-{}", first, last));
                }
                (Some(_), None) | (None, _) => (),
            };
            (acc, vec![next])
        }
    } else {
        group.push(next);
        (acc, group)
    }
}

/// Reports the test coverage using the users preferred method. See config.rs
/// or help text for details.
pub fn report_coverage(config: &Config, result: &TraceMap) -> Result<(), RunError> {
    if !result.is_empty() {
        info!("Coverage Results:");
        if config.verbose {
            println!("|| Uncovered Lines:");
            for (ref key, ref value) in result.iter() {
                let path = config.strip_project_path(key);
                let mut uncovered_lines = vec![];
                for v in value.iter() {
                    match v.stats {
                        traces::CoverageStat::Line(count) if count == 0 => {
                            uncovered_lines.push(v.line);
                        }
                        _ => (),
                    }
                }
                uncovered_lines.sort();
                let (groups, last_group) = uncovered_lines
                    .into_iter()
                    .fold((vec![], vec![]), accumulate_lines);
                let (groups, _) = accumulate_lines((groups, last_group), u64::max_value());
                if !groups.is_empty() {
                    println!("|| {}: {}", path.display(), groups.join(", "));
                }
            }
        }
        println!("|| Tested/Total Lines:");
        for file in result.files() {
            let path = config.strip_project_path(file);
            println!(
                "|| {}: {}/{}",
                path.display(),
                result.covered_in_path(&file),
                result.coverable_in_path(&file)
            );
        }
        let percent = result.coverage_percentage() * 100.0f64;
        // Put file filtering here
        println!(
            "|| \n{:.2}% coverage, {}/{} lines covered",
            percent,
            result.total_covered(),
            result.total_coverable()
        );
        if config.is_coveralls() {
            report::coveralls::export(result, config)?;
            info!("Coverage data sent");
        }

        for g in &config.generate {
            match *g {
                OutputFile::Xml => {
                    report::cobertura::report(result, config).map_err(|e| RunError::XML(e))?;
                }
                OutputFile::Html => {
                    report::html::export(result, config)?;
                }
                _ => {
                    return Err(RunError::OutFormat(
                        "Format currently unsupported".to_string(),
                    ));
                }
            }
        }

        Ok(())
    } else {
        Err(RunError::CovReport(
            "No coverage results collected.".to_string(),
        ))
    }
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    project: &Workspace,
    package: Option<&Package>,
    test: &Path,
    config: &Config,
    ignored: bool,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    if !test.exists() {
        return Ok(None);
    }
    match fork() {
        Ok(ForkResult::Parent { child }) => match collect_coverage(project, test, child, config) {
            Ok(t) => Ok(Some(t)),
            Err(e) => Err(RunError::TestCoverage(e.to_string())),
        },
        Ok(ForkResult::Child) => {
            info!("Launching test");
            execute_test(test, package, ignored, config)?;
            Ok(None)
        }
        Err(err) => Err(RunError::TestCoverage(format!(
            "Failed to run test {}, Error: {}",
            test.display(),
            err.to_string()
        ))),
    }
}

/// Collects the coverage data from the launched test
fn collect_coverage(
    project: &Workspace,
    test_path: &Path,
    test: Pid,
    config: &Config,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(project, test_path, config)?;
    {
        trace!("Test PID is {}", test);
        let (mut state, mut data) = create_state_machine(test, &mut traces, config);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(
    test: &Path,
    package: Option<&Package>,
    ignored: bool,
    config: &Config,
) -> Result<(), RunError> {
    let exec_path = CString::new(test.to_str().unwrap()).unwrap();
    match personality::disable_aslr() {
        Ok(_) => {}
        Err(e) => return Err(RunError::TestRuntime(format!("ASLR disable failed: {}", e))),
    }
    match request_trace() {
        Ok(_) => {}
        Err(e) => return Err(RunError::Trace(e.to_string())),
    }
    info!("running {}", test.display());
    if let Some(pack) = package {
        if let Some(parent) = pack.manifest_path().parent() {
            let _ = env::set_current_dir(parent);
        }
    }

    let mut envars: Vec<CString> = vec![CString::new("RUST_TEST_THREADS=1").unwrap()];
    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    } else {
        argv.push(CString::new("--quiet").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }
    execve(&exec_path, &argv, envars.as_slice()).unwrap();

    Ok(())
}
<|endoftext|>


<commit_before>pub mod flowgger;

pub fn start(config_file: &str) {
    flowgger::start(config_file);
}
<commit_msg>Added docstring to public library entry point<commit_after>pub mod flowgger;

/// Start a flowgger instance starting from a file path
///
/// # Parameters
/// - `config_file`: path to a configuration file in &str format
///
/// # Panics
/// This panics when the configuration file was not able to be parsed, when there's non supported input/outputs or encoder/decoders in the configuration
/// file.
pub fn start(config_file: &str) {
    flowgger::start(config_file);
}
<|endoftext|>