<commit_before>#[cfg(feature = "serialize")]
pub mod serialize {

use tls::*;
use tls_extensions::TlsExtension;
use rusticata_macros::*;

pub fn gen_tls_extension<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsExtension) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        _ => Err(GenError::NotYetImplemented),
    }
}

pub fn gen_tls_sessionid<'a,'b>(x:(&'a mut [u8],usize),m:&Option<&'b [u8]>) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &None    => gen_be_u8!(x,0),
        &Some(o) => {
            do_gen!(
                x,
                gen_be_u8!(o.len() as u8) >>
                gen_slice!(o)
            )
        }
    }
}

pub fn gen_tls_hellorequest<'a>(x:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
        gen_be_u8!(TlsHandshakeType::HelloRequest as u8) >>
        gen_be_u24!(0)
    )
}

pub fn gen_tls_clienthello<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsClientHelloContents) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
                 gen_be_u8!(TlsHandshakeType::ClientHello as u8) >>
        ofs_len: gen_skip!(3) >>
        start:   gen_be_u16!(m.version) >>
                 gen_be_u32!(m.rand_time) >>
                 gen_copy!(m.rand_data,28) >>
                 gen_tls_sessionid(&m.session_id) >>
                 gen_be_u16!((m.ciphers.len()*2) as u16) >>
                 gen_many!(m.ciphers,set_be_u16) >>
                 gen_be_u8!(m.comp.len() as u8) >>
                 gen_many!(m.comp,set_be_u8) >>
                 gen_cond!(m.ext.is_some(),gen_slice!(m.ext.unwrap())) >>
        end:     gen_at_offset!(ofs_len,gen_be_u24!(end-start))
    )
}

pub fn gen_tls_messagehandshake<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsMessageHandshake) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &TlsMessageHandshake::HelloRequest => gen_tls_hellorequest(x),
        &TlsMessageHandshake::ClientHello(ref m) => gen_tls_clienthello(x,m),
        _ => Err(GenError::NotYetImplemented),
    }
}

pub fn gen_tls_message<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsMessage) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &TlsMessage::Handshake(ref m) => gen_tls_messagehandshake(x,m),
        _ => Err(GenError::NotYetImplemented),
    }
}

/// Write a TlsPlaintext record to the input slice
///
/// if p.hdr.len is 0, compute the real size of the record
/// otherwise, use the provided length
pub fn gen_tls_plaintext<'a,'b>(x:(&'a mut [u8],usize),p:&'b TlsPlaintext) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
                 gen_be_u8!(p.hdr.record_type) >>
                 gen_be_u16!(p.hdr.version) >>
        ofs_len: gen_be_u16!(p.hdr.len) >>
        // gen_skip!(2) >>
        start:   gen_many_ref!(&p.msg,gen_tls_message) >>
        end:     gen_cond!(p.hdr.len == 0,
                           gen_at_offset!(ofs_len,gen_be_u16!(end-start)))
    )
}

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

    #[test]
    fn serialize_clienthello() {
        let rand_data = [0xff, 0x21, 0xeb, 0x04, 0xc8, 0xa5, 0x38, 0x39, 0x9a,
        0xcf, 0xb7, 0xa3, 0x82, 0x1f, 0x82, 0x6c, 0x49, 0xbc, 0x8b, 0xb8, 0xa9,
        0x03, 0x0a, 0x2d, 0xce, 0x38, 0x0b, 0xf4];
        let ciphers = vec![
            0xc030,
            0xc02c, 0xc028, 0xc024, 0xc014, 0xc00a, 0x00a5,
            0x00a3, 0x00a1, 0x009f, 0x006b, 0x006a, 0x0069,
            0x0068, 0x0039, 0x0038, 0x0037, 0x0036, 0x0088,
            0x0087, 0x0086, 0x0085, 0xc032, 0xc02e, 0xc02a,
            0xc026, 0xc00f, 0xc005, 0x009d, 0x003d, 0x0035,
            0x0084, 0xc02f, 0xc02b, 0xc027, 0xc023, 0xc013,
            0xc009, 0x00a4, 0x00a2, 0x00a0, 0x009e, 0x0067,
            0x0040, 0x003f, 0x003e, 0x0033, 0x0032, 0x0031,
            0x0030, 0x009a, 0x0099, 0x0098, 0x0097, 0x0045,
            0x0044, 0x0043, 0x0042, 0xc031, 0xc02d, 0xc029,
            0xc025, 0xc00e, 0xc004, 0x009c, 0x003c, 0x002f,
            0x0096, 0x0041, 0xc011, 0xc007, 0xc00c, 0xc002,
            0x0005, 0x0004, 0xc012, 0xc008, 0x0016, 0x0013,
            0x0010, 0x000d, 0xc00d, 0xc003, 0x000a, 0x00ff
        ];
        let comp = vec![0x00];

        let expected = TlsPlaintext {
            hdr: TlsRecordHeader {
                record_type: TlsRecordType::Handshake as u8,
                version: 0x0301,
                len: 213,
            },
            msg: vec![TlsMessage::Handshake(
                     TlsMessageHandshake::ClientHello(
                         TlsClientHelloContents {
                             version: 0x0303,
                             rand_time: 0xb29dd787,
                             rand_data: &rand_data,
                             session_id: None,
                             ciphers: ciphers,
                             comp: comp,
                             ext: None,
                         })
                     )]
        };

        {
            let mut mem : [u8; 218] = [0; 218];
            let s = &mut mem[..];

            let res = gen_tls_plaintext((s,0), &expected);
            println!("res: {:?}", res);
            match res {
                Ok((b,_)) => {
                    let res_reparse = parse_tls_plaintext(b);
                    assert_eq!(res_reparse,IResult::Done(&b""[..],expected));
                },
                Err(e)    => println!("Error: {:?}",e),
            };

        }

    }
}

}
<commit_msg>Serialize SNI and max_fragment_length extensions, and add tests<commit_after>#[cfg(feature = "serialize")]
pub mod serialize {

use tls::*;
use tls_extensions::TlsExtension;
use rusticata_macros::*;

#[macro_export]
macro_rules! gen_tagged_extension(
    (($i:expr, $idx:expr), $tag:expr, $submac:ident!( $($args:tt)* )) => (
        do_gen!(($i,$idx),
                   gen_be_u16!($tag) >>
            ofs:   gen_skip!(2) >>
            start: $submac!( $($args)* ) >>
            end:   gen_at_offset!(ofs,gen_be_u16!(end-start))
        )
    );
    (($i:expr, $idx:expr), $tag:expr, $f:ident( $($args:tt)* )) => (
        gen_tagged_extension!(($i,$idx), $tag, $gen_call!($f( $($args)* )))
    );
);

pub fn gen_tls_ext_sni_hostname<'a,'b>(x:(&'a mut [u8],usize),h:&(u8,&'b[u8])) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
        gen_be_u8!(h.0 as u8) >>
        gen_be_u16!(h.1.len() as u16) >>
        gen_slice!(h.1)
    )
}

