<commit_before>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput};
use markup5ever::QualName;

use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, ValueErrorKind>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str(s: &str) -> Result<Self, ValueErrorKind> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: Parse> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).attribute(self.clone())
    }

    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser)
            .and_then(validate)
            .attribute(self.clone())
    }
}

pub trait ParseValueToParseError<T: ParseToParseError> {
    /// Parses a `value` string into a type `T`.
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: ParseToParseError> ParseValueToParseError<T> for QualName {
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse_to_parse_error(&mut parser).attribute(self.clone())
    }

    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse_to_parse_error(&mut parser).attribute(self.clone())?;

        validate(v).map_err(|e| parser.new_custom_error(e)).attribute(self.clone())
    }
}

impl Parse for f64 {
    /// Avoid infinities, and convert to `f64`.
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
    fn parse(parser: &mut Parser<'_, '_>) -> Result<f64, ValueErrorKind> {
        parser
            .expect_number()
            .map_err(|_| ValueErrorKind::parse_error("parse error"))
            .and_then(|n| Ok(f64::from(finite_f32(n)?)))
    }
}

pub trait ParseToParseError: Sized {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>>;
    fn parse_str_to_parse_error<'i>(s: &'i str) -> Result<Self, CssParseError<'i>> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse_to_parse_error(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

impl ParseToParseError for f64 {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let loc = parser.current_source_location();
        parser
            .expect_number()
            .map_err(|e| e.into())
            .and_then(|n| {
                if n.is_finite() {
                    Ok(f64::from(n))
                } else {
                    Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
                }
            })
    }
}

// number-optional-number
//
// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber

pub fn number_optional_number(s: &str) -> Result<(f64, f64), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = f64::parse(&mut parser)?;

    if !parser.is_exhausted() {
        optional_comma(&mut parser);

        let y = f64::parse(&mut parser)?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

// integer
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
pub fn integer(s: &str) -> Result<i32, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(parser.expect_integer()?)
}

// integer-optional-integer
//
// Like number-optional-number but with integers.
pub fn integer_optional_integer(s: &str) -> Result<(i32, i32), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = parser.expect_integer()?;

    if !parser.is_exhausted() {
        optional_comma(&mut parser);

        let y = parser.expect_integer()?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(number_optional_number("1, 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1"), Ok((1.0, 1.0)));

        assert_eq!(number_optional_number("-1, -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1 -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1"), Ok((-1.0, -1.0)));
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(number_optional_number("").is_err());
        assert!(number_optional_number("1x").is_err());
        assert!(number_optional_number("x1").is_err());
        assert!(number_optional_number("1 x").is_err());
        assert!(number_optional_number("1 , x").is_err());
        assert!(number_optional_number("1 , 2x").is_err());
        assert!(number_optional_number("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(integer("1"), Ok(1));
        assert_eq!(integer("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(integer("").is_err());
        assert!(integer("1x").is_err());
        assert!(integer("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(integer_optional_integer("1, 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1"), Ok((1, 1)));

        assert_eq!(integer_optional_integer("-1, -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1 -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1"), Ok((-1, -1)));
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(integer_optional_integer("").is_err());
        assert!(integer_optional_integer("1x").is_err());
        assert!(integer_optional_integer("x1").is_err());
        assert!(integer_optional_integer("1 x").is_err());
        assert!(integer_optional_integer("1 , x").is_err());
        assert!(integer_optional_integer("1 , 2x").is_err());
        assert!(integer_optional_integer("1 2 x").is_err());
        assert!(integer_optional_integer("1.5").is_err());
        assert!(integer_optional_integer("1 2.5").is_err());
        assert!(integer_optional_integer("1, 2.5").is_err());
    }
}
<commit_msg>Remove unused parse_and_validate()<commit_after>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput};
use markup5ever::QualName;

use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, ValueErrorKind>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str(s: &str) -> Result<Self, ValueErrorKind> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, NodeError>;
}

impl<T: Parse> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).attribute(self.clone())
    }
}

pub trait ParseValueToParseError<T: ParseToParseError> {
    /// Parses a `value` string into a type `T`.
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: ParseToParseError> ParseValueToParseError<T> for QualName {
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse_to_parse_error(&mut parser).attribute(self.clone())
    }

    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse_to_parse_error(&mut parser).attribute(self.clone())?;

        validate(v).map_err(|e| parser.new_custom_error(e)).attribute(self.clone())
    }
}

impl Parse for f64 {
    /// Avoid infinities, and convert to `f64`.
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
    fn parse(parser: &mut Parser<'_, '_>) -> Result<f64, ValueErrorKind> {
        parser
            .expect_number()
            .map_err(|_| ValueErrorKind::parse_error("parse error"))
            .and_then(|n| Ok(f64::from(finite_f32(n)?)))
    }
}

pub trait ParseToParseError: Sized {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>>;
    fn parse_str_to_parse_error<'i>(s: &'i str) -> Result<Self, CssParseError<'i>> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse_to_parse_error(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

impl ParseToParseError for f64 {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let loc = parser.current_source_location();
        parser
            .expect_number()
            .map_err(|e| e.into())
            .and_then(|n| {
                if n.is_finite() {
                    Ok(f64::from(n))
                } else {
                    Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
                }
            })
    }
}

