<commit_before>use regex::{Regex};
use std::str::{FromStr};

use super::super::metrics::*;

/// Reader that takes a log line string and returns any metrics found in it.
pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

/// Reads metrics from log lines in the standard formats:
///
/// - Measures: `measure#metric=1.2`
/// - Counts: `count#metric=3`
pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_SAMPLE_REGEX: Regex =
        Regex::new(r"sample#([a-zA-Z0-9._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([a-zA-Z0-9._]+)=(\d+)").unwrap();

    static ref SOURCE_REGEX: Regex =
        Regex::new(r"source=([\w.]+)").unwrap();
}

impl StandardLogLineReader {
    fn parse_source<'a>(line: &'a str) -> Option<&'a str> {
        SOURCE_REGEX.captures(line)
                    .and_then(|c| c.get(1))
                    .map(|m| m.as_str())
    }
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let source = StandardLogLineReader::parse_source(line).map(|s| s.to_owned());
        let dimension = |name: &str| {
            Dimension { name: name.to_owned(), source: source.clone() }
        };

        let mut metrics = vec![];

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = u64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Count(dimension(name), value))
            }
        }

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = f64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Measure(dimension(name), value))
            }
        }

        // Look for samples
        for cap in LOG_SAMPLE_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = f64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Sample(dimension(name), value))
            }
        }

        metrics
    } // fn read
}

/// Reads Heroku's logging metrics.
pub struct HerokuLogLineReader;

lazy_static! {
    static ref DYNO_TYPE_REGEX: Regex =
        Regex::new(r"dyno=(\w+)").unwrap();

    static ref CONNECT_REGEX: Regex =
        Regex::new(r"connect=(\d+)ms").unwrap();

    static ref SERVICE_REGEX: Regex =
        Regex::new(r"service=(\d+)ms").unwrap();

    static ref STATUS_REGEX: Regex =
        Regex::new(r"status=(\d+)").unwrap();

    static ref HEROKU_HTTP_ERROR_CODE_REGEX: Regex =
        Regex::new(r"code=(H\d+)").unwrap();

    static ref HEROKU_RUNTIME_ERROR_CODE_REGEX: Regex =
        Regex::new(r"Error (R\d+)").unwrap();

    static ref LOAD_AVG_1M_REGEX: Regex =
        Regex::new(r"sample#load_avg_1m=([0-9.]+)").unwrap();
}

impl HerokuLogLineReader {
    /// Parses Heroku router status lines for the response service time (how
    /// long it took) and the HTTP response status.
    pub fn parse_status(line: &str) -> Option<Vec<Metric>> {
        let mut metrics: Vec<Metric> = vec![];

        let connect = match CONNECT_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|c| u16::from_str(c).ok()) {
                Some(c) => c,
                None => return None,
            };

        let status = match STATUS_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|s| u16::from_str(s).ok()) {
                Some(s) => s,
                None => return None,
            };

        let service = match SERVICE_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|s| u32::from_str(s).ok()) {
                Some(s) => s,
                None => return None,
            };

        let dyno_type = match DYNO_TYPE_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str()) {
                Some(d) => d,
                None => return None,
            };

        let base = format!("dyno.{}", dyno_type);

        // Counting a 499 as a 500
        let is_500 = status >= 499 && status < 600;

        // Don't record timing for 499 and 5xx errors
        if !is_500 {
            let service_time_name = format!("{}.service_time", base);
            metrics.push(Measure(Dimension::with_name_and_source(service_time_name, dyno_type), service as f64));
        }

        // Track the connect time (how long it took to pick up the request)
        let connect_time_name = format!("{}.connect_time", base);
        metrics.push(Measure(Dimension::with_name_and_source(connect_time_name, dyno_type), connect as f64));

        // Count the status
        let status_name = format!("{}.status.{}", base, status);
        metrics.push(Count(Dimension::with_name_and_source(status_name, dyno_type), 1));

        Some(metrics)
    }

    /// Parses Heroku warning and error codes like "Hxx" and "Rxx" where "xx" is a pair
    /// of numbers. See the [Heroku][] site for more details.
    ///
    /// [Heroku]: https://devcenter.heroku.com/articles/error-codes
    pub fn parse_heroku_code(line: &str) -> Option<Metric> {
        let code: &str;

        if let Some(http_code) = HEROKU_HTTP_ERROR_CODE_REGEX.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str()) {
            code = http_code;

        } else if let Some(runtime_code) = HEROKU_RUNTIME_ERROR_CODE_REGEX.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str()) {
            code = runtime_code;

        } else {
            return None
        }

        let name = format!("heroku.error.{}", code);
        Some(Count(Dimension::with_name(name), 1))
    }

    /// Parses the `sample#load_avg_1m=` metrics from Heroku logs.
    pub fn parse_load(line: &str) -> Option<Metric> {
        let load_avg_1m = match LOAD_AVG_1M_REGEX.captures(line)
                                                 .and_then(|c| c.get(1))
                                                 .map(|m| m.as_str())
                                                 .and_then(|c| f64::from_str(c).ok()) {
            Some(l) => l,
            None => return None,
        };

        let source = match StandardLogLineReader::parse_source(line) {
            Some(s) => s,
            None => return None,
        };

        let dyno_type = match source.split('.').nth(0) {
            Some(s) => s,
            None => return None,
        };

        let dim = Dimension::with_name_and_source(format!("dyno.{}.load_avg_1m", dyno_type), source);
        Some(Measure(dim, load_avg_1m))
    }
}

impl LogLineReader for HerokuLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        if !line.contains("heroku") { return vec![] }

        let mut metrics: Vec<Metric> = vec![];

        if let Some(statuses) = HerokuLogLineReader::parse_status(line)      { metrics.extend(statuses) }
        if let Some(code)     = HerokuLogLineReader::parse_heroku_code(line) { metrics.push(code) }
        if let Some(load)     = HerokuLogLineReader::parse_load(line)        { metrics.push(load) }

        metrics
    }
}

#[cfg(test)]
mod tests {
    use super::{
        LogLineReader,
        StandardLogLineReader,
        HerokuLogLineReader
    };
    use super::super::super::metrics::*;

    #[test]
    fn standard_reader_reads_measure() {
        let reader = StandardLogLineReader;
        let line = "measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name("foo"), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_measure_with_source() {
        let reader = StandardLogLineReader;
        let line = "source=web measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name_and_source("foo", "web"), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_count() {
        let reader = StandardLogLineReader;
        let line = "count#foo=1\n";

        assert_eq!(
            reader.read(line),
            vec![ Count(Dimension::with_name("foo"), 1) ]
        )
    }

    #[test]
    fn standard_reader_reads_sample() {
        let reader = StandardLogLineReader;
        let line = "sample#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![ Sample(Dimension::with_name("bar"), 3.4) ]
        )
    }

    #[test]
    fn standard_reader_returns_nothing_on_failed_read() {
        let reader = StandardLogLineReader;
        let line = "metric#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![]
        )
    }

    #[test]
    fn standard_reader_parses_source() {
        let line = "source=something metric#other=5.6\n";

        assert_eq!(
            StandardLogLineReader::parse_source(line),
            Some("something")
        )
    }

    #[test]
    fn heroku_reader_reads_loads() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.429615+00:00 heroku web.2 - - source=web.2 dyno=heroku.123.XYZ sample#load_avg_1m=0.56 sample#load_avg_5m=0.26 sample#load_avg_15m=0.17\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name_and_source("dyno.web.load_avg_1m", "web.2"), 0.56) ]
        )
    }

    #[test]
    fn heroku_reader_reads_http_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:50:36.352129+00:00 heroku router - - sock=backend at=error code=H18 desc=\"Server Request Interrupted\" method=GET path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.5 connect=0ms service=495ms status=503 bytes=1648\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure(Dimension::with_name_and_source("dyno.web.connect_time", "web"), 0.0),
                Count(Dimension::with_name_and_source("dyno.web.status.503", "web"), 1),
                Count(Dimension::with_name("heroku.error.H18"), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_runtime_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-25 21:35:34.990292+00:00 heroku scheduler.5451 - - Error R14 (Memory quota exceeded)\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count(Dimension::with_name("heroku.error.R14"), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_service_times() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.370813+00:00 heroku router - - at=info method=PUT path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.1 connect=1ms service=39ms status=200 bytes=1627\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure(Dimension::with_name_and_source("dyno.web.service_time", "web"), 39.0),
                Measure(Dimension::with_name_and_source("dyno.web.connect_time", "web"), 1.0),
                Count(Dimension::with_name_and_source("dyno.web.status.200", "web"), 1),
            ]
        )
    }
}
<commit_msg>Use Perl-style and ASCII-style regex character classes<commit_after>use regex::{Regex};
use std::str::{FromStr};