#[inline]
pub fn gen_tls_ext_sni<'a,'b>(x:(&'a mut [u8],usize),m:&'b Vec<(u8,&'b[u8])>) -> Result<(&'a mut [u8],usize),GenError> {
    gen_tagged_extension!((x.0,x.1), 0x0000, gen_many_ref!(m,gen_tls_ext_sni_hostname))
}

#[inline]
pub fn gen_tls_ext_max_fragment_length<'a,'b>(x:(&'a mut [u8],usize),l:u8) -> Result<(&'a mut [u8],usize),GenError> {
    gen_tagged_extension!((x.0,x.1), 0x0001, gen_be_u8!(l))
}

pub fn gen_tls_extension<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsExtension) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &TlsExtension::SNI(ref v)           => gen_tls_ext_sni(x,&v),
        &TlsExtension::MaxFragmentLength(l) => gen_tls_ext_max_fragment_length(x,l),
        _                                   => Err(GenError::NotYetImplemented),
    }
}

#[inline]
pub fn gen_tls_sessionid<'a,'b>(x:(&'a mut [u8],usize),m:&Option<&'b [u8]>) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &None    => gen_be_u8!(x,0),
        &Some(o) => {
            do_gen!(
                x,
                gen_be_u8!(o.len() as u8) >>
                gen_slice!(o)
            )
        }
    }
}

pub fn gen_tls_hellorequest<'a>(x:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
        gen_be_u8!(TlsHandshakeType::HelloRequest as u8) >>
        gen_be_u24!(0)
    )
}

pub fn gen_tls_clienthello<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsClientHelloContents) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
                 gen_be_u8!(TlsHandshakeType::ClientHello as u8) >>
        ofs_len: gen_skip!(3) >>
        start:   gen_be_u16!(m.version) >>
                 gen_be_u32!(m.rand_time) >>
                 gen_copy!(m.rand_data,28) >>
                 gen_tls_sessionid(&m.session_id) >>
                 gen_be_u16!((m.ciphers.len()*2) as u16) >>
                 gen_many!(m.ciphers,set_be_u16) >>
                 gen_be_u8!(m.comp.len() as u8) >>
                 gen_many!(m.comp,set_be_u8) >>
                 gen_cond!(m.ext.is_some(),gen_slice!(m.ext.unwrap())) >>
        end:     gen_at_offset!(ofs_len,gen_be_u24!(end-start))
    )
}

pub fn gen_tls_messagehandshake<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsMessageHandshake) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &TlsMessageHandshake::HelloRequest => gen_tls_hellorequest(x),
        &TlsMessageHandshake::ClientHello(ref m) => gen_tls_clienthello(x,m),
        _ => Err(GenError::NotYetImplemented),
    }
}