// number-optional-number
//
// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber

pub fn number_optional_number(s: &str) -> Result<(f64, f64), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = f64::parse(&mut parser)?;

    if !parser.is_exhausted() {
        optional_comma(&mut parser);

        let y = f64::parse(&mut parser)?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

// integer
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
pub fn integer(s: &str) -> Result<i32, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(parser.expect_integer()?)
}

// integer-optional-integer
//
// Like number-optional-number but with integers.
pub fn integer_optional_integer(s: &str) -> Result<(i32, i32), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = parser.expect_integer()?;

    if !parser.is_exhausted() {
        optional_comma(&mut parser);

        let y = parser.expect_integer()?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(number_optional_number("1, 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1"), Ok((1.0, 1.0)));

        assert_eq!(number_optional_number("-1, -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1 -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1"), Ok((-1.0, -1.0)));
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(number_optional_number("").is_err());
        assert!(number_optional_number("1x").is_err());
        assert!(number_optional_number("x1").is_err());
        assert!(number_optional_number("1 x").is_err());
        assert!(number_optional_number("1 , x").is_err());
        assert!(number_optional_number("1 , 2x").is_err());
        assert!(number_optional_number("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(integer("1"), Ok(1));
        assert_eq!(integer("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(integer("").is_err());
        assert!(integer("1x").is_err());
        assert!(integer("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(integer_optional_integer("1, 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1"), Ok((1, 1)));

        assert_eq!(integer_optional_integer("-1, -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1 -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1"), Ok((-1, -1)));
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(integer_optional_integer("").is_err());
        assert!(integer_optional_integer("1x").is_err());
        assert!(integer_optional_integer("x1").is_err());
        assert!(integer_optional_integer("1 x").is_err());
        assert!(integer_optional_integer("1 , x").is_err());
        assert!(integer_optional_integer("1 , 2x").is_err());
        assert!(integer_optional_integer("1 2 x").is_err());
        assert!(integer_optional_integer("1.5").is_err());
        assert!(integer_optional_integer("1 2.5").is_err());
        assert!(integer_optional_integer("1, 2.5").is_err());
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection,
    Searcher,
    SearcherBuilder,
    Sink,
    SinkContext,
    SinkContextKind,
    SinkMatch,
};
use ignore::Walk;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self, line: String) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: line,
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

#[derive(Debug)]
struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            let replacement = if let Some(r) = self.replacer.replace(&m.line.1) {
                r
            } else {
                println!("TODO: replacer failed on line");
                continue;
            };
            self.display(m, &replacement);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change(replacement)
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(replacement),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change(replacement)
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change(replacement)
                    }
                }
            };
            change_list.push(change);
        }

        if !change_list.is_empty() {
            self.apply_changes(path, &mut change_list);
        }

        println!("");
    }

    fn display(&self, m: &SearchMatch, replacement: &str) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        print!("+   {}: {}", m.line.0, replacement);

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, mut changes: &[Change]) {
        let src = File::open(path).expect("couldn't open file");
        let reader = BufReader::new(src);
        // TODO: fix this
        let dst = File::create("/tmp/replaceme").expect("couldn't create file");
        let mut writer = BufWriter::new(dst);

        let mut line_num = 0;
        for line in reader.lines() {
            let line = line.unwrap();
            line_num += 1;

            if !changes.is_empty() && changes[0].line_number == line_num {
                writer.write(changes[0].new_line.as_bytes()).unwrap();
                changes = &changes[1..];
            } else {
                // TODO: don't strip off newlines to begin with
                writer.write(line.as_bytes()).unwrap();
                writer.write(b"\n").unwrap();
            };
        }
    }
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    let paths = if opts.paths.is_empty() {
        vec![Path::new("./").to_owned()]
    } else {
        opts.paths
    };

    for path in paths {
        // TODO: Support files as well as directories here
        for dir_entry in Walk::new(path) {
            let dir_entry = dir_entry.unwrap();
            let path = dir_entry.path();
            if !path.is_file() {
                continue;
            }

            let mut sink = SearchMatchCollector::new();

            searcher
                .search_path(&matcher, dir_entry.path(), &mut sink)
                .expect("search failed");

            let matches = sink.collect();
            proc.handle_path(path, matches);
        }
    }

    println!("All done.");
}
<commit_msg>Add editable input<commit_after>use std::cmp::max;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self, line: String) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: line,
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