use super::super::metrics::*;

/// Reader that takes a log line string and returns any metrics found in it.
pub trait LogLineReader: Send + Sync {
    fn read(&self, &str) -> Vec<Metric>;
}

/// Reads metrics from log lines in the standard formats:
///
/// - Measures: `measure#metric=1.2`
/// - Counts: `count#metric=3`
pub struct StandardLogLineReader;

lazy_static! {
    static ref LOG_MEASURE_REGEX: Regex =
        Regex::new(r"measure#([[:alnum:]._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_SAMPLE_REGEX: Regex =
        Regex::new(r"sample#([[:alnum:]._]+)=(\d+(?:\.\d+)?)").unwrap();

    static ref LOG_COUNT_REGEX: Regex =
        Regex::new(r"count#([[:alnum:]._]+)=(\d+)").unwrap();

    static ref SOURCE_REGEX: Regex =
        Regex::new(r"source=([[:alnum:]._]+)").unwrap();
}

impl StandardLogLineReader {
    fn parse_source<'a>(line: &'a str) -> Option<&'a str> {
        SOURCE_REGEX.captures(line)
                    .and_then(|c| c.get(1))
                    .map(|m| m.as_str())
    }
}

impl LogLineReader for StandardLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        let source = StandardLogLineReader::parse_source(line).map(|s| s.to_owned());
        let dimension = |name: &str| {
            Dimension { name: name.to_owned(), source: source.clone() }
        };

        let mut metrics = vec![];

        // Look for counts
        for cap in LOG_COUNT_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = u64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Count(dimension(name), value))
            }
        }

        // Look for measures
        for cap in LOG_MEASURE_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = f64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Measure(dimension(name), value))
            }
        }

        // Look for samples
        for cap in LOG_SAMPLE_REGEX.captures_iter(line) {
            let name = cap.get(1).unwrap().as_str();

            if let Ok(value) = f64::from_str(cap.get(2).unwrap().as_str()) {
                metrics.push(Sample(dimension(name), value))
            }
        }

        metrics
    } // fn read
}

/// Reads Heroku's logging metrics.
pub struct HerokuLogLineReader;

lazy_static! {
    static ref DYNO_TYPE_REGEX: Regex =
        Regex::new(r"dyno=([[:alpha:]]+)").unwrap();

    static ref CONNECT_REGEX: Regex =
        Regex::new(r"connect=(\d+)ms").unwrap();

    static ref SERVICE_REGEX: Regex =
        Regex::new(r"service=(\d+)ms").unwrap();

    static ref STATUS_REGEX: Regex =
        Regex::new(r"status=(\d+)").unwrap();

    static ref HEROKU_HTTP_ERROR_CODE_REGEX: Regex =
        Regex::new(r"code=(H\d+)").unwrap();

    static ref HEROKU_RUNTIME_ERROR_CODE_REGEX: Regex =
        Regex::new(r"Error (R\d+)").unwrap();

    static ref LOAD_AVG_1M_REGEX: Regex =
        Regex::new(r"sample#load_avg_1m=([0-9.]+)").unwrap();
}

impl HerokuLogLineReader {
    /// Parses Heroku router status lines for the response service time (how
    /// long it took) and the HTTP response status.
    pub fn parse_status(line: &str) -> Option<Vec<Metric>> {
        let mut metrics: Vec<Metric> = vec![];

        let connect = match CONNECT_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|c| u16::from_str(c).ok()) {
                Some(c) => c,
                None => return None,
            };

        let status = match STATUS_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|s| u16::from_str(s).ok()) {
                Some(s) => s,
                None => return None,
            };

        let service = match SERVICE_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str())
            .and_then(|s| u32::from_str(s).ok()) {
                Some(s) => s,
                None => return None,
            };

        let dyno_type = match DYNO_TYPE_REGEX.captures(line)
            .and_then(|c| c.get(1))
            .map(|m| m.as_str()) {
                Some(d) => d,
                None => return None,
            };

        let base = format!("dyno.{}", dyno_type);

        // Counting a 499 as a 500
        let is_500 = status >= 499 && status < 600;

        // Don't record timing for 499 and 5xx errors
        if !is_500 {
            let service_time_name = format!("{}.service_time", base);
            metrics.push(Measure(Dimension::with_name_and_source(service_time_name, dyno_type), service as f64));
        }

        // Track the connect time (how long it took to pick up the request)
        let connect_time_name = format!("{}.connect_time", base);
        metrics.push(Measure(Dimension::with_name_and_source(connect_time_name, dyno_type), connect as f64));

        // Count the status
        let status_name = format!("{}.status.{}", base, status);
        metrics.push(Count(Dimension::with_name_and_source(status_name, dyno_type), 1));

        Some(metrics)
    }

    /// Parses Heroku warning and error codes like "Hxx" and "Rxx" where "xx" is a pair
    /// of numbers. See the [Heroku][] site for more details.
    ///
    /// [Heroku]: https://devcenter.heroku.com/articles/error-codes
    pub fn parse_heroku_code(line: &str) -> Option<Metric> {
        let code: &str;

        if let Some(http_code) = HEROKU_HTTP_ERROR_CODE_REGEX.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str()) {
            code = http_code;

        } else if let Some(runtime_code) = HEROKU_RUNTIME_ERROR_CODE_REGEX.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str()) {
            code = runtime_code;

        } else {
            return None
        }

        let name = format!("heroku.error.{}", code);
        Some(Count(Dimension::with_name(name), 1))
    }

    /// Parses the `sample#load_avg_1m=` metrics from Heroku logs.
    pub fn parse_load(line: &str) -> Option<Metric> {
        let load_avg_1m = match LOAD_AVG_1M_REGEX.captures(line)
                                                 .and_then(|c| c.get(1))
                                                 .map(|m| m.as_str())
                                                 .and_then(|c| f64::from_str(c).ok()) {
            Some(l) => l,
            None => return None,
        };

        let source = match StandardLogLineReader::parse_source(line) {
            Some(s) => s,
            None => return None,
        };

        let dyno_type = match source.split('.').nth(0) {
            Some(s) => s,
            None => return None,
        };

        let dim = Dimension::with_name_and_source(format!("dyno.{}.load_avg_1m", dyno_type), source);
        Some(Measure(dim, load_avg_1m))
    }
}

impl LogLineReader for HerokuLogLineReader {
    fn read(&self, line: &str) -> Vec<Metric> {
        if !line.contains("heroku") { return vec![] }

        let mut metrics: Vec<Metric> = vec![];

        if let Some(statuses) = HerokuLogLineReader::parse_status(line)      { metrics.extend(statuses) }
        if let Some(code)     = HerokuLogLineReader::parse_heroku_code(line) { metrics.push(code) }
        if let Some(load)     = HerokuLogLineReader::parse_load(line)        { metrics.push(load) }

        metrics
    }
}

#[cfg(test)]
mod tests {
    use super::{
        LogLineReader,
        StandardLogLineReader,
        HerokuLogLineReader
    };
    use super::super::super::metrics::*;