pub fn gen_tls_message<'a,'b>(x:(&'a mut [u8],usize),m:&'b TlsMessage) -> Result<(&'a mut [u8],usize),GenError> {
    match m {
        &TlsMessage::Handshake(ref m) => gen_tls_messagehandshake(x,m),
        _ => Err(GenError::NotYetImplemented),
    }
}

/// Write a TlsPlaintext record to the input slice
///
/// if p.hdr.len is 0, compute the real size of the record
/// otherwise, use the provided length
pub fn gen_tls_plaintext<'a,'b>(x:(&'a mut [u8],usize),p:&'b TlsPlaintext) -> Result<(&'a mut [u8],usize),GenError> {
    do_gen!(
        x,
                 gen_be_u8!(p.hdr.record_type) >>
                 gen_be_u16!(p.hdr.version) >>
        ofs_len: gen_be_u16!(p.hdr.len) >>
        // gen_skip!(2) >>
        start:   gen_many_ref!(&p.msg,gen_tls_message) >>
        end:     gen_cond!(p.hdr.len == 0,
                           gen_at_offset!(ofs_len,gen_be_u16!(end-start)))
    )
}

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

    #[test]
    fn serialize_plaintext() {
        let rand_data = [0xff, 0x21, 0xeb, 0x04, 0xc8, 0xa5, 0x38, 0x39, 0x9a,
        0xcf, 0xb7, 0xa3, 0x82, 0x1f, 0x82, 0x6c, 0x49, 0xbc, 0x8b, 0xb8, 0xa9,
        0x03, 0x0a, 0x2d, 0xce, 0x38, 0x0b, 0xf4];
        let ciphers = vec![
            0xc030,
            0xc02c, 0xc028, 0xc024, 0xc014, 0xc00a, 0x00a5,
            0x00a3, 0x00a1, 0x009f, 0x006b, 0x006a, 0x0069,
            0x0068, 0x0039, 0x0038, 0x0037, 0x0036, 0x0088,
            0x0087, 0x0086, 0x0085, 0xc032, 0xc02e, 0xc02a,
            0xc026, 0xc00f, 0xc005, 0x009d, 0x003d, 0x0035,
            0x0084, 0xc02f, 0xc02b, 0xc027, 0xc023, 0xc013,
            0xc009, 0x00a4, 0x00a2, 0x00a0, 0x009e, 0x0067,
            0x0040, 0x003f, 0x003e, 0x0033, 0x0032, 0x0031,
            0x0030, 0x009a, 0x0099, 0x0098, 0x0097, 0x0045,
            0x0044, 0x0043, 0x0042, 0xc031, 0xc02d, 0xc029,
            0xc025, 0xc00e, 0xc004, 0x009c, 0x003c, 0x002f,
            0x0096, 0x0041, 0xc011, 0xc007, 0xc00c, 0xc002,
            0x0005, 0x0004, 0xc012, 0xc008, 0x0016, 0x0013,
            0x0010, 0x000d, 0xc00d, 0xc003, 0x000a, 0x00ff
        ];
        let comp = vec![0x00];

        let expected = TlsPlaintext {
            hdr: TlsRecordHeader {
                record_type: TlsRecordType::Handshake as u8,
                version: 0x0301,
                len: 213,
            },
            msg: vec![TlsMessage::Handshake(
                     TlsMessageHandshake::ClientHello(
                         TlsClientHelloContents {
                             version: 0x0303,
                             rand_time: 0xb29dd787,
                             rand_data: &rand_data,
                             session_id: None,
                             ciphers: ciphers,
                             comp: comp,
                             ext: None,
                         })
                     )]
        };

        {
            let mut mem : [u8; 218] = [0; 218];
            let s = &mut mem[..];

            let res = gen_tls_plaintext((s,0), &expected);
            match res {
                Ok((b,_)) => {
                    let res_reparse = parse_tls_plaintext(b);
                    assert_eq!(res_reparse,IResult::Done(&b""[..],expected));
                },
                Err(e)    => println!("Error: {:?}",e),
            };

        }

    }

    #[test]
    fn serialize_hellorequest() {
        let mut mem : [u8; 256] = [0; 256];
        let s = &mut mem[..];
        let m = TlsMessageHandshake::HelloRequest;

        let res = gen_tls_messagehandshake((s,0), &m);
        match res {
            Ok((b,_)) => {
                let v = [0, 0, 0, 0];
                assert_eq!(&b[..v.len()],v);
            },
            Err(e)    => println!("Error: {:?}",e),
        };
    }

    #[test]
    fn serialize_tls_ext() {
        let mut mem : [u8; 256] = [0; 256];
        let s = &mut mem[..];
        let ext = vec![
            TlsExtension::SNI(vec![(0,b"www.google.com")]),
        ];

        let res = gen_many_ref!((s,0),ext,gen_tls_extension);
        match res {
            Ok((b,idx)) => {
                let v = [
                    0x00, 0x00, // SNI tag
                    0x00, 0x11, // SNI ext length
                    // element 0:
                    0x00, // type
                    0x00, 0x0e, // length
                    0x77, 0x77, 0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
                    0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
                ];
                assert_eq!(idx,v.len());
                assert_eq!(&b[..v.len()],&v[..]);
            },
            Err(e)    => println!("Error: {:?}",e),
        };
    }

    #[test]
    fn serialize_clienthello() {
        let rand_data = [0xff, 0x21, 0xeb, 0x04, 0xc8, 0xa5, 0x38, 0x39, 0x9a,
        0xcf, 0xb7, 0xa3, 0x82, 0x1f, 0x82, 0x6c, 0x49, 0xbc, 0x8b, 0xb8, 0xa9,
        0x03, 0x0a, 0x2d, 0xce, 0x38, 0x0b, 0xf4];
        let ciphers = vec![ 0xc030, 0xc02c ];
        let comp = vec![0x00];

        let m = TlsMessageHandshake::ClientHello(
            TlsClientHelloContents {
                version: 0x0303,
                rand_time: 0xb29dd787,
                rand_data: &rand_data,
                session_id: None,
                ciphers: ciphers,
                comp: comp,
                ext: None,
            });

        let mut mem : [u8; 256] = [0; 256];
        let s = &mut mem[..];

        let res = gen_tls_messagehandshake((s,0), &m);
        match res {
            Ok((b,idx)) => {
                let v = [
                    0x01, 0x00, 0x00, 0x2b, 0x03, 0x03, // type, length, version
                    0xb2, 0x9d, 0xd7, 0x87, // random time
                    0xff, 0x21, 0xeb, 0x04, 0xc8, 0xa5, 0x38, 0x39, // random data
                    0x9a, 0xcf, 0xb7, 0xa3, 0x82, 0x1f, 0x82, 0x6c,
                    0x49, 0xbc, 0x8b, 0xb8, 0xa9, 0x03, 0x0a, 0x2d,
                    0xce, 0x38, 0x0b, 0xf4,
                    0x00, // session ID
                    0x00, 0x04, 0xc0, 0x30, 0xc0, 0x2c, // ciphers
                    0x01, 0x00, // compression
                ];
                assert_eq!(idx,v.len());
                assert_eq!(&b[..v.len()],&v[..]);
            },
            Err(e)    => println!("Error: {:?}",e),
        };
    }

}

}
<|endoftext|>


<commit_before>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{fs, io};
use std::borrow::Cow;
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            println!("{:?}", filename);
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            regression(&g[0].measurement, &g[1].measurement)
        })
        .flatten()
        .collect();

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Regression>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        println!("checking regressions with the following measurements:");
        for (path, _) in &v_next {
            // TODO this printed nothing. What's the size of this vector?
            println!("{}", path.file_name().map(|x| x.to_string_lossy()).unwrap_or(Cow::from("unknown file")))
        }

        calculate_regressions(&v_next)
    })
}

pub fn exit_properly(regressions: Vec<Regression>) {
    match regressions[..] {
        [] => println!("congrats! no regressions"),
        _ => {
            for r in regressions {
                println!("{:?}", r);
            }
            println!("the above regressions were found.");
            exit(1)
        }
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<commit_msg>add debug line<commit_after>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{fs, io};
use std::borrow::Cow;
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            println!("{:?}", filename);
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            regression(&g[0].measurement, &g[1].measurement)
        })
        .flatten()
        .collect();

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Regression>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        println!("checking regressions with the following measurements:");
        println!("length: {}", &v_next.len());
        for (path, _) in &v_next {
            // TODO this printed nothing. What's the size of this vector?
            println!("{}", path.file_name().map(|x| x.to_string_lossy()).unwrap_or(Cow::from("unknown file")))
        }

        calculate_regressions(&v_next)
    })
}