#[derive(Debug)]
struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            let replacement = if let Some(r) = self.replacer.replace(&m.line.1) {
                r
            } else {
                println!("TODO: replacer failed on line");
                continue;
            };
            self.display(m, &replacement);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change(replacement)
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(replacement),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change(replacement)
                    }
                    ReplacementDecision::EditThis => {
                        print!("[replace with] ");
                        std::io::stdout().flush().unwrap();
                        let mut line: String = read!("{}\n");
                        line.push('\n');
                        self.display(m, &line);
                        println!("--");
                        m.as_change(line)
                    }
                }
            };
            change_list.push(change);
        }

        if !change_list.is_empty() {
            self.apply_changes(path, &mut change_list);
        }

        println!("");
    }

    fn display(&self, m: &SearchMatch, replacement: &str) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        print!("+   {}: {}", m.line.0, replacement);

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, mut changes: &[Change]) {
        let src = File::open(path).expect("couldn't open file");
        let reader = BufReader::new(src);
        // TODO: fix this
        let dst = File::create("/tmp/replaceme").expect("couldn't create file");
        let mut writer = BufWriter::new(dst);

        let mut line_num = 0;
        for line in reader.lines() {
            let line = line.unwrap();
            line_num += 1;

            if !changes.is_empty() && changes[0].line_number == line_num {
                writer.write(changes[0].new_line.as_bytes()).unwrap();
                changes = &changes[1..];
            } else {
                // TODO: don't strip off newlines to begin with
                writer.write(line.as_bytes()).unwrap();
                writer.write(b"\n").unwrap();
            };
        }
    }
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,e,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    let paths = if opts.paths.is_empty() {
        vec![Path::new("./").to_owned()]
    } else {
        opts.paths
    };

    for path in paths {
        // TODO: Support files as well as directories here
        for dir_entry in Walk::new(path) {
            let dir_entry = dir_entry.unwrap();
            let path = dir_entry.path();
            if !path.is_file() {
                continue;
            }

            let mut sink = SearchMatchCollector::new();

            searcher
                .search_path(&matcher, dir_entry.path(), &mut sink)
                .expect("search failed");

            let matches = sink.collect();
            proc.handle_path(path, matches);
        }
    }

    println!("All done.");
}
<|endoftext|>


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

//! Timing functions.

use std_time::precise_time_ns;
use collections::treemap::TreeMap;
use std::comm::{Sender, channel, Receiver};
use std::f64;
use std::iter::AdditiveIterator;
use std::io::timer::sleep;
use task::{spawn_named};

// front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)]
pub struct ProfilerChan(pub Sender<ProfilerMsg>);

impl ProfilerChan {
    pub fn send(&self, msg: ProfilerMsg) {
        let ProfilerChan(ref c) = *self;
        c.send(msg);
    }
}

pub enum ProfilerMsg {
    /// Normal message used for reporting time
    TimeMsg(ProfilerCategory, f64),
    /// Message used to force print the profiling metrics
    PrintMsg,
    /// Tells the profiler to shut down.
    ExitMsg,
}

#[repr(u32)]
#[deriving(Eq, Clone, Ord, TotalEq, TotalOrd)]
pub enum ProfilerCategory {
    CompositingCategory,
    LayoutQueryCategory,
    LayoutPerformCategory,
    LayoutStyleRecalcCategory,
    LayoutSelectorMatchCategory,
    LayoutTreeBuilderCategory,
    LayoutDamagePropagateCategory,
    LayoutMainCategory,
    LayoutParallelWarmupCategory,
    LayoutShapingCategory,
    LayoutDispListBuildCategory,
    GfxRegenAvailableFontsCategory,
    RenderingDrawingCategory,
    RenderingPrepBuffCategory,
    RenderingCategory,
    // FIXME(rust#8803): workaround for lack of CTFE function on enum types to return length
    NumBuckets,
}

impl ProfilerCategory {
    // convenience function to not have to cast every time
    pub fn num_buckets() -> uint {
        NumBuckets as uint
    }

    // enumeration of all ProfilerCategory types
    fn empty_buckets() -> ProfilerBuckets {
        let mut buckets = TreeMap::new();
        buckets.insert(CompositingCategory, ~[]);
        buckets.insert(LayoutQueryCategory, ~[]);
        buckets.insert(LayoutPerformCategory, ~[]);
        buckets.insert(LayoutStyleRecalcCategory, ~[]);
        buckets.insert(LayoutSelectorMatchCategory, ~[]);
        buckets.insert(LayoutTreeBuilderCategory, ~[]);
        buckets.insert(LayoutMainCategory, ~[]);
        buckets.insert(LayoutParallelWarmupCategory, ~[]);
        buckets.insert(LayoutShapingCategory, ~[]);
        buckets.insert(LayoutDamagePropagateCategory, ~[]);
        buckets.insert(LayoutDispListBuildCategory, ~[]);
        buckets.insert(GfxRegenAvailableFontsCategory, ~[]);
        buckets.insert(RenderingDrawingCategory, ~[]);
        buckets.insert(RenderingPrepBuffCategory, ~[]);
        buckets.insert(RenderingCategory, ~[]);

        buckets
    }

    // some categories are subcategories of LayoutPerformCategory
    // and should be printed to indicate this
    pub fn format(self) -> ~str {
        let padding = match self {
            LayoutStyleRecalcCategory |
            LayoutMainCategory |
            LayoutDispListBuildCategory |
            LayoutShapingCategory |
            LayoutDamagePropagateCategory => "+ ",
            LayoutParallelWarmupCategory |
            LayoutSelectorMatchCategory |
            LayoutTreeBuilderCategory => "| + ",
            _ => ""
        };
        format!("{:s}{:?}", padding, self)
    }
}

type ProfilerBuckets = TreeMap<ProfilerCategory, ~[f64]>;