    #[test]
    fn standard_reader_reads_measure() {
        let reader = StandardLogLineReader;
        let line = "measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name("foo"), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_measure_with_source() {
        let reader = StandardLogLineReader;
        let line = "source=web measure#foo=1.2\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name_and_source("foo", "web"), 1.2) ]
        )
    }

    #[test]
    fn standard_reader_reads_count() {
        let reader = StandardLogLineReader;
        let line = "count#foo=1\n";

        assert_eq!(
            reader.read(line),
            vec![ Count(Dimension::with_name("foo"), 1) ]
        )
    }

    #[test]
    fn standard_reader_reads_sample() {
        let reader = StandardLogLineReader;
        let line = "sample#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![ Sample(Dimension::with_name("bar"), 3.4) ]
        )
    }

    #[test]
    fn standard_reader_returns_nothing_on_failed_read() {
        let reader = StandardLogLineReader;
        let line = "metric#bar=3.4\n";

        assert_eq!(
            reader.read(line),
            vec![]
        )
    }

    #[test]
    fn standard_reader_parses_source() {
        let line = "source=something metric#other=5.6\n";

        assert_eq!(
            StandardLogLineReader::parse_source(line),
            Some("something")
        )
    }

    #[test]
    fn heroku_reader_reads_loads() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.429615+00:00 heroku web.2 - - source=web.2 dyno=heroku.123.XYZ sample#load_avg_1m=0.56 sample#load_avg_5m=0.26 sample#load_avg_15m=0.17\n";

        assert_eq!(
            reader.read(line),
            vec![ Measure(Dimension::with_name_and_source("dyno.web.load_avg_1m", "web.2"), 0.56) ]
        )
    }

    #[test]
    fn heroku_reader_reads_http_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:50:36.352129+00:00 heroku router - - sock=backend at=error code=H18 desc=\"Server Request Interrupted\" method=GET path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.5 connect=0ms service=495ms status=503 bytes=1648\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure(Dimension::with_name_and_source("dyno.web.connect_time", "web"), 0.0),
                Count(Dimension::with_name_and_source("dyno.web.status.503", "web"), 1),
                Count(Dimension::with_name("heroku.error.H18"), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_runtime_errors() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-25 21:35:34.990292+00:00 heroku scheduler.5451 - - Error R14 (Memory quota exceeded)\n";

        assert_eq!(
            reader.read(line),
            vec![
                Count(Dimension::with_name("heroku.error.R14"), 1),
            ]
        )
    }

    #[test]
    fn heroku_reader_reads_service_times() {
        let reader = HerokuLogLineReader;
        let line = "2016-02-26 21:34:59.370813+00:00 heroku router - - at=info method=PUT path=\"/\" host=www.example.com request_id=XYZ fwd=\"1.2.3.4\" dyno=web.1 connect=1ms service=39ms status=200 bytes=1627\n";

        assert_eq!(
            reader.read(line),
            vec![
                Measure(Dimension::with_name_and_source("dyno.web.service_time", "web"), 39.0),
                Measure(Dimension::with_name_and_source("dyno.web.connect_time", "web"), 1.0),
                Count(Dimension::with_name_and_source("dyno.web.status.200", "web"), 1),
            ]
        )
    }
}
<|endoftext|>


<commit_before>#![no_main]
extern crate libfuzzer_sys;
extern crate capnp;

use capnp::{serialize, message};
use test_capnp::test_all_types;

pub mod test_capnp;

fn traverse(v: test_all_types::Reader) -> ::capnp::Result<()> {
    v.get_int64_field();
    try!(v.get_text_field());
    try!(v.get_data_field());
    try!(v.get_struct_field());

    let structs = try!(v.get_struct_list());
    for s in structs.iter() {
        try!(s.get_text_field());
    }

    try!(v.get_bool_list());
    Ok(())
}

fn try_go(mut data: &[u8]) -> ::capnp::Result<()> {
    let orig_data = data;
    let message_reader = try!(serialize::read_message(
        &mut data,
        *message::ReaderOptions::new().traversal_limit_in_words(4 * 1024)));
    assert!(orig_data.len() > data.len());

    let root: test_all_types::Reader = try!(message_reader.get_root());
    try!(root.total_size());
    try!(traverse(root));

    let mut message = message::Builder::new_default();
    try!(message.set_root(root));
    {
        let root_builder = try!(message.get_root::<test_all_types::Builder>());
        try!(root_builder.total_size());
        try!(traverse(root_builder.as_reader()));
    }

    // init_root() will zero the previous value
    let mut new_root = message.init_root::<test_all_types::Builder>();

    try!(new_root.set_struct_field(root));
    Ok(())
}

#[export_name="rust_fuzzer_test_input"]
pub extern fn go(data: &[u8]) {
    let _ = try_go(data);
}
<commit_msg>add a bit more traversal in the test_all_types fuzzer<commit_after>#![no_main]
extern crate libfuzzer_sys;
extern crate capnp;

use capnp::{serialize, message};
use test_capnp::test_all_types;

pub mod test_capnp;

fn traverse(v: test_all_types::Reader) -> ::capnp::Result<()> {
    v.get_int64_field();
    v.get_float32_field();
    v.get_float64_field();
    v.get_text_field()?;
    v.get_data_field()?;
    v.get_struct_field()?;
    v.get_enum_field()?;

    let structs = v.get_struct_list()?;
    for s in structs.iter() {
        s.get_text_field()?;
        s.get_data_field()?;
    }

    let bools = v.get_bool_list()?;
    for idx in 0 .. bools.len() {
        bools.get(idx);
    }

    let enums = v.get_enum_list()?;
    for idx in 0 .. enums.len() {
        enums.get(idx)?;
    }

    let int8s = v.get_int8_list()?;
    for idx in 0 .. int8s.len() {
        int8s.get(idx);
    }

    Ok(())
}

fn try_go(mut data: &[u8]) -> ::capnp::Result<()> {
    let orig_data = data;
    let message_reader = serialize::read_message(
        &mut data,
        *message::ReaderOptions::new().traversal_limit_in_words(4 * 1024))?;
    assert!(orig_data.len() > data.len());

    let root: test_all_types::Reader = try!(message_reader.get_root());
    root.total_size()?;
    traverse(root)?;

    let mut message = message::Builder::new_default();
    message.set_root(root)?;
    {
        let root_builder = message.get_root::<test_all_types::Builder>()?;
        root_builder.total_size()?;
        traverse(root_builder.as_reader())?;
    }

    // init_root() will zero the previous value
    let mut new_root = message.init_root::<test_all_types::Builder>();

    new_root.set_struct_field(root)?;
    Ok(())
}

#[export_name="rust_fuzzer_test_input"]
pub extern fn go(data: &[u8]) {
    let _ = try_go(data);
}
<|endoftext|>


<commit_before>// #![feature(trace_macros)]
#![recursion_limit = "256"]
// trace_macros!(true);

// FIXME: Documenation

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate asn1_cereal;
#[macro_use]
extern crate nom;

use proc_macro::TokenStream;

use ::tag::parse_tag;
use ::alias::{asn1_alias_info_constructed, ber_alias_serialize, ber_alias_deserialize};
use ::seq_of::{ber_sequence_of_serialize, ber_sequence_of_deserialize};
use ::seq::{ber_sequence_serialize, ber_sequence_deserialize};
use ::choice::{ber_choice_serialize, ber_choice_deserialize};

mod tag;
mod seq;
mod seq_of;
mod alias;
mod choice;

#[proc_macro_derive(Asn1Info, attributes(asn1))]
pub fn asn1_info(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).expect("Couldn't parse input TokenSteam into AST");

  // Tag and asn1 type for this rust type.
  let mut tag = None;
  let mut asn1_type = ast.ident.as_ref().to_owned();
  let mut _logging = false;
  let mut form = None;

  // Parse attributes.
  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::Word(ref _ident) if _ident == "log" => _logging = true,
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "tag" => tag = {
                let tag = parse_tag(value.as_bytes());
                if !tag.is_done() {
                  panic!("Failed to parse tag");
                }
                Some(tag.unwrap())
              },
              "form" => form = Some(value.clone()),
              "asn1_type" => asn1_type = value.clone(),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  // Used in the quasi-quotation below as `#name`
  let name = &ast.ident;

  // Helper is provided for handling complex generic types correctly and effortlessly
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  // FIXME: We need the encoding settings here.
  let mut asn1_constructed = quote!();

  if form == Some("alias".into()) {
    asn1_constructed = asn1_alias_info_constructed(&ast);
  }

  if form == Some("choice".into()) && tag.is_some() {
    panic!("A choice must not have a tag defined");
  }

  let default_tag_num = if let Some(form) = form {
    match form.as_str() {
      "sequence" | "seq of" => Some(16),
      "set" | "set of" => Some(17),
      "choice" | "alias" => None,
      _ => None,
    }
  } else {
    None
  };

  let tag = match (tag, default_tag_num) {
    (Some(t), _) => quote!(#t),
    (_, Some(n)) => quote!(
      Some(::asn1_cereal::tag::Tag {
        class: ::asn1_cereal::tag::Class::Universal,
        tagnum: #n.into(),
        constructed: true,
      })
    ),
    _ => quote!(None),
  };

  let derived = quote! {
    impl #impl_generics ::asn1_cereal::Asn1Info for #name #ty_generics #where_clause {
      fn asn1_tag() -> Option<::asn1_cereal::tag::Tag> {
        #tag
      }

      fn asn1_type() -> ::asn1_cereal::tag::Type {
        #asn1_type.to_owned()
      }

      #asn1_constructed
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

fn logging_enabled(ast: &syn::MacroInput) -> bool {
  // Parse attributes.
  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::Word(ref _ident) if _ident == "log" => return true,
          _ => (),
        };
      }
    }
  }
  false
}