pub fn exit_properly(regressions: Vec<Regression>) {
    match regressions[..] {
        [] => println!("congrats! no regressions"),
        _ => {
            for r in regressions {
                println!("{:?}", r);
            }
            println!("the above regressions were found.");
            exit(1)
        }
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<|endoftext|>


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

use std::cell::RefCell;

fn main() {
    let mut r = 0;
    let s = 0;
    let x = RefCell::new((&mut r,s));

    let val: &_ = x.borrow().0;
    //~^ ERROR borrowed value does not live long enough
    //~| temporary value dropped here while still borrowed
    //~| temporary value created here
    //~| consider using a `let` binding to increase its lifetime
    println!("{}", val);
}
//~^ temporary value needs to live until here
<commit_msg>Updated test to reflect expected Mir output.<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir

use std::cell::RefCell;

fn main() {
    let mut r = 0;
    let s = 0;
    let x = RefCell::new((&mut r,s));

    let val: &_ = x.borrow().0;
    //[ast]~^ ERROR borrowed value does not live long enough [E0597]
    //[ast]~| NOTE temporary value dropped here while still borrowed
    //[ast]~| NOTE temporary value created here
    //[ast]~| NOTE consider using a `let` binding to increase its lifetime
    //[mir]~^^^^^ ERROR borrowed value does not live long enough (Ast) [E0597]
    //[mir]~| NOTE temporary value dropped here while still borrowed
    //[mir]~| NOTE temporary value created here
    //[mir]~| NOTE consider using a `let` binding to increase its lifetime
    //[mir]~| ERROR borrowed value does not live long enough (Mir) [E0597]
    //[mir]~| NOTE temporary value dropped here while still borrowed
    //[mir]~| NOTE temporary value created here
    //[mir]~| NOTE consider using a `let` binding to increase its lifetime
    println!("{}", val);
}
//[ast]~^ NOTE temporary value needs to live until here
//[mir]~^^ NOTE temporary value needs to live until here
//[mir]~| NOTE temporary value needs to live until here
<|endoftext|>


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

fn foo<T: Iterator>() {
    T::Item; //~ ERROR no associated item named `Item` found for type `T`
}

fn main() { }
<commit_msg>more complete error message<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn foo<T: Iterator>() {
    T::Item; //~ ERROR no associated item named `Item` found for type `T` in the current scope
}

fn main() { }
<|endoftext|>


<commit_before>// Ensure inout asm! operands are marked as used by the liveness pass

// check-pass

#![feature(asm)]
#![allow(dead_code)]
#![warn(unused_assignments)]
#![warn(unused_variables)]

// Test the single inout case
unsafe fn f1(mut src: *const u8) {
    asm!("/*{0}*/", inout(reg) src); //~ WARN value assigned to `src` is never read
}

unsafe fn f2(mut src: *const u8) -> *const u8 {
    asm!("/*{0}*/", inout(reg) src);
    src
}

// Test the split inout case
unsafe fn f3(mut src: *const u8) {
    asm!("/*{0}*/", inout(reg) src => src); //~ WARN value assigned to `src` is never read
}

unsafe fn f4(mut src: *const u8) -> *const u8 {
    asm!("/*{0}*/", inout(reg) src => src);
    src
}

// Tests the use of field projections
struct S {
    field: *mut u8,
}

unsafe fn f5(src: &mut S) {
    asm!("/*{0}*/", inout(reg) src.field);
}

unsafe fn f6(src: &mut S) {
    asm!("/*{0}*/", inout(reg) src.field => src.field);
}

fn main() {}
<commit_msg>Limit liveness-asm tests to x86_64<commit_after>// Ensure inout asm! operands are marked as used by the liveness pass

// only-x86_64
// check-pass

#![feature(asm)]
#![allow(dead_code)]
#![warn(unused_assignments)]
#![warn(unused_variables)]

// Test the single inout case
unsafe fn f1(mut src: *const u8) {
    asm!("/*{0}*/", inout(reg) src); //~ WARN value assigned to `src` is never read
}

unsafe fn f2(mut src: *const u8) -> *const u8 {
    asm!("/*{0}*/", inout(reg) src);
    src
}

// Test the split inout case
unsafe fn f3(mut src: *const u8) {
    asm!("/*{0}*/", inout(reg) src => src); //~ WARN value assigned to `src` is never read
}

unsafe fn f4(mut src: *const u8) -> *const u8 {
    asm!("/*{0}*/", inout(reg) src => src);
    src
}

// Tests the use of field projections
struct S {
    field: *mut u8,
}

unsafe fn f5(src: &mut S) {
    asm!("/*{0}*/", inout(reg) src.field);
}

unsafe fn f6(src: &mut S) {
    asm!("/*{0}*/", inout(reg) src.field => src.field);
}

fn main() {}
<|endoftext|>


<commit_before>use nom::{Err, IResult, Needed};

use std::io;
use std::io::Read;
use std::ptr;
use std::cmp;

use super::StreamProducer;

#[derive(Debug)]
pub enum ErrorKind {
  IO(io::Error),
  Incomplete(usize),
  Consumed(usize),
  EndOfInput,
  Unknown,
}

// Structure that hold a slice of bytes.
pub struct ByteStream<'a> {
  offset: usize,
  bytes: &'a [u8],
}

impl<'a> ByteStream<'a> {
  // Construct a `ByteStream` based on the passed in byte slice.
  pub fn new(bytes: &'a [u8]) -> Self {
    ByteStream {
      offset: 0,
      bytes: bytes,
    }
  }

  // Return the number of bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.bytes.len() - self.offset
  }

  // Return true if the stream contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
}

impl<'a> StreamProducer for ByteStream<'a> {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.is_empty() {
      return Err(ErrorKind::EndOfInput);
    }

    match f(&self.bytes[self.offset..]) {
      IResult::Done(i, o)    => {
        self.offset += self.len() - i.len();

        Ok(o)
      }
      IResult::Incomplete(n) => {
        let mut needed = self.len();

        if let Needed::Size(size) = n {
          needed = size;
        }

        Err(ErrorKind::Incomplete(needed))
      }
      IResult::Error(e)      => {
        if let Err::Position(_, i) = e {
          let consumed = self.len() - i.len();

          self.offset += consumed;

          Err(ErrorKind::Consumed(consumed))
        } else {
          Err(ErrorKind::Unknown)
        }
      }
    }
  }
}

// Growable buffer of bytes.
//
// Mainly used to the `ReadStream` structure but can be used seperately for
// manually filling with some `Read` source.
pub struct Buffer {
  data: Vec<u8>,
  filled: usize,
  offset: usize,
}

impl Buffer {
  // Default constructor for `Buffer`
  pub fn new() -> Self {
    Self::with_capacity(1024)
  }

  // Explicitly set the buffer capacity.
  pub fn with_capacity(capacity: usize) -> Self {
    let mut buffer = Vec::with_capacity(capacity);

    unsafe {
      buffer.set_len(capacity);
    }

    Buffer {
      data: buffer,
      filled: 0,
      offset: 0,
    }
  }