// back end of the profiler that handles data aggregation and performance metrics
pub struct Profiler {
    pub port: Receiver<ProfilerMsg>,
    buckets: ProfilerBuckets,
    pub last_msg: Option<ProfilerMsg>,
}

impl Profiler {
    pub fn create(period: Option<f64>) -> ProfilerChan {
        let (chan, port) = channel();
        match period {
            Some(period) => {
                let period = (period * 1000f64) as u64;
                let chan = chan.clone();
                spawn_named("Profiler timer", proc() {
                    loop {
                        sleep(period);
                        if !chan.try_send(PrintMsg) {
                            break;
                        }
                    }
                });
                // Spawn the profiler
                spawn_named("Profiler", proc() {
                    let mut profiler = Profiler::new(port);
                    profiler.start();
                });
            }
            None => {
                // no-op to handle profiler messages when the profiler is inactive
                spawn_named("Profiler", proc() {
                    loop {
                        match port.recv_opt() {
                            None | Some(ExitMsg) => break,
                            _ => {}
                        }
                    }
                });
            }
        }

        ProfilerChan(chan)
    }

    pub fn new(port: Receiver<ProfilerMsg>) -> Profiler {
        Profiler {
            port: port,
            buckets: ProfilerCategory::empty_buckets(),
            last_msg: None,
        }
    }

    pub fn start(&mut self) {
        loop {
            let msg = self.port.recv_opt();
            match msg {
               Some(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               None => break
            }
        }
    }

    fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
        match msg {
            TimeMsg(category, t) => self.buckets.find_mut(&category).unwrap().push(t),
            PrintMsg => match self.last_msg {
                // only print if more data has arrived since the last printout
                Some(TimeMsg(..)) => self.print_buckets(),
                _ => ()
            },
            ExitMsg => return false,
        };
        self.last_msg = Some(msg);
        true
    }

    fn print_buckets(&mut self) {
        println!("{:39s} {:15s} {:15s} {:15s} {:15s} {:15s}",
                 "_category_", "_mean (ms)_", "_median (ms)_",
                 "_min (ms)_", "_max (ms)_", "_bucket size_");
        for (category, data) in self.buckets.iter() {
            // FIXME(XXX): TreeMap currently lacks mut_iter()
            let mut data = data.clone();
            data.sort_by(|a, b| {
                if a < b {
                    Less
                } else {
                    Greater
                }
            });
            let data_len = data.len();
            if data_len > 0 {
                let (mean, median, min, max) =
                    (data.iter().map(|&x|x).sum() / (data_len as f64),
                     data[data_len / 2],
                     data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
                     data.iter().fold(-f64::INFINITY, |a, &b| a.max(b)));
                println!("{:-35s}: {:15.4f} {:15.4f} {:15.4f} {:15.4f} {:15u}",
                         category.format(), mean, median, min, max, data_len);
            }
        }
        println!("");
    }
}

pub fn profile<T>(category: ProfilerCategory,
                  profiler_chan: ProfilerChan,
                  callback: || -> T)
                  -> T {
    let start_time = precise_time_ns();
    let val = callback();
    let end_time = precise_time_ns();
    let ms = (end_time - start_time) as f64 / 1000000f64;
    profiler_chan.send(TimeMsg(category, ms));
    return val;
}

pub fn time<T>(msg: &str, callback: || -> T) -> T{
    let start_time = precise_time_ns();
    let val = callback();
    let end_time = precise_time_ns();
    let ms = (end_time - start_time) as f64 / 1000000f64;
    if ms >= 5f64 {
        debug!("{:s} took {} ms", msg, ms);
    }
    return val;
}

// ensure that the order of the buckets matches the order of the enum categories
#[test]
fn check_order() {
    let buckets = ProfilerCategory::empty_buckets();
    assert!(buckets.len() == NumBuckets as uint);
}
<commit_msg>~[] to Vec in util/time.rs<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Timing functions.

use std_time::precise_time_ns;
use collections::treemap::TreeMap;
use std::comm::{Sender, channel, Receiver};
use std::f64;
use std::iter::AdditiveIterator;
use std::io::timer::sleep;
use task::{spawn_named};

// front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)]
pub struct ProfilerChan(pub Sender<ProfilerMsg>);

impl ProfilerChan {
    pub fn send(&self, msg: ProfilerMsg) {
        let ProfilerChan(ref c) = *self;
        c.send(msg);
    }
}

pub enum ProfilerMsg {
    /// Normal message used for reporting time
    TimeMsg(ProfilerCategory, f64),
    /// Message used to force print the profiling metrics
    PrintMsg,
    /// Tells the profiler to shut down.
    ExitMsg,
}

#[repr(u32)]
#[deriving(Eq, Clone, Ord, TotalEq, TotalOrd)]
pub enum ProfilerCategory {
    CompositingCategory,
    LayoutQueryCategory,
    LayoutPerformCategory,
    LayoutStyleRecalcCategory,
    LayoutSelectorMatchCategory,
    LayoutTreeBuilderCategory,
    LayoutDamagePropagateCategory,
    LayoutMainCategory,
    LayoutParallelWarmupCategory,
    LayoutShapingCategory,
    LayoutDispListBuildCategory,
    GfxRegenAvailableFontsCategory,
    RenderingDrawingCategory,
    RenderingPrepBuffCategory,
    RenderingCategory,
    // FIXME(rust#8803): workaround for lack of CTFE function on enum types to return length
    NumBuckets,
}