#[proc_macro_derive(BerSerialize, attributes(asn1))]
pub fn ber_serialize(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).unwrap();

  let body = ast.body.clone();
  let mut form = None;

  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "form" => form = Some(value.clone()),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  let derived = if let Some(form) = form {
    match form.as_str() {
      "sequence of" | "seq of" | "set of" => ber_sequence_of_serialize(&ast),
      "alias" => ber_alias_serialize(&ast),
      "choice" => ber_choice_serialize(&ast),
      "seq" | "sequence" => ber_sequence_serialize(&ast),
      _ => panic!("Unknown serialize form {}", form),
    }
  } else {
    match body {
      syn::Body::Enum(_) => {
        ber_choice_serialize(&ast)
      },
      syn::Body::Struct(syn::VariantData::Tuple(fields)) => {
        if fields.len() == 1 {
          ber_alias_serialize(&ast)
        } else {
          ber_sequence_serialize(&ast)
        }
      },
      syn::Body::Struct(syn::VariantData::Struct(_fields)) => {
        ber_sequence_serialize(&ast)
      },
      _ => unimplemented!(),
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

#[proc_macro_derive(BerDeserialize, attributes(asn1))]
pub fn ber_deserialize(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).expect("Couldn't parse input TokenSteam into AST");

  let body = ast.body.clone();
  let mut form = None;

  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "form" => form = Some(value.clone()),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  let derived = if let Some(form) = form {
    match form.as_str() {
      "seq of" | "sequence of" | "set of" => ber_sequence_of_deserialize(&ast),
      "alias" => ber_alias_deserialize(&ast),
      "choice" => ber_choice_deserialize(&ast),
      "seq" | "sequence" => ber_sequence_deserialize(&ast),
      _ => panic!("Unknown deserialize form {}", form),
    }
  } else {
    match body {
      syn::Body::Enum(_) => {
        ber_choice_deserialize(&ast)
      },
      syn::Body::Struct(syn::VariantData::Tuple(fields)) => {
        if fields.len() == 1 {
          ber_alias_deserialize(&ast)
        } else {
          ber_sequence_deserialize(&ast)
        }
      },
      syn::Body::Struct(syn::VariantData::Struct(_fields)) => {
        ber_sequence_deserialize(&ast)
      },
      _ => unimplemented!(),
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

#[test]
fn test() {
}
<commit_msg>Fix issue in tag decoding<commit_after>// #![feature(trace_macros)]
#![recursion_limit = "256"]
// trace_macros!(true);

// FIXME: Documenation

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate asn1_cereal;
#[macro_use]
extern crate nom;

use proc_macro::TokenStream;

use ::tag::parse_tag;
use ::alias::{asn1_alias_info_constructed, ber_alias_serialize, ber_alias_deserialize};
use ::seq_of::{ber_sequence_of_serialize, ber_sequence_of_deserialize};
use ::seq::{ber_sequence_serialize, ber_sequence_deserialize};
use ::choice::{ber_choice_serialize, ber_choice_deserialize};

mod tag;
mod seq;
mod seq_of;
mod alias;
mod choice;

#[proc_macro_derive(Asn1Info, attributes(asn1))]
pub fn asn1_info(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).expect("Couldn't parse input TokenSteam into AST");

  // Tag and asn1 type for this rust type.
  let mut tag = None;
  let mut asn1_type = ast.ident.as_ref().to_owned();
  let mut _logging = false;
  let mut form = None;

  // Parse attributes.
  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::Word(ref _ident) if _ident == "log" => _logging = true,
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "tag" => tag = {
                let tag = parse_tag(value.as_bytes());
                if !tag.is_done() {
                  panic!("Failed to parse tag");
                }
                Some(tag.unwrap().1)
              },
              "form" => form = Some(value.clone()),
              "asn1_type" => asn1_type = value.clone(),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  // Used in the quasi-quotation below as `#name`
  let name = &ast.ident;

  // Helper is provided for handling complex generic types correctly and effortlessly
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  // FIXME: We need the encoding settings here.
  let mut asn1_constructed = quote!();

  if form == Some("alias".into()) {
    asn1_constructed = asn1_alias_info_constructed(&ast);
  }

  if form == Some("choice".into()) && tag.is_some() {
    panic!("A choice must not have a tag defined");
  }

  let default_tag_num = if let Some(form) = form {
    match form.as_str() {
      "sequence" | "seq of" => Some(16),
      "set" | "set of" => Some(17),
      "choice" | "alias" => None,
      _ => None,
    }
  } else {
    None
  };

  let tag = match (tag, default_tag_num) {
    (Some(t), _) => quote!(#t),
    (_, Some(n)) => quote!(
      Some(::asn1_cereal::tag::Tag {
        class: ::asn1_cereal::tag::Class::Universal,
        tagnum: #n.into(),
        constructed: true,
      })
    ),
    _ => quote!(None),
  };

  let derived = quote! {
    impl #impl_generics ::asn1_cereal::Asn1Info for #name #ty_generics #where_clause {
      fn asn1_tag() -> Option<::asn1_cereal::tag::Tag> {
        #tag
      }

      fn asn1_type() -> ::asn1_cereal::tag::Type {
        #asn1_type.to_owned()
      }

      #asn1_constructed
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

fn logging_enabled(ast: &syn::MacroInput) -> bool {
  // Parse attributes.
  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::Word(ref _ident) if _ident == "log" => return true,
          _ => (),
        };
      }
    }
  }
  false
}

#[proc_macro_derive(BerSerialize, attributes(asn1))]
pub fn ber_serialize(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).unwrap();

  let body = ast.body.clone();
  let mut form = None;

  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "form" => form = Some(value.clone()),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  let derived = if let Some(form) = form {
    match form.as_str() {
      "sequence of" | "seq of" | "set of" => ber_sequence_of_serialize(&ast),
      "alias" => ber_alias_serialize(&ast),
      "choice" => ber_choice_serialize(&ast),
      "seq" | "sequence" => ber_sequence_serialize(&ast),
      _ => panic!("Unknown serialize form {}", form),
    }
  } else {
    match body {
      syn::Body::Enum(_) => {
        ber_choice_serialize(&ast)
      },
      syn::Body::Struct(syn::VariantData::Tuple(fields)) => {
        if fields.len() == 1 {
          ber_alias_serialize(&ast)
        } else {
          ber_sequence_serialize(&ast)
        }
      },
      syn::Body::Struct(syn::VariantData::Struct(_fields)) => {
        ber_sequence_serialize(&ast)
      },
      _ => unimplemented!(),
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

#[proc_macro_derive(BerDeserialize, attributes(asn1))]
pub fn ber_deserialize(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).expect("Couldn't parse input TokenSteam into AST");

  let body = ast.body.clone();
  let mut form = None;

  for attr in &ast.attrs.iter().find(|e| e.name() == "asn1") {
    if let syn::MetaItem::List(_, ref items) = attr.value {
      for item in items {
        let item = match *item {
          syn::NestedMetaItem::MetaItem(ref item) => item,
          _ => panic!(),
        };
        match *item {
          syn::MetaItem::NameValue(ref _name, syn::Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "form" => form = Some(value.clone()),
              _ => (),
            };
          },
          _ => (),
        };
      }
    }
  }

  let derived = if let Some(form) = form {
    match form.as_str() {
      "seq of" | "sequence of" | "set of" => ber_sequence_of_deserialize(&ast),
      "alias" => ber_alias_deserialize(&ast),
      "choice" => ber_choice_deserialize(&ast),
      "seq" | "sequence" => ber_sequence_deserialize(&ast),
      _ => panic!("Unknown deserialize form {}", form),
    }
  } else {
    match body {
      syn::Body::Enum(_) => {
        ber_choice_deserialize(&ast)
      },
      syn::Body::Struct(syn::VariantData::Tuple(fields)) => {
        if fields.len() == 1 {
          ber_alias_deserialize(&ast)
        } else {
          ber_sequence_deserialize(&ast)
        }
      },
      syn::Body::Struct(syn::VariantData::Struct(_fields)) => {
        ber_sequence_deserialize(&ast)
      },
      _ => unimplemented!(),
    }
  };

  derived.to_string().parse().expect("Failure parsing derived impl")
}

#[test]
fn test() {
}
<|endoftext|>


<commit_before>#![macro_use]

// For testing purposes, we want to generate valid Asts without a full-fledged parser.
// `ast!` is clunky and makes it *super* easy to leave off `QuoteMore`, `QuoteLess`, and `ExtendEnv`
// We'll assume that the form itself is known and that we can drop all the literals on the ground.
// The result is a terse way to make valid ASTs.

use ast::Ast::{self, *};
use grammar::FormPat;
use name::*;
use std::iter::{Iterator, Peekable};
use util::mbe::EnvMBE;

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("REP")),
            $( u!( $($acc_rest)* ), )*
            u!( $($acc_cur)* )
        ])
    };
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        ::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form("Expr", stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form(stringify!($nt), stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ::beta::ExportBeta::Nothing)
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form(stringify!($nt), stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ({ $( $anything:tt )* }) => {
        panic!("Needed a : in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        ::ast::Atom(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    ( $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        Ty(u!( $($ts)* ))
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            let flimsy = match flimsy_seq.peek() {
                None => return EnvMBE::new(), // Or is this an error?
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    let _ = flimsy_seq.next();
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use grammar::FormPat::*;

    match grammar {
        Literal(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP")
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape"),
        },
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            ::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        _ => unimplemented!(),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Literal(_) => Trivial,
        AnyToken => unimplemented!(),
        AnyAtomicToken => match flimsy {
            VariableReference(a) => Atom(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        VarRef => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        // Lookup is faked by the flimsy macros, so we don't need to do anything:
        ComputeSyntax(_, body) | SynImport(body, _, _) => parse_flimsy_ast(flimsy, &*body),
        Call(_) => flimsy.clone(),
        _ => unimplemented!(),
    }
}
<commit_msg>Travis is using an old Rust version for some reason: make compatible with Rust 1.35.0.<commit_after>#![macro_use]

// For testing purposes, we want to generate valid Asts without a full-fledged parser.
// `ast!` is clunky and makes it *super* easy to leave off `QuoteMore`, `QuoteLess`, and `ExtendEnv`
// We'll assume that the form itself is known and that we can drop all the literals on the ground.
// The result is a terse way to make valid ASTs.

use ast::Ast::{self, *};
use grammar::FormPat;
use name::*;
use std::iter::{Iterator, Peekable};
use util::mbe::EnvMBE;

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("REP")),
            $( u!( $($acc_rest)* ), )*
            u!( $($acc_cur)* )
        ])
    };
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        ::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form("Expr", stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form(stringify!($nt), stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ::beta::ExportBeta::Nothing)
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = ::core_forms::find_core_form(stringify!($nt), stringify!($form));
            ::ast::Node(f.clone(),
                ::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ({ $( $anything:tt )* }) => {
        panic!("Needed a : in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        ::ast::Atom(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    ( $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        Ty(u!( $($ts)* ))
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            let flimsy = *match flimsy_seq.peek() {
                None => return EnvMBE::new(), // Or is this an error?
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    let _ = flimsy_seq.next();
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use grammar::FormPat::*;

    match grammar {
        Literal(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP")
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape"),
        },
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            ::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        _ => unimplemented!(),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Literal(_) => Trivial,
        AnyToken => unimplemented!(),
        AnyAtomicToken => match flimsy {
            VariableReference(a) => Atom(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        VarRef => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        // Lookup is faked by the flimsy macros, so we don't need to do anything:
        ComputeSyntax(_, body) | SynImport(body, _, _) => parse_flimsy_ast(flimsy, &*body),
        Call(_) => flimsy.clone(),
        _ => unimplemented!(),
    }
}
<|endoftext|>


<commit_before><commit_msg>accept the algorithm identifier used by OpenSSH<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use console::{style, Term};
use futures::{Future, FutureExt};
use iml_wire_types::{Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration};
use indicatif::ProgressBar;
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use spinners::{Spinner, Spinners};
use std::{fmt::Display, io, str::FromStr};
use structopt::StructOpt;

pub fn wrap_fut<T>(msg: &str, fut: impl Future<Output = T>) -> impl Future<Output = T> {
    let pb = ProgressBar::new_spinner();
    pb.enable_steady_tick(100);
    pb.set_message(msg);

    fut.inspect(move |_| pb.finish_and_clear())
}

pub fn start_spinner(msg: &str) -> impl FnOnce(Option<String>) -> () {
    let sp = Spinner::new(Spinners::Dots9, style(msg).dim().to_string());

    move |msg_opt| match msg_opt {
        Some(msg) => {
            sp.message(msg);
        }
        None => {
            sp.stop();
            if let Err(e) = Term::stdout().clear_line() {
                tracing::debug!("Could not clear current line {}", e);
            };
        }
    }
}

pub fn format_cmd_state(cmd: &Command) -> String {
    if cmd.errored {
        format_error(format!("{} errored", cmd.message))
    } else if cmd.cancelled {
        format_cancelled(&format!("{} cancelled", cmd.message))
    } else {
        format_success(format!("{} successful", cmd.message))
    }
}

pub fn display_cmd_state(cmd: &Command) {
    println!("{}", format_cmd_state(&cmd));
}

pub fn format_cancelled(message: impl Display) -> String {
    format!("🚫 {}", message)
}

pub fn display_cancelled(message: impl Display) {
    println!("{}", format_cancelled(&message));
}

pub fn format_success(message: impl Display) -> String {
    format!("{} {}", style("✔").green(), message)
}

pub fn display_success(message: impl Display) {
    println!("{}", format_success(message))
}

pub fn format_error(message: impl Display) -> String {
    format!("{} {}", style("✗").red(), message)
}

pub fn display_error(message: impl Display) {
    println!("{}", format_error(message))
}

pub fn generate_table<Rows, R>(columns: &[&str], rows: Rows) -> Table
where
    R: IntoIterator,
    R::Item: ToString,
    Rows: IntoIterator<Item = R>,
{
    let mut table = Table::new();

    table.set_titles(Row::from(columns));

    for r in rows {
        table.add_row(Row::from(r));
    }

    table
}

pub fn usage(
    free: Option<u64>,
    total: Option<u64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total as f64 - free as f64, Some(0)),
            formatter(total as f64, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total as f64, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

pub trait IntoTable {
    fn into_table(self) -> Table;
}

impl IntoTable for Vec<Host> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "FQDN", "State", "Nids"],
            self.into_iter().map(|h| {
                vec![
                    h.id.to_string(),
                    h.fqdn,
                    h.state,
                    h.nids.unwrap_or_default().join(" "),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<StratagemConfiguration> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "State", "Interval", "Purge", "Report"],
            self.into_iter().map(|x| {
                vec![
                    x.id.to_string(),
                    x.filesystem,
                    x.state,
                    x.interval.to_string(),
                    x.purge_duration.map(|x| x.to_string()).unwrap_or_default(),
                    x.report_duration.map(|x| x.to_string()).unwrap_or_default(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<OstPool> {
    fn into_table(self) -> Table {
        generate_table(
            &["Filesystem", "Pool Name", "OST Count"],
            self.into_iter()
                .map(|x| vec![x.filesystem, x.name, x.osts.len().to_string()]),
        )
    }
}

impl IntoTable for Vec<Filesystem> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
            ],
            self.into_iter().map(|x| {
                vec![
                    x.label,
                    x.state,
                    usage(
                        x.bytes_free.map(|x| x as u64),
                        x.bytes_total.map(|x| x as u64),
                        format_bytes,
                    ),
                    usage(x.files_free, x.files_total, format_number),
                    format!("{}", x.client_count.unwrap_or(0)),
                    x.mdts.len().to_string(),
                    x.osts.len().to_string(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<ServerProfile> {
    fn into_table(self) -> Table {
        generate_table(
            &["Profile", "Name", "Description"],
            self.into_iter()
                .filter(|x| x.user_selectable)
                .map(|x| vec![x.name, x.ui_name, x.ui_description]),
        )
    }
}

pub trait IntoDisplayType {
    fn into_display_type(self, display_type: DisplayType) -> String;
}

impl<T: IntoTable + serde::Serialize> IntoDisplayType for T {
    fn into_display_type(self, display_type: DisplayType) -> String {
        match display_type {
            DisplayType::Json => {
                serde_json::to_string_pretty(&self).expect("Cannot serialize item to JSON")
            }
            DisplayType::Yaml => {
                serde_yaml::to_string(&self).expect("Cannot serialize item to YAML")
            }
            DisplayType::Tabular => self.into_table().to_string(),
        }
    }
}

#[derive(StructOpt, Debug)]
pub enum DisplayType {
    Json,
    Yaml,
    Tabular,
}

impl Default for DisplayType {
    fn default() -> Self {
        Self::Tabular
    }
}

impl FromStr for DisplayType {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(Self::Json),
            "yaml" => Ok(Self::Yaml),
            "tabular" => Ok(Self::Tabular),
            _ => Err(Self::Err::new(
                io::ErrorKind::InvalidInput,
                "Couldn't parse display type.",
            )),
        }
    }
}
<commit_msg>Do not print empty tables (#1912)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use console::{style, Term};
use futures::{Future, FutureExt};
use iml_wire_types::{Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration};
use indicatif::ProgressBar;
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use spinners::{Spinner, Spinners};
use std::{fmt::Display, io, str::FromStr};
use structopt::StructOpt;

pub fn wrap_fut<T>(msg: &str, fut: impl Future<Output = T>) -> impl Future<Output = T> {
    let pb = ProgressBar::new_spinner();
    pb.enable_steady_tick(100);
    pb.set_message(msg);

    fut.inspect(move |_| pb.finish_and_clear())
}

pub fn start_spinner(msg: &str) -> impl FnOnce(Option<String>) -> () {
    let sp = Spinner::new(Spinners::Dots9, style(msg).dim().to_string());

    move |msg_opt| match msg_opt {
        Some(msg) => {
            sp.message(msg);
        }
        None => {
            sp.stop();
            if let Err(e) = Term::stdout().clear_line() {
                tracing::debug!("Could not clear current line {}", e);
            };
        }
    }
}

pub fn format_cmd_state(cmd: &Command) -> String {
    if cmd.errored {
        format_error(format!("{} errored", cmd.message))
    } else if cmd.cancelled {
        format_cancelled(&format!("{} cancelled", cmd.message))
    } else {
        format_success(format!("{} successful", cmd.message))
    }
}

pub fn display_cmd_state(cmd: &Command) {
    println!("{}", format_cmd_state(&cmd));
}

pub fn format_cancelled(message: impl Display) -> String {
    format!("🚫 {}", message)
}

pub fn display_cancelled(message: impl Display) {
    println!("{}", format_cancelled(&message));
}

pub fn format_success(message: impl Display) -> String {
    format!("{} {}", style("✔").green(), message)
}

pub fn display_success(message: impl Display) {
    println!("{}", format_success(message))
}

pub fn format_error(message: impl Display) -> String {
    format!("{} {}", style("✗").red(), message)
}

pub fn display_error(message: impl Display) {
    println!("{}", format_error(message))
}

pub fn generate_table<Rows, R>(columns: &[&str], rows: Rows) -> Table
where
    R: IntoIterator,
    R::Item: ToString,
    Rows: IntoIterator<Item = R>,
{
    let mut table = Table::new();

    table.set_titles(Row::from(columns));

    for r in rows {
        table.add_row(Row::from(r));
    }

    table
}

pub fn usage(
    free: Option<u64>,
    total: Option<u64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total as f64 - free as f64, Some(0)),
            formatter(total as f64, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total as f64, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

impl<T> IsEmpty for Vec<T> {
    fn is_empty(&self) -> bool {
        self.is_empty()
    }
}

pub trait IntoTable {
    fn into_table(self) -> Table;
}

impl IntoTable for Vec<Host> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "FQDN", "State", "Nids"],
            self.into_iter().map(|h| {
                vec![
                    h.id.to_string(),
                    h.fqdn,
                    h.state,
                    h.nids.unwrap_or_default().join(" "),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<StratagemConfiguration> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "State", "Interval", "Purge", "Report"],
            self.into_iter().map(|x| {
                vec![
                    x.id.to_string(),
                    x.filesystem,
                    x.state,
                    x.interval.to_string(),
                    x.purge_duration.map(|x| x.to_string()).unwrap_or_default(),
                    x.report_duration.map(|x| x.to_string()).unwrap_or_default(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<OstPool> {
    fn into_table(self) -> Table {
        generate_table(
            &["Filesystem", "Pool Name", "OST Count"],
            self.into_iter()
                .map(|x| vec![x.filesystem, x.name, x.osts.len().to_string()]),
        )
    }
}

impl IntoTable for Vec<Filesystem> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
            ],
            self.into_iter().map(|x| {
                vec![
                    x.label,
                    x.state,
                    usage(
                        x.bytes_free.map(|x| x as u64),
                        x.bytes_total.map(|x| x as u64),
                        format_bytes,
                    ),
                    usage(x.files_free, x.files_total, format_number),
                    format!("{}", x.client_count.unwrap_or(0)),
                    x.mdts.len().to_string(),
                    x.osts.len().to_string(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<ServerProfile> {
    fn into_table(self) -> Table {
        generate_table(
            &["Profile", "Name", "Description"],
            self.into_iter()
                .filter(|x| x.user_selectable)
                .map(|x| vec![x.name, x.ui_name, x.ui_description]),
        )
    }
}

pub trait IntoDisplayType {
    fn into_display_type(self, display_type: DisplayType) -> String;
}

impl<T> IntoDisplayType for T
where
    T: IsEmpty + IntoTable + serde::Serialize,
{
    fn into_display_type(self, display_type: DisplayType) -> String {
        match display_type {
            DisplayType::Json => {
                serde_json::to_string_pretty(&self).expect("Cannot serialize item to JSON")
            }
            DisplayType::Yaml => {
                serde_yaml::to_string(&self).expect("Cannot serialize item to YAML")
            }
            DisplayType::Tabular => {
                if self.is_empty() {
                    "".to_string()
                } else {
                    self.into_table().to_string()
                }
            }
        }
    }
}

#[derive(StructOpt, Debug)]
pub enum DisplayType {
    Json,
    Yaml,
    Tabular,
}

impl Default for DisplayType {
    fn default() -> Self {
        Self::Tabular
    }
}

impl FromStr for DisplayType {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(Self::Json),
            "yaml" => Ok(Self::Yaml),
            "tabular" => Ok(Self::Tabular),
            _ => Err(Self::Err::new(
                io::ErrorKind::InvalidInput,
                "Couldn't parse display type.",
            )),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use chrono_humanize::{Accuracy, HumanTime, Tense};
use console::{style, Term};
use futures::{Future, FutureExt};
use iml_wire_types::{
    snapshot::{ReserveUnit, Snapshot, SnapshotInterval, SnapshotRetention},
    Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration,
};
use indicatif::ProgressBar;
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use spinners::{Spinner, Spinners};
use std::{fmt::Display, io, str::FromStr};
use structopt::StructOpt;

pub fn wrap_fut<T>(msg: &str, fut: impl Future<Output = T>) -> impl Future<Output = T> {
    let pb = ProgressBar::new_spinner();
    pb.enable_steady_tick(100);
    pb.set_message(msg);

    fut.inspect(move |_| pb.finish_and_clear())
}

pub fn start_spinner(msg: &str) -> impl FnOnce(Option<String>) {
    let sp = Spinner::new(Spinners::Dots9, style(msg).dim().to_string());

    move |msg_opt| match msg_opt {
        Some(msg) => {
            sp.message(msg);
        }
        None => {
            sp.stop();
            if let Err(e) = Term::stdout().clear_line() {
                tracing::debug!("Could not clear current line {}", e);
            };
        }
    }
}

pub fn format_cmd_state(cmd: &Command) -> String {
    if cmd.errored {
        format_error(format!("{} errored", cmd.message))
    } else if cmd.cancelled {
        format_cancelled(&format!("{} cancelled", cmd.message))
    } else {
        format_success(format!("{} successful", cmd.message))
    }
}

pub fn display_cmd_state(cmd: &Command) {
    println!("{}", format_cmd_state(&cmd));
}

pub fn format_cancelled(message: impl Display) -> String {
    format!("🚫 {}", message)
}

pub fn display_cancelled(message: impl Display) {
    println!("{}", format_cancelled(&message));
}

pub fn format_success(message: impl Display) -> String {
    format!("{} {}", style("✔").green(), message)
}

pub fn display_success(message: impl Display) {
    println!("{}", format_success(message))
}

pub fn format_error(message: impl Display) -> String {
    format!("{} {}", style("✗").red(), message)
}

pub fn display_error(message: impl Display) {
    println!("{}", format_error(message))
}

pub fn generate_table<Rows, R>(columns: &[&str], rows: Rows) -> Table
where
    R: IntoIterator,
    R::Item: ToString,
    Rows: IntoIterator<Item = R>,
{
    let mut table = Table::new();

    table.set_titles(Row::from(columns));

    for r in rows {
        table.add_row(Row::from(r));
    }

    table
}

pub fn usage(
    free: Option<u64>,
    total: Option<u64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total as f64 - free as f64, Some(0)),
            formatter(total as f64, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total as f64, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

impl<T> IsEmpty for Vec<T> {
    fn is_empty(&self) -> bool {
        self.is_empty()
    }
}

pub trait IntoTable {
    fn into_table(self) -> Table;
}

impl IntoTable for Vec<Snapshot> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Filesystem",
                "Snapshot",
                "Creation Time",
                "State",
                "Comment",
            ],
            self.into_iter().map(|s| {
                vec![
                    s.filesystem_name,
                    s.snapshot_name,
                    s.create_time.to_rfc2822(),
                    match s.mounted {
                        Some(true) => "mounted",
                        Some(false) => "unmounted",
                        None => "--",
                    }
                    .to_string(),
                    s.comment.unwrap_or_else(|| "--".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<SnapshotInterval> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "Interval", "Use Barrier", "Last Run"],
            self.into_iter().map(|i| {
                vec![
                    i.id.to_string(),
                    i.filesystem_name,
                    chrono::Duration::from_std(i.interval.0)
                        .map(HumanTime::from)
                        .map(|x| x.to_text_en(Accuracy::Precise, Tense::Present))
                        .unwrap_or_else(|_| "--".to_string()),
                    i.use_barrier.to_string(),
                    i.last_run
                        .map(|t| t.to_rfc2822())
                        .unwrap_or_else(|| "--".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<SnapshotRetention> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "Reserve", "Keep", "Last Run"],
            self.into_iter().map(|r| {
                vec![
                    r.id.to_string(),
                    r.filesystem_name,
                    format!(
                        "{} {}",
                        r.reserve_value,
                        match r.reserve_unit {
                            ReserveUnit::Percent => "%",
                            ReserveUnit::Gibibytes => "GiB",
                            ReserveUnit::Tebibytes => "TiB",
                        }
                    ),
                    r.keep_num.to_string(),
                    r.last_run
                        .map(|t| t.to_rfc2822())
                        .unwrap_or_else(|| "--".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<Host> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "FQDN", "State", "Nids"],
            self.into_iter().map(|h| {
                vec![
                    h.id.to_string(),
                    h.fqdn,
                    h.state,
                    h.nids.unwrap_or_default().join(" "),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<StratagemConfiguration> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "State", "Interval", "Purge", "Report"],
            self.into_iter().map(|x| {
                vec![
                    x.id.to_string(),
                    x.filesystem,
                    x.state,
                    x.interval.to_string(),
                    x.purge_duration.map(|x| x.to_string()).unwrap_or_default(),
                    x.report_duration.map(|x| x.to_string()).unwrap_or_default(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<OstPool> {
    fn into_table(self) -> Table {
        generate_table(
            &["Filesystem", "Pool Name", "OST Count"],
            self.into_iter()
                .map(|x| vec![x.filesystem, x.name, x.osts.len().to_string()]),
        )
    }
}

impl IntoTable for Vec<Filesystem> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
            ],
            self.into_iter().map(|x| {
                vec![
                    x.label,
                    x.state,
                    usage(
                        x.bytes_free.map(|x| x as u64),
                        x.bytes_total.map(|x| x as u64),
                        format_bytes,
                    ),
                    usage(x.files_free, x.files_total, format_number),
                    format!("{}", x.client_count.unwrap_or(0)),
                    x.mdts.len().to_string(),
                    x.osts.len().to_string(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<ServerProfile> {
    fn into_table(self) -> Table {
        generate_table(
            &["Profile", "Name", "Description"],
            self.into_iter()
                .filter(|x| x.user_selectable)
                .map(|x| vec![x.name, x.ui_name, x.ui_description]),
        )
    }
}

pub trait IntoDisplayType {
    fn into_display_type(self, display_type: DisplayType) -> String;
}

impl<T> IntoDisplayType for T
where
    T: IsEmpty + IntoTable + serde::Serialize,
{
    fn into_display_type(self, display_type: DisplayType) -> String {
        match display_type {
            DisplayType::Json => {
                serde_json::to_string_pretty(&self).expect("Cannot serialize item to JSON")
            }
            DisplayType::Yaml => {
                serde_yaml::to_string(&self).expect("Cannot serialize item to YAML")
            }
            DisplayType::Tabular => {
                if self.is_empty() {
                    "".to_string()
                } else {
                    self.into_table().to_string()
                }
            }
        }
    }
}

#[derive(StructOpt, Debug)]
pub enum DisplayType {
    Json,
    Yaml,
    Tabular,
}

impl Default for DisplayType {
    fn default() -> Self {
        Self::Tabular
    }
}

impl FromStr for DisplayType {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(Self::Json),
            "yaml" => Ok(Self::Yaml),
            "tabular" => Ok(Self::Tabular),
            _ => Err(Self::Err::new(
                io::ErrorKind::InvalidInput,
                "Couldn't parse display type.",
            )),
        }
    }
}
<commit_msg>Use three dashes instead of two (#2280)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use chrono_humanize::{Accuracy, HumanTime, Tense};
use console::{style, Term};
use futures::{Future, FutureExt};
use iml_wire_types::{
    snapshot::{ReserveUnit, Snapshot, SnapshotInterval, SnapshotRetention},
    Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration,
};
use indicatif::ProgressBar;
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use spinners::{Spinner, Spinners};
use std::{fmt::Display, io, str::FromStr};
use structopt::StructOpt;

pub fn wrap_fut<T>(msg: &str, fut: impl Future<Output = T>) -> impl Future<Output = T> {
    let pb = ProgressBar::new_spinner();
    pb.enable_steady_tick(100);
    pb.set_message(msg);

    fut.inspect(move |_| pb.finish_and_clear())
}

pub fn start_spinner(msg: &str) -> impl FnOnce(Option<String>) {
    let sp = Spinner::new(Spinners::Dots9, style(msg).dim().to_string());

    move |msg_opt| match msg_opt {
        Some(msg) => {
            sp.message(msg);
        }
        None => {
            sp.stop();
            if let Err(e) = Term::stdout().clear_line() {
                tracing::debug!("Could not clear current line {}", e);
            };
        }
    }
}

pub fn format_cmd_state(cmd: &Command) -> String {
    if cmd.errored {
        format_error(format!("{} errored", cmd.message))
    } else if cmd.cancelled {
        format_cancelled(&format!("{} cancelled", cmd.message))
    } else {
        format_success(format!("{} successful", cmd.message))
    }
}

pub fn display_cmd_state(cmd: &Command) {
    println!("{}", format_cmd_state(&cmd));
}

pub fn format_cancelled(message: impl Display) -> String {
    format!("🚫 {}", message)
}

pub fn display_cancelled(message: impl Display) {
    println!("{}", format_cancelled(&message));
}

pub fn format_success(message: impl Display) -> String {
    format!("{} {}", style("✔").green(), message)
}

pub fn display_success(message: impl Display) {
    println!("{}", format_success(message))
}

pub fn format_error(message: impl Display) -> String {
    format!("{} {}", style("✗").red(), message)
}

pub fn display_error(message: impl Display) {
    println!("{}", format_error(message))
}

pub fn generate_table<Rows, R>(columns: &[&str], rows: Rows) -> Table
where
    R: IntoIterator,
    R::Item: ToString,
    Rows: IntoIterator<Item = R>,
{
    let mut table = Table::new();

    table.set_titles(Row::from(columns));

    for r in rows {
        table.add_row(Row::from(r));
    }

    table
}

pub fn usage(
    free: Option<u64>,
    total: Option<u64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total as f64 - free as f64, Some(0)),
            formatter(total as f64, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total as f64, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

impl<T> IsEmpty for Vec<T> {
    fn is_empty(&self) -> bool {
        self.is_empty()
    }
}

pub trait IntoTable {
    fn into_table(self) -> Table;
}

impl IntoTable for Vec<Snapshot> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Filesystem",
                "Snapshot",
                "Creation Time",
                "State",
                "Comment",
            ],
            self.into_iter().map(|s| {
                vec![
                    s.filesystem_name,
                    s.snapshot_name,
                    s.create_time.to_rfc2822(),
                    match s.mounted {
                        Some(true) => "mounted",
                        Some(false) => "unmounted",
                        None => "---",
                    }
                    .to_string(),
                    s.comment.unwrap_or_else(|| "---".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<SnapshotInterval> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "Interval", "Use Barrier", "Last Run"],
            self.into_iter().map(|i| {
                vec![
                    i.id.to_string(),
                    i.filesystem_name,
                    chrono::Duration::from_std(i.interval.0)
                        .map(HumanTime::from)
                        .map(|x| x.to_text_en(Accuracy::Precise, Tense::Present))
                        .unwrap_or_else(|_| "---".to_string()),
                    i.use_barrier.to_string(),
                    i.last_run
                        .map(|t| t.to_rfc2822())
                        .unwrap_or_else(|| "---".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<SnapshotRetention> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "Reserve", "Keep", "Last Run"],
            self.into_iter().map(|r| {
                vec![
                    r.id.to_string(),
                    r.filesystem_name,
                    format!(
                        "{} {}",
                        r.reserve_value,
                        match r.reserve_unit {
                            ReserveUnit::Percent => "%",
                            ReserveUnit::Gibibytes => "GiB",
                            ReserveUnit::Tebibytes => "TiB",
                        }
                    ),
                    r.keep_num.to_string(),
                    r.last_run
                        .map(|t| t.to_rfc2822())
                        .unwrap_or_else(|| "---".to_string()),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<Host> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "FQDN", "State", "Nids"],
            self.into_iter().map(|h| {
                vec![
                    h.id.to_string(),
                    h.fqdn,
                    h.state,
                    h.nids.unwrap_or_default().join(" "),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<StratagemConfiguration> {
    fn into_table(self) -> Table {
        generate_table(
            &["Id", "Filesystem", "State", "Interval", "Purge", "Report"],
            self.into_iter().map(|x| {
                vec![
                    x.id.to_string(),
                    x.filesystem,
                    x.state,
                    x.interval.to_string(),
                    x.purge_duration.map(|x| x.to_string()).unwrap_or_default(),
                    x.report_duration.map(|x| x.to_string()).unwrap_or_default(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<OstPool> {
    fn into_table(self) -> Table {
        generate_table(
            &["Filesystem", "Pool Name", "OST Count"],
            self.into_iter()
                .map(|x| vec![x.filesystem, x.name, x.osts.len().to_string()]),
        )
    }
}

impl IntoTable for Vec<Filesystem> {
    fn into_table(self) -> Table {
        generate_table(
            &[
                "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
            ],
            self.into_iter().map(|x| {
                vec![
                    x.label,
                    x.state,
                    usage(
                        x.bytes_free.map(|x| x as u64),
                        x.bytes_total.map(|x| x as u64),
                        format_bytes,
                    ),
                    usage(x.files_free, x.files_total, format_number),
                    format!("{}", x.client_count.unwrap_or(0)),
                    x.mdts.len().to_string(),
                    x.osts.len().to_string(),
                ]
            }),
        )
    }
}

impl IntoTable for Vec<ServerProfile> {
    fn into_table(self) -> Table {
        generate_table(
            &["Profile", "Name", "Description"],
            self.into_iter()
                .filter(|x| x.user_selectable)
                .map(|x| vec![x.name, x.ui_name, x.ui_description]),
        )
    }
}

pub trait IntoDisplayType {
    fn into_display_type(self, display_type: DisplayType) -> String;
}

impl<T> IntoDisplayType for T
where
    T: IsEmpty + IntoTable + serde::Serialize,
{
    fn into_display_type(self, display_type: DisplayType) -> String {
        match display_type {
            DisplayType::Json => {
                serde_json::to_string_pretty(&self).expect("Cannot serialize item to JSON")
            }
            DisplayType::Yaml => {
                serde_yaml::to_string(&self).expect("Cannot serialize item to YAML")
            }
            DisplayType::Tabular => {
                if self.is_empty() {
                    "".to_string()
                } else {
                    self.into_table().to_string()
                }
            }
        }
    }
}

#[derive(StructOpt, Debug)]
pub enum DisplayType {
    Json,
    Yaml,
    Tabular,
}

impl Default for DisplayType {
    fn default() -> Self {
        Self::Tabular
    }
}

impl FromStr for DisplayType {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(Self::Json),
            "yaml" => Ok(Self::Yaml),
            "tabular" => Ok(Self::Tabular),
            _ => Err(Self::Err::new(
                io::ErrorKind::InvalidInput,
                "Couldn't parse display type.",
            )),
        }
    }
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use std::num::ParseFloatError;

use irc::client::prelude::*;
use irc::error::Error as IrcError;

use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use plugin::*;

#[derive(PluginName, Debug)]
pub struct Currency;

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    pub fn new() -> Currency {
        Currency {}
    }

    fn eval_command<'a>(&self, tokens: &'a [String]) -> Result<ConvertionRequest<'a>, ParseFloatError> {
        Ok(ConvertionRequest {
            value: tokens[0].parse()?,
            source: &tokens[1],
            target: &tokens[2],
        })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {

        if command.tokens.len() < 3 {
            return self.invalid_command(server, &command);
        }

        let request = match self.eval_command(&command.tokens) {
            Ok(request) => request,
            Err(_) => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_notice(&command.source, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
        let help = format!("usage: {} currency value from_currency to_currency\r\n\
                            example: 1.5 eur usd\r\n\
                            available currencies: AUD, BGN, BRL, CAD, \
                            CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
                            IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
                            NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
                            TRY, USD, ZAR",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect Command. \
                           Send \"{} currency help\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        Ok(())
    }

    fn command(&mut self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> {

        if command.tokens.is_empty() {
            return self.invalid_command(server, &command);
        }

        match command.tokens[0].as_ref() {
            "help" => self.help(server, &mut command),
            _ => self.convert(server, command),
        }
    }
}

#[cfg(test)]
mod tests {}
<commit_msg>Panic if execute is called on the Currency Plugin<commit_after>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use std::num::ParseFloatError;

use irc::client::prelude::*;
use irc::error::Error as IrcError;

use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use plugin::*;

#[derive(PluginName, Debug)]
pub struct Currency;

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    pub fn new() -> Currency {
        Currency {}
    }

    fn eval_command<'a>(&self, tokens: &'a [String]) -> Result<ConvertionRequest<'a>, ParseFloatError> {
        Ok(ConvertionRequest {
            value: tokens[0].parse()?,
            source: &tokens[1],
            target: &tokens[2],
        })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {

        if command.tokens.len() < 3 {
            return self.invalid_command(server, &command);
        }

        let request = match self.eval_command(&command.tokens) {
            Ok(request) => request,
            Err(_) => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_notice(&command.source, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
        let help = format!("usage: {} currency value from_currency to_currency\r\n\
                            example: 1.5 eur usd\r\n\
                            available currencies: AUD, BGN, BRL, CAD, \
                            CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
                            IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
                            NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
                            TRY, USD, ZAR",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect Command. \
                           Send \"{} currency help\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        panic!("Currency does not implement the execute function!")
    }

    fn command(&mut self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> {

        if command.tokens.is_empty() {
            return self.invalid_command(server, &command);
        }

        match command.tokens[0].as_ref() {
            "help" => self.help(server, &mut command),
            _ => self.convert(server, command),
        }
    }
}

#[cfg(test)]
mod tests {}
<|endoftext|>