  // Return the number of read bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.filled - self.offset
  }

  // Return true if buffer contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }

  // The set length of the unlining buffer.
  #[inline]
  pub fn capacity(&self) -> usize {
    self.data.len()
  }

  // Return a reference to the slice of unread bytes.
  pub fn as_slice(&self) -> &[u8] {
    &self.data[self.offset..self.filled]
  }

  // Fill the buffer with bytes from a `Read` source.
  pub fn fill<R: Read>(&mut self, reader: &mut R) -> io::Result<usize> {
    reader.read(&mut self.data[self.filled..]).map(|consumed| {
      self.filled += consumed;

      consumed
    })
  }

  // Resize the current buffer
  //
  // This will only allocate data when the size requests is larger than the
  // current capacity of the buffer, otherwise it moves the currently filled
  // data to the beginning of the buffer.
  pub fn resize(&mut self, size: usize) {
    if size > self.data.capacity() {
      self.data.reserve(size);

      let capacity = self.data.capacity();

      unsafe {
        self.data.set_len(capacity);
      }
    }

    if self.data.len() - self.filled < size  {
      let length  = self.filled - self.offset;
      let mut_ptr = self.data.as_mut_ptr();

      unsafe {
        let offset_ptr  = self.data.as_ptr().offset(self.offset as isize);

        ptr::copy(offset_ptr, mut_ptr, length);
      }

      self.filled -= self.offset;
      self.offset  = 0;
    }
  }

  // Move the offset by the amount of consumed bytes.
  pub fn consume(&mut self, consumed: usize) {
    self.offset += consumed;
  }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParserState {
  Incomplete,
  EndOfInput,
}

fn fill<R: Read>(buffer: &mut Buffer, reader: &mut R, needed: usize)
                 -> io::Result<usize> {
  let mut read = 0;

  if buffer.len() < needed {
    buffer.resize(needed);

    while buffer.len() < needed {
      let size_read = try!(buffer.fill(reader));

      if size_read > 0 {
        read += size_read;
      } else {
        break;
      }
    }
  }

  Ok(read)
}

// Structure that hold a reader for a source of bytes.
pub struct ReadStream<R: Read> {
  reader: R,
  buffer: Buffer,
  needed: usize,
  state: ParserState,
}

impl<R> ReadStream<R> where R: Read {
  // Constructor for `ReadStream` based on a `Read` source.
  pub fn new(reader: R) -> Self {
    ReadStream {
      reader: reader,
      buffer: Buffer::new(),
      needed: 0,
      state: ParserState::Incomplete,
    }
  }

  fn fill(&mut self) -> io::Result<usize> {
    let needed = cmp::max(1, self.needed);

    fill(&mut self.buffer, &mut self.reader, needed).map(|consumed| {
      if self.buffer.len() < needed {
        self.state = ParserState::EndOfInput;
      }

      consumed
    })
  }
}

fn from_iresult<T>(buffer: &Buffer, result: IResult<&[u8], T>)
                   -> Result<(usize, T), ErrorKind> {
  match result {
    IResult::Done(i, o)    => Ok((buffer.len() - i.len(), o)),
    IResult::Incomplete(n) => {
      let mut needed = buffer.capacity();

      if let Needed::Size(size) = n {
        needed = size;
      }

      Err(ErrorKind::Incomplete(needed))
    }
    IResult::Error(e)      => {
      if let Err::Position(_, i) = e {
        Err(ErrorKind::Consumed(buffer.len() - i.len()))
      } else {
        Err(ErrorKind::Unknown)
      }
    }
  }
}

impl<R> StreamProducer for ReadStream<R> where R: Read {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.state != ParserState::EndOfInput {
      try!(self.fill().map_err(ErrorKind::IO));
    }

    let mut buffer = &mut self.buffer;

    if buffer.is_empty() {
      self.state = ParserState::EndOfInput;

      return Err(ErrorKind::EndOfInput);
    }

    let result = {
      let iresult = f(buffer.as_slice());

      from_iresult(&buffer, iresult)
    };

    match result {
      Ok((consumed, o)) => {
        buffer.consume(consumed);

        Ok(o)
      }
      Err(kind)         => {
        match kind {
          ErrorKind::Incomplete(needed) => self.needed = needed,
          ErrorKind::Consumed(consumed) => buffer.consume(consumed),
          _                             => (),
        }

        Err(kind)
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use utility::StreamProducer;
  use nom::be_u32;

  #[test]
  fn test_buffer() {
    let mut buffer = Buffer::new();
    let bytes      = b"Hello World";
    let mut reader = &bytes[..];

    assert!(buffer.is_empty());
    assert_eq!(buffer.capacity(), 1024);

    let bytes_read = buffer.fill(&mut reader).unwrap_or(0);
    let bytes_len  = bytes.len();

    assert_eq!(bytes_read, bytes_len);
    assert_eq!(buffer.len(), bytes_len);
    assert_eq!(buffer.as_slice(), bytes);

    buffer.resize(512);
    assert_eq!(buffer.capacity(), 1024);
  }

  #[test]
  fn test_byte_stream() {
    let bytes      = b"Hello World";
    let mut stream = ByteStream::new(bytes);

    assert_eq!(stream.len(), bytes.len());

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444);
    assert_eq!(stream.len(), 7);
  }

  #[test]
  fn test_read_stream() {
    let bytes      = b"Hello World";
    let mut stream = ReadStream::new(&bytes[..]);

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444)
  }
}
<commit_msg>Add documentation for `ReadStream::fill`<commit_after>use nom::{Err, IResult, Needed};

use std::io;
use std::io::Read;
use std::ptr;
use std::cmp;

use super::StreamProducer;

#[derive(Debug)]
pub enum ErrorKind {
  IO(io::Error),
  Incomplete(usize),
  Consumed(usize),
  EndOfInput,
  Unknown,
}

// Structure that hold a slice of bytes.
pub struct ByteStream<'a> {
  offset: usize,
  bytes: &'a [u8],
}

impl<'a> ByteStream<'a> {
  // Construct a `ByteStream` based on the passed in byte slice.
  pub fn new(bytes: &'a [u8]) -> Self {
    ByteStream {
      offset: 0,
      bytes: bytes,
    }
  }

  // Return the number of bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.bytes.len() - self.offset
  }

  // Return true if the stream contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
}

impl<'a> StreamProducer for ByteStream<'a> {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.is_empty() {
      return Err(ErrorKind::EndOfInput);
    }

    match f(&self.bytes[self.offset..]) {
      IResult::Done(i, o)    => {
        self.offset += self.len() - i.len();

        Ok(o)
      }
      IResult::Incomplete(n) => {
        let mut needed = self.len();

        if let Needed::Size(size) = n {
          needed = size;
        }

        Err(ErrorKind::Incomplete(needed))
      }
      IResult::Error(e)      => {
        if let Err::Position(_, i) = e {
          let consumed = self.len() - i.len();

          self.offset += consumed;

          Err(ErrorKind::Consumed(consumed))
        } else {
          Err(ErrorKind::Unknown)
        }
      }
    }
  }
}