impl ProfilerCategory {
    // convenience function to not have to cast every time
    pub fn num_buckets() -> uint {
        NumBuckets as uint
    }

    // enumeration of all ProfilerCategory types
    fn empty_buckets() -> ProfilerBuckets {
        let mut buckets = TreeMap::new();
        buckets.insert(CompositingCategory, Vec::new());
        buckets.insert(LayoutQueryCategory, Vec::new());
        buckets.insert(LayoutPerformCategory, Vec::new());
        buckets.insert(LayoutStyleRecalcCategory, Vec::new());
        buckets.insert(LayoutSelectorMatchCategory, Vec::new());
        buckets.insert(LayoutTreeBuilderCategory, Vec::new());
        buckets.insert(LayoutMainCategory, Vec::new());
        buckets.insert(LayoutParallelWarmupCategory, Vec::new());
        buckets.insert(LayoutShapingCategory, Vec::new());
        buckets.insert(LayoutDamagePropagateCategory, Vec::new());
        buckets.insert(LayoutDispListBuildCategory, Vec::new());
        buckets.insert(GfxRegenAvailableFontsCategory, Vec::new());
        buckets.insert(RenderingDrawingCategory, Vec::new());
        buckets.insert(RenderingPrepBuffCategory, Vec::new());
        buckets.insert(RenderingCategory, Vec::new());

        buckets
    }

    // some categories are subcategories of LayoutPerformCategory
    // and should be printed to indicate this
    pub fn format(self) -> ~str {
        let padding = match self {
            LayoutStyleRecalcCategory |
            LayoutMainCategory |
            LayoutDispListBuildCategory |
            LayoutShapingCategory |
            LayoutDamagePropagateCategory => "+ ",
            LayoutParallelWarmupCategory |
            LayoutSelectorMatchCategory |
            LayoutTreeBuilderCategory => "| + ",
            _ => ""
        };
        format!("{:s}{:?}", padding, self)
    }
}

type ProfilerBuckets = TreeMap<ProfilerCategory, Vec<f64>>;

// back end of the profiler that handles data aggregation and performance metrics
pub struct Profiler {
    pub port: Receiver<ProfilerMsg>,
    buckets: ProfilerBuckets,
    pub last_msg: Option<ProfilerMsg>,
}

impl Profiler {
    pub fn create(period: Option<f64>) -> ProfilerChan {
        let (chan, port) = channel();
        match period {
            Some(period) => {
                let period = (period * 1000f64) as u64;
                let chan = chan.clone();
                spawn_named("Profiler timer", proc() {
                    loop {
                        sleep(period);
                        if !chan.try_send(PrintMsg) {
                            break;
                        }
                    }
                });
                // Spawn the profiler
                spawn_named("Profiler", proc() {
                    let mut profiler = Profiler::new(port);
                    profiler.start();
                });
            }
            None => {
                // no-op to handle profiler messages when the profiler is inactive
                spawn_named("Profiler", proc() {
                    loop {
                        match port.recv_opt() {
                            None | Some(ExitMsg) => break,
                            _ => {}
                        }
                    }
                });
            }
        }

        ProfilerChan(chan)
    }

    pub fn new(port: Receiver<ProfilerMsg>) -> Profiler {
        Profiler {
            port: port,
            buckets: ProfilerCategory::empty_buckets(),
            last_msg: None,
        }
    }

    pub fn start(&mut self) {
        loop {
            let msg = self.port.recv_opt();
            match msg {
               Some(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               None => break
            }
        }
    }

    fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
        match msg {
            TimeMsg(category, t) => self.buckets.find_mut(&category).unwrap().push(t),
            PrintMsg => match self.last_msg {
                // only print if more data has arrived since the last printout
                Some(TimeMsg(..)) => self.print_buckets(),
                _ => ()
            },
            ExitMsg => return false,
        };
        self.last_msg = Some(msg);
        true
    }

    fn print_buckets(&mut self) {
        println!("{:39s} {:15s} {:15s} {:15s} {:15s} {:15s}",
                 "_category_", "_mean (ms)_", "_median (ms)_",
                 "_min (ms)_", "_max (ms)_", "_bucket size_");
        for (category, data) in self.buckets.iter() {
            // FIXME(XXX): TreeMap currently lacks mut_iter()
            let mut data = data.clone();
            data.sort_by(|a, b| {
                if a < b {
                    Less
                } else {
                    Greater
                }
            });
            let data_len = data.len();
            if data_len > 0 {
                let (mean, median, min, max) =
                    (data.iter().map(|&x|x).sum() / (data_len as f64),
                     data.get(data_len / 2).clone(),
                     data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
                     data.iter().fold(-f64::INFINITY, |a, &b| a.max(b)));
                println!("{:-35s}: {:15.4f} {:15.4f} {:15.4f} {:15.4f} {:15u}",
                         category.format(), mean, median, min, max, data_len);
            }
        }
        println!("");
    }
}