// Growable buffer of bytes.
//
// Mainly used to the `ReadStream` structure but can be used seperately for
// manually filling with some `Read` source.
pub struct Buffer {
  data: Vec<u8>,
  filled: usize,
  offset: usize,
}

impl Buffer {
  // Default constructor for `Buffer`
  pub fn new() -> Self {
    Self::with_capacity(1024)
  }

  // Explicitly set the buffer capacity.
  pub fn with_capacity(capacity: usize) -> Self {
    let mut buffer = Vec::with_capacity(capacity);

    unsafe {
      buffer.set_len(capacity);
    }

    Buffer {
      data: buffer,
      filled: 0,
      offset: 0,
    }
  }

  // Return the number of read bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.filled - self.offset
  }

  // Return true if buffer contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }

  // The set length of the unlining buffer.
  #[inline]
  pub fn capacity(&self) -> usize {
    self.data.len()
  }

  // Return a reference to the slice of unread bytes.
  pub fn as_slice(&self) -> &[u8] {
    &self.data[self.offset..self.filled]
  }

  // Fill the buffer with bytes from a `Read` source.
  pub fn fill<R: Read>(&mut self, reader: &mut R) -> io::Result<usize> {
    reader.read(&mut self.data[self.filled..]).map(|consumed| {
      self.filled += consumed;

      consumed
    })
  }

  // Resize the current buffer
  //
  // This will only allocate data when the size requests is larger than the
  // current capacity of the buffer, otherwise it moves the currently filled
  // data to the beginning of the buffer.
  pub fn resize(&mut self, size: usize) {
    if size > self.data.capacity() {
      self.data.reserve(size);

      let capacity = self.data.capacity();

      unsafe {
        self.data.set_len(capacity);
      }
    }

    if self.data.len() - self.filled < size  {
      let length  = self.filled - self.offset;
      let mut_ptr = self.data.as_mut_ptr();

      unsafe {
        let offset_ptr  = self.data.as_ptr().offset(self.offset as isize);

        ptr::copy(offset_ptr, mut_ptr, length);
      }

      self.filled -= self.offset;
      self.offset  = 0;
    }
  }

  // Move the offset by the amount of consumed bytes.
  pub fn consume(&mut self, consumed: usize) {
    self.offset += consumed;
  }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParserState {
  Incomplete,
  EndOfInput,
}

fn fill<R: Read>(buffer: &mut Buffer, reader: &mut R, needed: usize)
                 -> io::Result<usize> {
  let mut read = 0;

  if buffer.len() < needed {
    buffer.resize(needed);

    while buffer.len() < needed {
      let size_read = try!(buffer.fill(reader));

      if size_read > 0 {
        read += size_read;
      } else {
        break;
      }
    }
  }

  Ok(read)
}

// Structure that hold a reader for a source of bytes.
pub struct ReadStream<R: Read> {
  reader: R,
  buffer: Buffer,
  needed: usize,
  state: ParserState,
}

impl<R> ReadStream<R> where R: Read {
  // Constructor for `ReadStream` based on a `Read` source.
  pub fn new(reader: R) -> Self {
    ReadStream {
      reader: reader,
      buffer: Buffer::new(),
      needed: 0,
      state: ParserState::Incomplete,
    }
  }

  // Fill the stream with bytes from a `Read` source.
  fn fill(&mut self) -> io::Result<usize> {
    let needed = cmp::max(1, self.needed);

    fill(&mut self.buffer, &mut self.reader, needed).map(|consumed| {
      if self.buffer.len() < needed {
        self.state = ParserState::EndOfInput;
      }

      consumed
    })
  }
}

fn from_iresult<T>(buffer: &Buffer, result: IResult<&[u8], T>)
                   -> Result<(usize, T), ErrorKind> {
  match result {
    IResult::Done(i, o)    => Ok((buffer.len() - i.len(), o)),
    IResult::Incomplete(n) => {
      let mut needed = buffer.capacity();

      if let Needed::Size(size) = n {
        needed = size;
      }

      Err(ErrorKind::Incomplete(needed))
    }
    IResult::Error(e)      => {
      if let Err::Position(_, i) = e {
        Err(ErrorKind::Consumed(buffer.len() - i.len()))
      } else {
        Err(ErrorKind::Unknown)
      }
    }
  }
}

impl<R> StreamProducer for ReadStream<R> where R: Read {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.state != ParserState::EndOfInput {
      try!(self.fill().map_err(ErrorKind::IO));
    }

    let mut buffer = &mut self.buffer;

    if buffer.is_empty() {
      self.state = ParserState::EndOfInput;

      return Err(ErrorKind::EndOfInput);
    }

    let result = {
      let iresult = f(buffer.as_slice());

      from_iresult(&buffer, iresult)
    };

    match result {
      Ok((consumed, o)) => {
        buffer.consume(consumed);

        Ok(o)
      }
      Err(kind)         => {
        match kind {
          ErrorKind::Incomplete(needed) => self.needed = needed,
          ErrorKind::Consumed(consumed) => buffer.consume(consumed),
          _                             => (),
        }

        Err(kind)
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use utility::StreamProducer;
  use nom::be_u32;

  #[test]
  fn test_buffer() {
    let mut buffer = Buffer::new();
    let bytes      = b"Hello World";
    let mut reader = &bytes[..];

    assert!(buffer.is_empty());
    assert_eq!(buffer.capacity(), 1024);

    let bytes_read = buffer.fill(&mut reader).unwrap_or(0);
    let bytes_len  = bytes.len();

    assert_eq!(bytes_read, bytes_len);
    assert_eq!(buffer.len(), bytes_len);
    assert_eq!(buffer.as_slice(), bytes);

    buffer.resize(512);
    assert_eq!(buffer.capacity(), 1024);
  }

  #[test]
  fn test_byte_stream() {
    let bytes      = b"Hello World";
    let mut stream = ByteStream::new(bytes);

    assert_eq!(stream.len(), bytes.len());

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444);
    assert_eq!(stream.len(), 7);
  }

  #[test]
  fn test_read_stream() {
    let bytes      = b"Hello World";
    let mut stream = ReadStream::new(&bytes[..]);

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444)
  }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustless::server::{header, status};
use rustless::{Api, Nesting};
use valico::json_dsl;
use super::query;
use model::v1::*;
use model;
use params::{coord_param, dataset_param, get_param_array, paginate_param, shape_param, types_param};
use mimir::rubber::Rubber;
use prometheus;
use prometheus::Encoder;
use iron::typemap::Key;
use hyper::mime::Mime;

const DEFAULT_LIMIT: u64 = 10u64;
const DEFAULT_OFFSET: u64 = 0u64;

struct Timer;
impl Key for Timer {
    type Value = prometheus::HistogramTimer;
}

lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = register_counter_vec!(
            "bragi_http_requests_total",
            "Total number of HTTP requests made.",
            &["handler", "method", "status"]
    ).unwrap();

    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    ).unwrap();
}