pub fn profile<T>(category: ProfilerCategory,
                  profiler_chan: ProfilerChan,
                  callback: || -> T)
                  -> T {
    let start_time = precise_time_ns();
    let val = callback();
    let end_time = precise_time_ns();
    let ms = (end_time - start_time) as f64 / 1000000f64;
    profiler_chan.send(TimeMsg(category, ms));
    return val;
}

pub fn time<T>(msg: &str, callback: || -> T) -> T{
    let start_time = precise_time_ns();
    let val = callback();
    let end_time = precise_time_ns();
    let ms = (end_time - start_time) as f64 / 1000000f64;
    if ms >= 5f64 {
        debug!("{:s} took {} ms", msg, ms);
    }
    return val;
}

// ensure that the order of the buckets matches the order of the enum categories
#[test]
fn check_order() {
    let buckets = ProfilerCategory::empty_buckets();
    assert!(buckets.len() == NumBuckets as uint);
}
<|endoftext|>


<commit_before>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]

/// Scheme compiler integration tests
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd).

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;
use svm::Inst::*;

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn compile_list_creation() {
    assert_eq!(
        scheme::compile("(cons 10 (cons 20 nil))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 10 (cons 20 nil)))
/// ```
#[test]
fn  compile_list_car() {
    assert_eq!(
        scheme::compile("(car (cons 10 (cons 20 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CAR)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 10 (cons 20 nil)))
/// ```
#[test]
fn compile_list_cdr() {
    assert_eq!(
        scheme::compile("(cdr (cons 10 (cons 20 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
        ))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn compile_simple_add(){
    assert_eq!(
        scheme::compile("(+ 10 10)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn compile_nested_arith() {
     assert_eq!(
        scheme::compile("(- 20 (+ 5 5))"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(ADD),
            InstCell(LDC), AtomCell(SInt(20)),
            InstCell(SUB)
        ))
    );
}

/// Tests for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ```
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ```
#[test]
fn compile_basic_branching() {
    assert_eq!(
        scheme::compile("((if (= 0 (- 1 1)) #t #f)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(0)),
            InstCell(EQ),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
                ListCell(box list!(InstCell(NIL), InstCell(JOIN))
            )
        ))
    );
    assert_eq!(
        scheme::compile("(+ 10 (if (nil? nil) 10 20))"),
        Ok(list!(
            InstCell(NIL), InstCell(NULL),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN))),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN))),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

<commit_msg>Split branching integration target<commit_after>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]

/// Scheme compiler integration tests
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd).

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;
use svm::Inst::*;

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn compile_list_creation() {
    assert_eq!(
        scheme::compile("(cons 10 (cons 20 nil))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 10 (cons 20 nil)))
/// ```
#[test]
fn  compile_list_car() {
    assert_eq!(
        scheme::compile("(car (cons 10 (cons 20 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CAR)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 10 (cons 20 nil)))
/// ```
#[test]
fn compile_list_cdr() {
    assert_eq!(
        scheme::compile("(cdr (cons 10 (cons 20 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
        ))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn compile_simple_add(){
    assert_eq!(
        scheme::compile("(+ 10 10)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn compile_nested_arith() {
     assert_eq!(
        scheme::compile("(- 20 (+ 5 5))"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(ADD),
            InstCell(LDC), AtomCell(SInt(20)),
            InstCell(SUB)
        ))
    );
}

/// Tests for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ```
#[test]
fn compile_basic_branching_1() {
    assert_eq!(
        scheme::compile("((if (= 0 (- 1 1)) #t #f)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(0)),
            InstCell(EQ),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
                ListCell(box list!(InstCell(NIL), InstCell(JOIN))
            )
        ))
    );
}
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ```
#[test]
fn compile_basic_branching_2() {
    assert_eq!(
        scheme::compile("(+ 10 (if (nil? nil) 10 20))"),
        Ok(list!(
            InstCell(NIL), InstCell(NULL),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN))),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN))),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

<|endoftext|>


<commit_before>
// src: https://doc.rust-lang.org/nightly/book/structs.html

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    //let mut a=Point { x:1, y:2};
    let point: &mut Point = &mut Point { x: 0, y: 0 }; //ugleh - immutable point, mutable struct
    //let mut point = Point { x: 0, y: 0 }; //mutable point and struct - no thanks!

    point.x = 5;
    //point = &mut a; //not allowed - GOOD!

    println!("The point is at ({}, {})", point.x, point.y);
}

<commit_msg>here's immutable both<commit_after>
// src: https://doc.rust-lang.org/nightly/book/structs.html

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    //let mut a=Point { x:1, y:2};
    let point: &mut Point = &mut Point { x: 0, y: 0 }; //ugleh - immutable point, mutable struct
    //let mut point = Point { x: 0, y: 0 }; //mutable point and struct - no thanks!
    //let point: &Point = &Point { x: 0, y: 0 }; //immutable point and struct

    point.x = 5;
    //point = &mut a; //not allowed - GOOD!

    println!("The point is at ({}, {})", point.x, point.y);
}

<|endoftext|>


<commit_before>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {

        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"  =>
                                    if VkBlock::Types == cur_block {
                                        if let Some(category) = find_attribute(tag_attrs, "category") {
                                            registry.push_type(type_buffer).ok();
                                            match category {
                                                "basetype"      => type_buffer = VkType::Unhandled,
                                                "bitmask"       => type_buffer = VkType::Unhandled,
                                                "define"        => type_buffer = VkType::Unhandled,
                                                "enum"          => type_buffer = VkType::Unhandled,
                                                "funcpointer"   => type_buffer = VkType::Unhandled,
                                                "group"         => type_buffer = VkType::Unhandled,
                                                "handle"        => type_buffer = VkType::Unhandled,
                                                "include"       => type_buffer = VkType::Unhandled,
                                                "struct"        =>
                                                    type_buffer = VkType::Struct{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), fields: Vec::with_capacity(8)},
                                                "union"         =>
                                                    type_buffer = VkType::Union{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), variants: Vec::with_capacity(8)},
                                                _               => panic!("Unexpected category")
                                            }
                                        }
                                    } else {/*panic!("\"type\" tag found outside of \"types\" block")*/}, //TODO: implement fully so panic can be used

                                "member" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut fields, ..} => fields.push(VkMember::empty()),
                                            VkType::Union{variants: ref mut variants, ..} => variants.push(VkMember::empty()),
                                            _ =>
                                                panic!("Unexpected \"member\" tag found")
                                        }
                                    } else {panic!("\"member\" tag found outside of \"types\" block")},

                                _ => ()
                            },

                        Characters{chars: ref chars, tag} =>
                            match tag {
                                "type" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut members, ..} |
                                            VkType::Union{variants: ref mut members, ..} => members.last_mut().unwrap().set_type(registry.append_str(&chars[..])),
                                            _ => ()
                                        }
                                    },
                                _ => ()
                            }
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..} => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, fields: ref fields} => {
                    println!("Struct {:?}", CStr::from_ptr(name));

                    for f in fields {
                        println!("type: {:?}", f.field_type);
                    }
                }
                VkType::Union{name, variants: ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));

                    for v in variants {
                        println!("type: {:?}", v.field_type);
                    }
                }
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    match vk_elements.pop() {
        Some(el) =>
            if let XmlElement::Characters{..} = el {
                pop_element_stack(vk_elements)
            },

        None => panic!("Invalid xml; Unexpected closing tag")
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<commit_msg>Changed some WTF patterns to read nicer<commit_after>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {

        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"  =>
                                    if VkBlock::Types == cur_block {
                                        if let Some(category) = find_attribute(tag_attrs, "category") {
                                            registry.push_type(type_buffer).ok();
                                            match category {
                                                "basetype"      => type_buffer = VkType::Unhandled,
                                                "bitmask"       => type_buffer = VkType::Unhandled,
                                                "define"        => type_buffer = VkType::Unhandled,
                                                "enum"          => type_buffer = VkType::Unhandled,
                                                "funcpointer"   => type_buffer = VkType::Unhandled,
                                                "group"         => type_buffer = VkType::Unhandled,
                                                "handle"        => type_buffer = VkType::Unhandled,
                                                "include"       => type_buffer = VkType::Unhandled,
                                                "struct"        =>
                                                    type_buffer = VkType::Struct{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), fields: Vec::with_capacity(8)},
                                                "union"         =>
                                                    type_buffer = VkType::Union{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), variants: Vec::with_capacity(8)},
                                                _               => panic!("Unexpected category")
                                            }
                                        }
                                    } else {/*panic!("\"type\" tag found outside of \"types\" block")*/}, //TODO: implement fully so panic can be used

                                "member" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{ref mut fields, ..} => fields.push(VkMember::empty()),
                                            VkType::Union{ref mut variants, ..} => variants.push(VkMember::empty()),
                                            _ =>
                                                panic!("Unexpected \"member\" tag found")
                                        }
                                    } else {panic!("\"member\" tag found outside of \"types\" block")},

                                _ => ()
                            },

                        Characters{ref chars, tag} =>
                            match tag {
                                "type" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut members, ..} |
                                            VkType::Union{variants: ref mut members, ..} => members.last_mut().unwrap().set_type(registry.append_str(&chars[..])),
                                            _ => ()
                                        }
                                    },
                                _ => ()
                            }
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..} => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, ref fields} => {
                    println!("Struct {:?}", CStr::from_ptr(name));

                    for f in fields {
                        println!("type: {:?}", f.field_type);
                    }
                }
                VkType::Union{name, ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));

                    for v in variants {
                        println!("type: {:?}", v.field_type);
                    }
                }
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    match vk_elements.pop() {
        Some(el) =>
            if let XmlElement::Characters{..} = el {
                pop_element_stack(vk_elements)
            },

        None => panic!("Invalid xml; Unexpected closing tag")
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<|endoftext|>


<commit_before>use tikv::raftserver::store::*;

use super::util::*;
use super::cluster::{Cluster, Simulator};
use super::node::new_node_cluster;
use super::server::new_server_cluster;

fn test_multi_base<T: Simulator>(cluster: &mut Cluster<T>) {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

fn test_multi_leader_crash<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(800);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key2);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_some());

    // week up
    cluster.run_node(last_leader.get_node_id());

    sleep_ms(400);
    must_get_equal(&cluster.engines[&last_leader.get_node_id()], key2, value2);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key1);
}

fn test_multi_cluster_restart<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.start();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

fn test_multi_lost_majority<T: Simulator>(cluster: &mut Cluster<T>, count: usize) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let half = (count as u64 + 1) / 2;
    for i in 1..half + 1 {
        cluster.stop_node(i);
    }
    if let Some(leader) = cluster.leader_of_region(1) {
        if leader.get_node_id() >= half + 1 {
            cluster.stop_node(leader.get_node_id());
        }
    }
    cluster.reset_leader_of_region(1);
    sleep_ms(600);

    assert!(cluster.leader_of_region(1).is_none());

}

#[test]
fn test_multi_node_base() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_server_base() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_node_leader_crash() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_server_leader_crash() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_node_cluster_restart() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_server_cluster_restart() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_node_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_node_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}

#[test]
fn test_multi_server_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_server_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}
<commit_msg>Remove unnecessary sleep<commit_after>use tikv::raftserver::store::*;

use super::util::*;
use super::cluster::{Cluster, Simulator};
use super::node::new_node_cluster;
use super::server::new_server_cluster;

fn test_multi_base<T: Simulator>(cluster: &mut Cluster<T>) {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

fn test_multi_leader_crash<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(800);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key2);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_some());

    // week up
    cluster.run_node(last_leader.get_node_id());

    must_get_equal(&cluster.engines[&last_leader.get_node_id()], key2, value2);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key1);
}

fn test_multi_cluster_restart<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.start();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

fn test_multi_lost_majority<T: Simulator>(cluster: &mut Cluster<T>, count: usize) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let half = (count as u64 + 1) / 2;
    for i in 1..half + 1 {
        cluster.stop_node(i);
    }
    if let Some(leader) = cluster.leader_of_region(1) {
        if leader.get_node_id() >= half + 1 {
            cluster.stop_node(leader.get_node_id());
        }
    }
    cluster.reset_leader_of_region(1);
    sleep_ms(600);

    assert!(cluster.leader_of_region(1).is_none());

}

#[test]
fn test_multi_node_base() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_server_base() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_node_leader_crash() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_server_leader_crash() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_node_cluster_restart() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_server_cluster_restart() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_node_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_node_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}

#[test]
fn test_multi_server_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_server_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use crate::error::{Error, Result, Source};
use crate::book::Book;

use std::io::Write;
use std::fs::File;
use std::path::Path;

/// Thait that must be implemented by the various renderers to render a whole book.

pub trait BookRenderer: Sync {
    /// Path destination when output is set to auto
    fn auto_path(&self, _book_file: &str) -> Result<String> {
        Err(Error::default(Source::empty(),
                           lformat!("This renderer does not support the auto output")))
    }

    /// Render the book and write the result to the specified writer
    fn render(&self, book: &Book, to: &mut dyn Write) -> Result<()>;

    /// Render the book to a given file.
    ///
    /// The default implementation creates a file and calls `render` to write to it,
    /// but in some cases it might be useful to override it.
    fn render_to_file(&self, book: &Book, path: &Path) -> Result<()> {
        // Not optimal but avoid creating an empty file if it fails
        let mut content = vec![];
        self.render(book, &mut content)?;
        let mut file = File::create(path)
            .map_err(|err| Error::default(Source::empty(),
                                    lformat!("could not create file '{file}': {err}",
                                             file = path.display(),
                                             err = err)))?;
        file.write_all(&content)
            .map_err(|err| Error::default(Source::empty(),
                                          lformat!("could not write book content to file '{file}': {err}",
                                                   file = path.display(),
                                                   err = err)))?;
        Ok(())
    }
}
<commit_msg>Fix small docs typo.<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use crate::error::{Error, Result, Source};
use crate::book::Book;

use std::io::Write;
use std::fs::File;
use std::path::Path;

/// Trait that must be implemented by the various renderers to render a whole book.

pub trait BookRenderer: Sync {
    /// Path destination when output is set to auto
    fn auto_path(&self, _book_file: &str) -> Result<String> {
        Err(Error::default(Source::empty(),
                           lformat!("This renderer does not support the auto output")))
    }

    /// Render the book and write the result to the specified writer
    fn render(&self, book: &Book, to: &mut dyn Write) -> Result<()>;

    /// Render the book to a given file.
    ///
    /// The default implementation creates a file and calls `render` to write to it,
    /// but in some cases it might be useful to override it.
    fn render_to_file(&self, book: &Book, path: &Path) -> Result<()> {
        // Not optimal but avoid creating an empty file if it fails
        let mut content = vec![];
        self.render(book, &mut content)?;
        let mut file = File::create(path)
            .map_err(|err| Error::default(Source::empty(),
                                    lformat!("could not create file '{file}': {err}",
                                             file = path.display(),
                                             err = err)))?;
        file.write_all(&content)
            .map_err(|err| Error::default(Source::empty(),
                                          lformat!("could not write book content to file '{file}': {err}",
                                                   file = path.display(),
                                                   err = err)))?;
        Ok(())
    }
}
<|endoftext|>