fn render<T>(
    mut client: rustless::Client,
    obj: T,
) -> Result<rustless::Client, rustless::ErrorResponse>
where
    T: serde::Serialize,
{
    client.set_json_content_type();
    client.set_header(header::AccessControlAllowOrigin::Any);
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint {
                        description: "autocomplete service".to_string(),
                    };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                    }
                };
                let mut resp = rustless::Response::from(
                    status::StatusCode::BadRequest,
                    Box::new(serde_json::to_string(&err).unwrap()),
                );
                resp.set_json_content_type();
                Some(resp)
            });

            api.before(|client, _params| {
                let method = client.endpoint.method.to_string();

                HTTP_REQ_HISTOGRAM
                    .get_metric_with(&labels!{
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                    })
                    .map(|timer| {
                        client.ext.insert::<Timer>(timer.start_timer());
                    })
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_REQ_HISTOGRAM metrics"; "err" => err.to_string());
                    });
                Ok(())
            });

            api.after(|client, _params| {
                let method = client.endpoint.method.to_string();
                let code = client.status().to_string();

                HTTP_COUNTER
                    .get_metric_with(&labels!{
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                        "status" => code.as_str(),
                    })
                    .map(|counter| {
                        counter.inc();
                    })
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_COUNTER metrics"; "err" => err.to_string());
                    });
                client
                    .ext
                    .remove::<Timer>()
                    .map(|timer| {
                        timer.observe_duration();
                    })
                    .unwrap_or_else(|| {
                        error!("impossible to get timers from typemap");
                    });
                Ok(())
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
            api.mount(self.features());
            api.mount(self.reverse());
            api.mount(self.metrics());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn metrics(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("metrics", |endpoint| {
                endpoint.handle(move |mut client, _params| {
                    let encoder = prometheus::TextEncoder::new();
                    let metric_familys = prometheus::gather();
                    let mut buffer = vec![];
                    encoder.encode(&metric_familys, &mut buffer).unwrap();
                    client.set_content_type(encoder.format_type().parse::<Mime>().unwrap());
                    client.text(String::from_utf8(buffer).unwrap())
                })
            });
        })
    }

    fn reverse(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("reverse", |endpoint| {
                endpoint.params(|params| {
                    coord_param(params, false);
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let coord = ::mimir::Coord::new(
                        params.find("lon").and_then(|p| p.as_f64()).unwrap(),
                        params.find("lat").and_then(|p| p.as_f64()).unwrap(),
                    );
                    let mut rubber = Rubber::new(&cnx);
                    let model_autocomplete = rubber.get_address(&coord);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }

    fn features(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("features/:id", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("id", json_dsl::string());
                    dataset_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |mut client, params| {
                    let id = params.find("id").unwrap().as_str().unwrap();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let features = query::features(&pt_datasets, all_data, &cnx, &id);
                    if features.is_err() {
                        client.set_status(status::StatusCode::NotFound);
                    }
                    let response = model::v1::AutocompleteResponse::from(features);
                    render(client, response)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    shape_param(params);
                    types_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let geometry = params.find_path(&["shape", "geometry"]).unwrap();
                    let coordinates = geometry
                        .find_path(&["coordinates"])
                        .unwrap()
                        .as_array()
                        .unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        // (Lat, Lon)
                        shape.push((
                            ar.as_array().unwrap()[1].as_f64().unwrap(),
                            ar.as_array().unwrap()[0].as_f64().unwrap(),
                        ));
                    }
                    let types = get_param_array(params, "type");
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        None,
                        &cnx,
                        Some(shape),
                        &types,
                    );
                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    coord_param(params, true);
                    types_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(model::Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });

                    let types = get_param_array(params, "type");

                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        coord,
                        &cnx,
                        None,
                        &types,
                    );

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }
}
<commit_msg>refacto<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustless::server::{header, status};
use rustless::{Api, Nesting};
use valico::json_dsl;
use super::query;
use model::v1::*;
use model;
use params::{coord_param, dataset_param, get_param_array, paginate_param, shape_param, types_param};
use mimir::rubber::Rubber;
use prometheus;
use prometheus::Encoder;
use iron::typemap::Key;
use hyper::mime::Mime;

const DEFAULT_LIMIT: u64 = 10u64;
const DEFAULT_OFFSET: u64 = 0u64;

struct Timer;
impl Key for Timer {
    type Value = prometheus::HistogramTimer;
}

lazy_static! {
    static ref HTTP_COUNTER: prometheus::CounterVec = register_counter_vec!(
            "bragi_http_requests_total",
            "Total number of HTTP requests made.",
            &["handler", "method", "status"]
    ).unwrap();

    static ref HTTP_REQ_HISTOGRAM: prometheus::HistogramVec = register_histogram_vec!(
        "bragi_http_request_duration_seconds",
        "The HTTP request latencies in seconds.",
        &["handler", "method"],
        prometheus::exponential_buckets(0.001, 1.5, 25).unwrap()
    ).unwrap();
}

fn render<T>(
    mut client: rustless::Client,
    obj: T,
) -> Result<rustless::Client, rustless::ErrorResponse>
where
    T: serde::Serialize,
{
    client.set_json_content_type();
    client.set_header(header::AccessControlAllowOrigin::Any);
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint {
                        description: "autocomplete service".to_string(),
                    };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                    }
                };
                let mut resp = rustless::Response::from(
                    status::StatusCode::BadRequest,
                    Box::new(serde_json::to_string(&err).unwrap()),
                );
                resp.set_json_content_type();
                Some(resp)
            });

            api.before(|client, _params| {
                let method = client.endpoint.method.to_string();

                HTTP_REQ_HISTOGRAM
                    .get_metric_with(&labels!{
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                    })
                    .map(|timer| {
                        client.ext.insert::<Timer>(timer.start_timer());
                    })
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_REQ_HISTOGRAM metrics"; "err" => err.to_string());
                    });
                Ok(())
            });

            api.after(|client, _params| {
                let method = client.endpoint.method.to_string();
                let code = client.status().to_string();

                HTTP_COUNTER
                    .get_metric_with(&labels!{
                        "handler" => client.endpoint.path.path.as_str(),
                        "method" => method.as_str(),
                        "status" => code.as_str(),
                    })
                    .map(|counter| counter.inc())
                    .unwrap_or_else(|err| {
                        error!("impossible to get HTTP_COUNTER metrics"; "err" => err.to_string());
                    });
                client
                    .ext
                    .remove::<Timer>()
                    .map(|timer| timer.observe_duration())
                    .unwrap_or_else(|| error!("impossible to get timers from typemap"));
                Ok(())
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
            api.mount(self.features());
            api.mount(self.reverse());
            api.mount(self.metrics());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn metrics(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("metrics", |endpoint| {
                endpoint.handle(move |mut client, _params| {
                    let encoder = prometheus::TextEncoder::new();
                    let metric_familys = prometheus::gather();
                    let mut buffer = vec![];
                    encoder.encode(&metric_familys, &mut buffer).unwrap();
                    client.set_content_type(encoder.format_type().parse::<Mime>().unwrap());
                    client.text(String::from_utf8(buffer).unwrap())
                })
            });
        })
    }

    fn reverse(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("reverse", |endpoint| {
                endpoint.params(|params| {
                    coord_param(params, false);
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let coord = ::mimir::Coord::new(
                        params.find("lon").and_then(|p| p.as_f64()).unwrap(),
                        params.find("lat").and_then(|p| p.as_f64()).unwrap(),
                    );
                    let mut rubber = Rubber::new(&cnx);
                    let model_autocomplete = rubber.get_address(&coord);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }

    fn features(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("features/:id", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("id", json_dsl::string());
                    dataset_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |mut client, params| {
                    let id = params.find("id").unwrap().as_str().unwrap();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let features = query::features(&pt_datasets, all_data, &cnx, &id);
                    if features.is_err() {
                        client.set_status(status::StatusCode::NotFound);
                    }
                    let response = model::v1::AutocompleteResponse::from(features);
                    render(client, response)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    shape_param(params);
                    types_param(params);
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let geometry = params.find_path(&["shape", "geometry"]).unwrap();
                    let coordinates = geometry
                        .find_path(&["coordinates"])
                        .unwrap()
                        .as_array()
                        .unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        // (Lat, Lon)
                        shape.push((
                            ar.as_array().unwrap()[1].as_f64().unwrap(),
                            ar.as_array().unwrap()[0].as_f64().unwrap(),
                        ));
                    }
                    let types = get_param_array(params, "type");
                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        None,
                        &cnx,
                        Some(shape),
                        &types,
                    );
                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    dataset_param(params);
                    paginate_param(params);
                    coord_param(params, true);
                    types_param(params);
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params
                        .find("q")
                        .and_then(|val| val.as_str())
                        .unwrap_or("")
                        .to_string();
                    let pt_datasets = get_param_array(params, "pt_dataset");
                    let all_data = params
                        .find("_all_data")
                        .map_or(false, |val| val.as_bool().unwrap());
                    let offset = params
                        .find("offset")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_OFFSET);
                    let limit = params
                        .find("limit")
                        .and_then(|val| val.as_u64())
                        .unwrap_or(DEFAULT_LIMIT);
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(model::Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });

                    let types = get_param_array(params, "type");

                    let model_autocomplete = query::autocomplete(
                        &q,
                        &pt_datasets,
                        all_data,
                        offset,
                        limit,
                        coord,
                        &cnx,
                        None,
                        &types,
                    );

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }
}
<|endoftext|>


<commit_before>extern crate winreg;

use winreg::RegKey;
use winreg::enums::*;
use std::path::Path;

/*
KNOWN CLSIDs with Logitech Gaming Software on windows 7 x64:
x64 HKEY_CLASSES_ROOT\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x64 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
*/

fn main() {
    assert!(cfg!(all(windows, target_pointer_width = "64")), "Unsupported target platform");

    // Try and find path to LogitechLcd.dll searching for its CLSID
    // in the windows registry.
    let hkcl = RegKey::predef(HKEY_CLASSES_ROOT);
    let key = hkcl.open_subkey_with_flags(
        "CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary", KEY_READ)
        .expect("Unable to find registry key \
                 'KEY_CLASSES_ROOT\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary', \
                 please make sure 'Logitech Gaming Software' is installed or \
                 manually specify SDK path by overriding this build script \
                 in any acceptable Cargo configuration location.");

    let dll_path_str = key.get_value::<String, &str>("").expect(
        "The 'KEY_CLASSES_ROOT\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary' \
        registry key is missing the '(default)' subkey");

    let dll_path = Path::new(&dll_path_str[..]);
    let dir_str = dll_path.parent().unwrap().to_str().unwrap();

    println!("cargo:rustc-link-search=native={}", dir_str);
    println!("cargo:rustc-link-lib=LogitechLcd");
}<commit_msg>Read library path from environment variable<commit_after>extern crate winreg;

use winreg::RegKey;
use winreg::enums::*;

use std::path::Path;
use std::env;

/*
KNOWN CLSIDs with Logitech Gaming Software on windows 7 x64:
x64 HKEY_CLASSES_ROOT\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x64 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
x86 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\ServerBinary
*/

// Try and find path to LogitechLcd.dll searching for its CLSID
// in the windows registry.
fn dll_path_clsid() -> String{
    assert!(cfg!(target_pointer_width = "64"), "Unsupported target platform");

    let hkcl = RegKey::predef(HKEY_CLASSES_ROOT);
    let key = hkcl.open_subkey_with_flags(
        "CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary", KEY_READ)
        .expect("Unable to find registry key \
                 'KEY_CLASSES_ROOT\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary', \
                 please make sure 'Logitech Gaming Software' is installed or \
                 manually specify a library path with the 'LOGITECH_LCD' environment
                 variable or by overriding build script in a manifest file");

    key.get_value::<String, &str>("").expect(
        "The 'KEY_CLASSES_ROOT\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary' \
        registry key is missing the '(default)' subkey")
}

fn main() {
    assert!(cfg!(windows), "Unsupported target platform");

    let dll_path_str = match env::var("LOGITECH_LCD") {
        Ok(val) => val,
        Err(_)  => dll_path_clsid(),
    };

    let dll_path = Path::new(&dll_path_str[..]);

    assert!(dll_path.is_file());

    let dir_str  = dll_path.parent().unwrap().to_str().unwrap();

    println!("cargo:rustc-link-search=native={}", dir_str);
    println!("cargo:rustc-link-lib=LogitechLcd");
}<|endoftext|>