<commit_before>// Copyright 2014 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use rust::respan;
use rust::Token as rtok;
use rust::BinOpToken as rbtok;
use rust;
use std::str::Chars;
use std::iter::Peekable;

use front::ast::*;
use front::ast::Expression::*;

pub struct Parser<'a>
{
  rp: rust::Parser<'a>,
  grammar: FGrammar
}

impl<'a> Parser<'a>
{
  pub fn new(sess: &'a rust::ParseSess,
         cfg: rust::CrateConfig,
         tts: Vec<rust::TokenTree>,
         grammar_name: rust::Ident) -> Parser<'a>
  {
    Parser{
      rp: rust::new_parser_from_tts(sess, cfg, tts),
      grammar: FGrammar::new(grammar_name)
    }
  }

  fn alloc_expr(&mut self, lo: BytePos, hi: BytePos, expr: Expression) -> usize {
    self.grammar.alloc_expr(lo, hi, expr)
  }

  pub fn parse_grammar(mut self) -> rust::PResult<'a, FGrammar> {
    try!(self.parse_blocks());
    Ok(self.grammar)
  }

  fn bump(&mut self) {
    self.rp.bump()
  }

  fn parse_blocks(&mut self) -> rust::PResult<'a, ()> {
    while self.rp.token != rtok::Eof
    {
      try!(self.parse_inner_attributes());
      match try!(self.rp.parse_item()) {
        None => try!(self.parse_rule()),
        Some(item) => self.grammar.push_rust_item(item),
      }
    }
    Ok(())
  }

  fn parse_rule(&mut self) -> rust::PResult<'a, ()> {
    let outer_attrs = try!(self.rp.parse_outer_attributes());
    let name = try!(self.parse_rule_decl());
    try!(self.rp.expect(&rtok::Eq));
    let body = try!(self.parse_rule_rhs(ident_to_string(name.node).as_str()));
    self.grammar.push_rule(name, outer_attrs, body);
    Ok(())
  }

  fn parse_inner_attributes(&mut self) -> rust::PResult<'a, ()> {
    let inners = try!(self.rp.parse_inner_attributes());
    for attr in inners {
      self.grammar.push_attr(attr);
    }
    Ok(())
  }

  fn parse_rule_decl(&mut self) -> rust::PResult<'a, rust::SpannedIdent> {
    let sp = self.rp.span;
    Ok(respan(sp, try!(self.rp.parse_ident())))
  }

  fn parse_rule_rhs(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    self.parse_rule_choice(rule_name)
  }

  fn parse_rule_choice(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let lo = self.rp.span.lo;
    let mut choices = Vec::new();
    loop{
      let seq = try!(self.parse_rule_seq(rule_name));
      choices.push(try!(self.parse_semantic_action_or_ty(seq, rule_name)));
      let token = self.rp.token.clone();
      match token {
        rtok::BinOp(rbtok::Slash) => self.bump(),
        _ => break
      }
    }
    let hi = self.rp.last_span.hi;
    let res = if choices.len() == 1 {
      choices.pop().unwrap()
    } else {
      self.alloc_expr(lo, hi, Choice(choices))
    };
    Ok(res)
  }

  fn parse_semantic_action_or_ty(&mut self, expr: usize, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    let lo = self.rp.span.lo;
    match token {
      rtok::Gt => {
        self.bump();
        let fun_name = try!(self.rp.parse_ident());
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, SemanticAction(expr, fun_name)))
      },
      rtok::RArrow => {
        self.bump();
        self.parse_type(lo, expr, rule_name)
      }
      _ => Ok(expr)
    }
  }

  // `()` or `(^)`
  fn parse_type(&mut self, lo: BytePos, expr: usize, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    match token {
      rtok::OpenDelim(rust::DelimToken::Paren) => {
        self.bump();
        let token = self.rp.token.clone();
        let ty =
          if token == rtok::BinOp(rbtok::Caret) {
            self.bump();
            IType::Invisible
          }
          else {
            IType::Regular(Type::Unit)
          };
        try!(self.rp.expect(&rtok::CloseDelim(rust::DelimToken::Paren)));
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, TypeAscription(expr, ty)))
      }
      _ => {
        let span = self.rp.span;
        self.rp.span_err(
          span,
          format!("In rule {}: Unknown token after `->`. Use the arrow to annotate an expression with the unit type `()` or the invisible type `(^)`.",
            rule_name).as_str()
        );
        Ok(expr)
      }
    }
  }

  fn parse_rule_seq(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let lo = self.rp.span.lo;
    let mut seq = Vec::new();
    while let Some(expr) = try!(self.parse_rule_prefixed(rule_name)) {
      seq.push(expr);
    }
    let hi = self.rp.last_span.hi;
    if seq.len() == 0 {
      self.rp.span_err(
        mk_sp(lo, hi),
        format!("In rule {}: must define at least one expression.",
          rule_name).as_str())
    }
    else if seq.len() == 1 {
      return Ok(seq[0]);
    }
    Ok(self.alloc_expr(lo, hi, Sequence(seq)))
  }

  fn parse_rule_prefixed(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let token = self.rp.token.clone();
    match token {
      rtok::Not => {
        self.parse_prefix(rule_name, |e| NotPredicate(e), "A not predicate (`!expr`)").map(Some)
      }
      rtok::BinOp(rbtok::And) => {
        self.parse_prefix(rule_name, |e| AndPredicate(e), "An and predicate (`&expr`)").map(Some)
      }
      _ => self.parse_rule_suffixed(rule_name)
    }
  }

  fn parse_prefix<F>(&mut self, rule_name: &str, make_prefix: F, pred_name: &str) -> rust::PResult<'a, usize>
   where F: Fn(usize) -> Expression
  {
    let lo = self.rp.span.lo;
    self.bump();
    match try!(self.parse_rule_suffixed(rule_name)) {
      Some(expr) => {
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, make_prefix(expr)))
      }
      None => {
        Err(self.fatal_error(
          format!("In rule {}: {} is not followed by a valid expression.
            Do not forget it must be in front of the expression.",
            rule_name, pred_name).as_str()
        ))
      }
    }
  }

  fn parse_rule_suffixed(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let lo = self.rp.span.lo;
    let expr = match try!(self.parse_rule_atom(rule_name)) {
      Some(expr) => expr,
      None => return Ok(None),
    };
    let hi = self.rp.span.hi;
    let token = self.rp.token.clone();
    let res = match token {
      rtok::BinOp(rbtok::Star) => {
        self.bump();
        self.alloc_expr(lo, hi, ZeroOrMore(expr))
      },
      rtok::BinOp(rbtok::Plus) => {
        self.bump();
        self.alloc_expr(lo, hi, OneOrMore(expr))
      },
      rtok::Question => {
        self.bump();
        self.alloc_expr(lo, hi, ZeroOrOne(expr))
      },
      _ => expr
    };
    Ok(Some(res))
  }

  fn last_respan(&mut self, expr: Expression) -> usize {
    let sp = self.rp.last_span;
    self.alloc_expr(sp.lo, sp.hi, expr)
  }

  fn fatal_error(&mut self, err_msg: &str) -> rust::DiagnosticBuilder<'a> {
    let span = self.rp.span;
    self.rp.span_fatal(span, err_msg)
  }

  fn parse_rule_atom(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let token = self.rp.token.clone();
    let res = match token {
      rtok::Literal(rust::token::Lit::Str_(name),_) => {
        self.bump();
        let cooked_lit = cook_lit(name);
        Some(self.last_respan(StrLiteral(cooked_lit)))
      },
      rtok::Dot => {
        self.bump();
        Some(self.last_respan(AnySingleChar))
      },
      rtok::OpenDelim(rust::DelimToken::Paren) => {
        self.bump();
        let res = try!(self.parse_rule_rhs(rule_name));
        try!(self.rp.expect(&rtok::CloseDelim(rust::DelimToken::Paren)));
        Some(res)
      },
      rtok::Ident(id) if !token.is_any_keyword() => {
        if self.is_rule_lhs() { None }
        else {
          self.bump();
          Some(self.last_respan(NonTerminalSymbol(id)))
        }
      },
      rtok::OpenDelim(rust::DelimToken::Bracket) => {
        self.bump();
        let res = try!(self.parse_char_class(rule_name));
        match self.rp.token {
          rtok::CloseDelim(rust::DelimToken::Bracket) => {
            self.bump();
            Some(res)
          },
          _ => {
            return Err(self.fatal_error(
              format!("In rule {}: A character class must always be terminated by `]` \
                and can only contain a string literal (such as in `[\"a-z\"]`",
                rule_name).as_str()
            ));
          }
        }
      },
      _ => { None }
    };
    Ok(res)
  }

  fn parse_char_class(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    match token {
      rtok::Literal(rust::token::Lit::Str_(name),_) => {
        self.bump();
        let cooked_lit = cook_lit(name);
        Ok(self.parse_set_of_char_range(&cooked_lit, rule_name))
      },
      _ => {
        Err(self.fatal_error(
          format!("In rule {}: Unexpected character in this character class. \
            `[` must only be followed by a string literal (such as in `[\"a-z\"]`)",
            rule_name).as_str()
        ))
      }
    }
  }

  fn parse_set_of_char_range(&mut self, ranges: &String, rule_name: &str) -> usize {
    let mut ranges = ranges.chars().peekable();
    let mut intervals = vec![];
    match ranges.peek() {
      Some(&sep) if sep == '-' => {
        intervals.push(CharacterInterval::new('-', '-'));
        ranges.next();
      }
      _ => ()
    }
    loop {
      let char_set = self.parse_char_range(&mut ranges, rule_name);
      intervals.extend_from_slice(char_set.as_slice());
      if char_set.is_empty() {
          break;
      }
    }
    let sp = self.rp.span;
    self.alloc_expr(sp.lo, sp.hi, CharacterClass(CharacterClassExpr::new(intervals)))
  }

  fn parse_char_range<'b>(&mut self, ranges: &mut Peekable<Chars<'b>>, rule_name: &str) -> Vec<CharacterInterval> {
    let mut res = vec![];
    let separator_err = format!(
      "In rule {}: Unexpected separator `-`. Put it in the start or the end if you want \
      to accept it as a character in the set. Otherwise, you should only use it for \
      character intervals as in `[\"a-z\"]`.",
      rule_name);
    let span = self.rp.span;
    let lo = ranges.next();
    // Twisted logic due to the fact that `peek` borrows the ranges...
    let lo = {
      let next = ranges.peek();
      match (lo, next) {
        (Some('-'), Some(_)) => {
          self.rp.span_err(span, separator_err.as_str());
          return res;
        }
        (Some(lo), Some(&sep)) if sep == '-' => {
          lo
        },
        (Some(lo), _) => {
          res.push(CharacterInterval::new(lo, lo)); // If lo == '-', it ends the class, allowed.
          return res;
        }
        (None, _) => return res,
      }
    };
    ranges.next();
    match ranges.next() {
      Some('-') => { self.rp.span_err(span, separator_err.as_str()); }
      Some(hi) => {
        res.push(CharacterInterval::new(lo, hi));
      }
      None => {
        res.push(CharacterInterval::new(lo, lo));
        res.push(CharacterInterval::new('-', '-'));
      }
    };
    res
  }

  fn is_rule_lhs(&mut self) -> bool {
    self.rp.look_ahead(1, |t| match t { &rtok::Eq => true, _ => false})
  }
}
<commit_msg>[Parser][Bug] Forbid empty character class.<commit_after>// Copyright 2014 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use rust::respan;
use rust::Token as rtok;
use rust::BinOpToken as rbtok;
use rust;
use std::str::Chars;
use std::iter::Peekable;

use front::ast::*;
use front::ast::Expression::*;

pub struct Parser<'a>
{
  rp: rust::Parser<'a>,
  grammar: FGrammar
}

impl<'a> Parser<'a>
{
  pub fn new(sess: &'a rust::ParseSess,
         cfg: rust::CrateConfig,
         tts: Vec<rust::TokenTree>,
         grammar_name: rust::Ident) -> Parser<'a>
  {
    Parser{
      rp: rust::new_parser_from_tts(sess, cfg, tts),
      grammar: FGrammar::new(grammar_name)
    }
  }

  fn alloc_expr(&mut self, lo: BytePos, hi: BytePos, expr: Expression) -> usize {
    self.grammar.alloc_expr(lo, hi, expr)
  }

  pub fn parse_grammar(mut self) -> rust::PResult<'a, FGrammar> {
    try!(self.parse_blocks());
    Ok(self.grammar)
  }

  fn bump(&mut self) {
    self.rp.bump()
  }

  fn parse_blocks(&mut self) -> rust::PResult<'a, ()> {
    while self.rp.token != rtok::Eof
    {
      try!(self.parse_inner_attributes());
      match try!(self.rp.parse_item()) {
        None => try!(self.parse_rule()),
        Some(item) => self.grammar.push_rust_item(item),
      }
    }
    Ok(())
  }

  fn parse_rule(&mut self) -> rust::PResult<'a, ()> {
    let outer_attrs = try!(self.rp.parse_outer_attributes());
    let name = try!(self.parse_rule_decl());
    try!(self.rp.expect(&rtok::Eq));
    let body = try!(self.parse_rule_rhs(ident_to_string(name.node).as_str()));
    self.grammar.push_rule(name, outer_attrs, body);
    Ok(())
  }

  fn parse_inner_attributes(&mut self) -> rust::PResult<'a, ()> {
    let inners = try!(self.rp.parse_inner_attributes());
    for attr in inners {
      self.grammar.push_attr(attr);
    }
    Ok(())
  }

  fn parse_rule_decl(&mut self) -> rust::PResult<'a, rust::SpannedIdent> {
    let sp = self.rp.span;
    Ok(respan(sp, try!(self.rp.parse_ident())))
  }

  fn parse_rule_rhs(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    self.parse_rule_choice(rule_name)
  }

  fn parse_rule_choice(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let lo = self.rp.span.lo;
    let mut choices = Vec::new();
    loop{
      let seq = try!(self.parse_rule_seq(rule_name));
      choices.push(try!(self.parse_semantic_action_or_ty(seq, rule_name)));
      let token = self.rp.token.clone();
      match token {
        rtok::BinOp(rbtok::Slash) => self.bump(),
        _ => break
      }
    }
    let hi = self.rp.last_span.hi;
    let res = if choices.len() == 1 {
      choices.pop().unwrap()
    } else {
      self.alloc_expr(lo, hi, Choice(choices))
    };
    Ok(res)
  }

  fn parse_semantic_action_or_ty(&mut self, expr: usize, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    let lo = self.rp.span.lo;
    match token {
      rtok::Gt => {
        self.bump();
        let fun_name = try!(self.rp.parse_ident());
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, SemanticAction(expr, fun_name)))
      },
      rtok::RArrow => {
        self.bump();
        self.parse_type(lo, expr, rule_name)
      }
      _ => Ok(expr)
    }
  }

  // `()` or `(^)`
  fn parse_type(&mut self, lo: BytePos, expr: usize, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    match token {
      rtok::OpenDelim(rust::DelimToken::Paren) => {
        self.bump();
        let token = self.rp.token.clone();
        let ty =
          if token == rtok::BinOp(rbtok::Caret) {
            self.bump();
            IType::Invisible
          }
          else {
            IType::Regular(Type::Unit)
          };
        try!(self.rp.expect(&rtok::CloseDelim(rust::DelimToken::Paren)));
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, TypeAscription(expr, ty)))
      }
      _ => {
        let span = self.rp.span;
        self.rp.span_err(
          span,
          format!("In rule {}: Unknown token after `->`. Use the arrow to annotate an expression with the unit type `()` or the invisible type `(^)`.",
            rule_name).as_str()
        );
        Ok(expr)
      }
    }
  }

  fn parse_rule_seq(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let lo = self.rp.span.lo;
    let mut seq = Vec::new();
    while let Some(expr) = try!(self.parse_rule_prefixed(rule_name)) {
      seq.push(expr);
    }
    let hi = self.rp.last_span.hi;
    if seq.len() == 0 {
      self.rp.span_err(
        mk_sp(lo, hi),
        format!("In rule {}: must define at least one expression.",
          rule_name).as_str())
    }
    else if seq.len() == 1 {
      return Ok(seq[0]);
    }
    Ok(self.alloc_expr(lo, hi, Sequence(seq)))
  }

  fn parse_rule_prefixed(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let token = self.rp.token.clone();
    match token {
      rtok::Not => {
        self.parse_prefix(rule_name, |e| NotPredicate(e), "A not predicate (`!expr`)").map(Some)
      }
      rtok::BinOp(rbtok::And) => {
        self.parse_prefix(rule_name, |e| AndPredicate(e), "An and predicate (`&expr`)").map(Some)
      }
      _ => self.parse_rule_suffixed(rule_name)
    }
  }

  fn parse_prefix<F>(&mut self, rule_name: &str, make_prefix: F, pred_name: &str) -> rust::PResult<'a, usize>
   where F: Fn(usize) -> Expression
  {
    let lo = self.rp.span.lo;
    self.bump();
    match try!(self.parse_rule_suffixed(rule_name)) {
      Some(expr) => {
        let hi = self.rp.span.hi;
        Ok(self.alloc_expr(lo, hi, make_prefix(expr)))
      }
      None => {
        Err(self.fatal_error(
          format!("In rule {}: {} is not followed by a valid expression.
            Do not forget it must be in front of the expression.",
            rule_name, pred_name).as_str()
        ))
      }
    }
  }

  fn parse_rule_suffixed(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let lo = self.rp.span.lo;
    let expr = match try!(self.parse_rule_atom(rule_name)) {
      Some(expr) => expr,
      None => return Ok(None),
    };
    let hi = self.rp.span.hi;
    let token = self.rp.token.clone();
    let res = match token {
      rtok::BinOp(rbtok::Star) => {
        self.bump();
        self.alloc_expr(lo, hi, ZeroOrMore(expr))
      },
      rtok::BinOp(rbtok::Plus) => {
        self.bump();
        self.alloc_expr(lo, hi, OneOrMore(expr))
      },
      rtok::Question => {
        self.bump();
        self.alloc_expr(lo, hi, ZeroOrOne(expr))
      },
      _ => expr
    };
    Ok(Some(res))
  }

  fn last_respan(&mut self, expr: Expression) -> usize {
    let sp = self.rp.last_span;
    self.alloc_expr(sp.lo, sp.hi, expr)
  }

  fn fatal_error(&mut self, err_msg: &str) -> rust::DiagnosticBuilder<'a> {
    let span = self.rp.span;
    self.rp.span_fatal(span, err_msg)
  }

  fn parse_rule_atom(&mut self, rule_name: &str) -> rust::PResult<'a, Option<usize>> {
    let token = self.rp.token.clone();
    let res = match token {
      rtok::Literal(rust::token::Lit::Str_(name),_) => {
        self.bump();
        let cooked_lit = cook_lit(name);
        Some(self.last_respan(StrLiteral(cooked_lit)))
      },
      rtok::Dot => {
        self.bump();
        Some(self.last_respan(AnySingleChar))
      },
      rtok::OpenDelim(rust::DelimToken::Paren) => {
        self.bump();
        let res = try!(self.parse_rule_rhs(rule_name));
        try!(self.rp.expect(&rtok::CloseDelim(rust::DelimToken::Paren)));
        Some(res)
      },
      rtok::Ident(id) if !token.is_any_keyword() => {
        if self.is_rule_lhs() { None }
        else {
          self.bump();
          Some(self.last_respan(NonTerminalSymbol(id)))
        }
      },
      rtok::OpenDelim(rust::DelimToken::Bracket) => {
        self.bump();
        let res = try!(self.parse_char_class(rule_name));
        match self.rp.token {
          rtok::CloseDelim(rust::DelimToken::Bracket) => {
            self.bump();
            Some(res)
          },
          _ => {
            return Err(self.fatal_error(
              format!("In rule {}: A character class must always be terminated by `]` \
                and can only contain a string literal (such as in `[\"a-z\"]`",
                rule_name).as_str()
            ));
          }
        }
      },
      _ => { None }
    };
    Ok(res)
  }

  fn parse_char_class(&mut self, rule_name: &str) -> rust::PResult<'a, usize> {
    let token = self.rp.token.clone();
    match token {
      rtok::Literal(rust::token::Lit::Str_(name),_) => {
        let span = self.rp.span;
        self.bump();
        let cooked_lit = cook_lit(name);
        if cooked_lit.is_empty() {
          self.rp.span_err(span,
            "Empty character classes is forbidden. For empty expression \
            you can use the empty string literal `\"\"`.");
        }
        Ok(self.parse_set_of_char_range(&cooked_lit, rule_name))
      },
      _ => {
        Err(self.fatal_error(
          format!("In rule {}: Unexpected character in this character class. \
            `[` must only be followed by a string literal (such as in `[\"a-z\"]`)",
            rule_name).as_str()
        ))
      }
    }
  }

  fn parse_set_of_char_range(&mut self, ranges: &String, rule_name: &str) -> usize {
    let mut ranges = ranges.chars().peekable();
    let mut intervals = vec![];
    match ranges.peek() {
      Some(&sep) if sep == '-' => {
        intervals.push(CharacterInterval::new('-', '-'));
        ranges.next();
      }
      _ => ()
    }
    loop {
      let char_set = self.parse_char_range(&mut ranges, rule_name);
      intervals.extend_from_slice(char_set.as_slice());
      if char_set.is_empty() {
          break;
      }
    }
    let sp = self.rp.span;
    self.alloc_expr(sp.lo, sp.hi, CharacterClass(CharacterClassExpr::new(intervals)))
  }

  fn parse_char_range<'b>(&mut self, ranges: &mut Peekable<Chars<'b>>, rule_name: &str) -> Vec<CharacterInterval> {
    let mut res = vec![];
    let separator_err = format!(
      "In rule {}: Unexpected separator `-`. Put it in the start or the end if you want \
      to accept it as a character in the set. Otherwise, you should only use it for \
      character intervals as in `[\"a-z\"]`.",
      rule_name);
    let span = self.rp.span;
    let lo = ranges.next();
    // Twisted logic due to the fact that `peek` borrows the ranges...
    let lo = {
      let next = ranges.peek();
      match (lo, next) {
        (Some('-'), Some(_)) => {
          self.rp.span_err(span, separator_err.as_str());
          return res;
        }
        (Some(lo), Some(&sep)) if sep == '-' => {
          lo
        },
        (Some(lo), _) => {
          res.push(CharacterInterval::new(lo, lo)); // If lo == '-', it ends the class, allowed.
          return res;
        }
        (None, _) => return res,
      }
    };
    ranges.next();
    match ranges.next() {
      Some('-') => { self.rp.span_err(span, separator_err.as_str()); }
      Some(hi) => {
        res.push(CharacterInterval::new(lo, hi));
      }
      None => {
        res.push(CharacterInterval::new(lo, lo));
        res.push(CharacterInterval::new('-', '-'));
      }
    };
    res
  }

  fn is_rule_lhs(&mut self) -> bool {
    self.rp.look_ahead(1, |t| match t { &rtok::Eq => true, _ => false})
  }
}
<|endoftext|>


<commit_before>#![cfg(feature = "alloc")]

#[macro_use]
extern crate criterion;
#[macro_use]
extern crate combine;

use std::fmt;

use {
    combine::{
        many, many1,
        parser::range::{range, take_while1},
        stream::easy,
        token, ParseError, Parser, RangeStream,
    },
    criterion::{black_box, Bencher, Criterion},
};

#[derive(Debug)]
struct Request<'a> {
    method: &'a [u8],
    uri: &'a [u8],
    version: &'a [u8],
}

#[derive(Debug)]
struct Header<'a> {
    name: &'a [u8],
    value: Vec<&'a [u8]>,
}

fn is_token(c: u8) -> bool {
    !matches!(
        c,
        128..=255
        | 0..=31
        | b'('
        | b')'
        | b'<'
        | b'>'
        | b'@'
        | b','
        | b';'
        | b':'
        | b'\\'
        | b'"'
        | b'/'
        | b'['
        | b']'
        | b'?'
        | b'='
        | b'{'
        | b'}'
        | b' '
    )
}

fn is_horizontal_space(c: u8) -> bool {
    c == b' ' || c == b'\t'
}
fn is_space(c: u8) -> bool {
    c == b' '
}
fn is_not_space(c: u8) -> bool {
    c != b' '
}
fn is_http_version(c: u8) -> bool {
    (b'0'..=b'9').contains(&c)
}

fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    (token(b'\r'), token(b'\n')).map(|_| b'\r').or(token(b'\n'))
}

fn message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    let message_header_line = (
        take_while1(is_horizontal_space),
        take_while1(|c| c != b'\r' && c != b'\n'),
        end_of_line(),
    )
        .map(|(_, line, _)| line);

    struct_parser!(Header {
        name: take_while1(is_token),
        _: token(b':'),
        value: many1(message_header_line),
    })
}

type HttpRequest<'a> = (Request<'a>, Vec<Header<'a>>);

fn parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    let http_version = range(&b"HTTP/"[..]).with(take_while1(is_http_version));

    let request_line = struct_parser!(Request {
        method: take_while1(is_token),
        _: take_while1(is_space),
        uri: take_while1(is_not_space),
        _: take_while1(is_space),
        version: http_version,
    });

    let mut request = (
        request_line,
        end_of_line(),
        many(message_header()),
        end_of_line(),
    )
        .map(|(request, _, headers, _)| (request, headers));

    request.parse(input)
}

static REQUESTS: &[u8] = include_bytes!("http-requests.txt");

fn http_requests_small(b: &mut Bencher<'_>) {
    http_requests_bench(b, easy::Stream(REQUESTS))
}

fn http_requests_large(b: &mut Bencher<'_>) {
    use std::iter;

    let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
    for buf in iter::repeat(REQUESTS).take(5) {
        buffer.extend_from_slice(buf);
    }
    http_requests_bench(b, easy::Stream(&buffer[..]))
}

fn http_requests_large_cheap_error(b: &mut Bencher<'_>) {
    use std::iter;

    let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
    for buf in iter::repeat(REQUESTS).take(5) {
        buffer.extend_from_slice(buf);
    }
    http_requests_bench(b, &buffer[..])
}

fn http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input)
where
    Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position> + fmt::Debug,
{
    b.iter(|| {
        let mut buf = black_box(buffer.clone());

        while buf.clone().uncons().is_ok() {
            match parse_http_request(buf) {
                Ok(((_, _), b)) => {
                    buf = b;
                }
                Err(err) => panic!("{:?}", err),
            }
        }
    });
}

fn http_requests(c: &mut Criterion) {
    c.bench_function("http_requests_small", http_requests_small);
    c.bench_function("http_requests_large", http_requests_large);
    c.bench_function(
        "http_requests_large_cheap_error",
        http_requests_large_cheap_error,
    );
}

criterion_group!(http, http_requests,);
criterion_main!(http);
<commit_msg>Fix http bench<commit_after>#![cfg(feature = "alloc")]

#[macro_use]
extern crate criterion;
#[macro_use]
extern crate combine;

use std::fmt;

use {
    combine::{
        many, many1,
        parser::range::{range, take_while1},
        stream::easy,
        token, ParseError, Parser, RangeStream,
    },
    criterion::{black_box, Bencher, Criterion},
};

#[derive(Debug)]
struct Request<'a> {
    method: &'a [u8],
    uri: &'a [u8],
    version: &'a [u8],
}

#[derive(Debug)]
struct Header<'a> {
    name: &'a [u8],
    value: Vec<&'a [u8]>,
}

fn is_token(c: u8) -> bool {
    !matches!(
        c,
        128..=255
        | 0..=31
        | b'('
        | b')'
        | b'<'
        | b'>'
        | b'@'
        | b','
        | b';'
        | b':'
        | b'\\'
        | b'"'
        | b'/'
        | b'['
        | b']'
        | b'?'
        | b'='
        | b'{'
        | b'}'
        | b' '
    )
}

fn is_horizontal_space(c: u8) -> bool {
    c == b' ' || c == b'\t'
}
fn is_space(c: u8) -> bool {
    c == b' '
}
fn is_not_space(c: u8) -> bool {
    c != b' '
}
fn is_http_version(c: u8) -> bool {
    (b'0'..=b'9').contains(&c) || c == b'.'
}

fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    (token(b'\r'), token(b'\n')).map(|_| b'\r').or(token(b'\n'))
}

fn message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    let message_header_line = (
        take_while1(is_horizontal_space),
        take_while1(|c| c != b'\r' && c != b'\n'),
        end_of_line(),
    )
        .map(|(_, line, _)| line);

    struct_parser!(Header {
        name: take_while1(is_token),
        _: token(b':'),
        value: many1(message_header_line),
    })
}

type HttpRequest<'a> = (Request<'a>, Vec<Header<'a>>);

fn parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error>
where
    Input: RangeStream<Token = u8, Range = &'a [u8]>,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    let http_version = range(&b"HTTP/"[..]).with(take_while1(is_http_version));

    let request_line = struct_parser!(Request {
        method: take_while1(is_token),
        _: take_while1(is_space),
        uri: take_while1(is_not_space),
        _: take_while1(is_space),
        version: http_version,
    });

    let mut request = (
        request_line,
        end_of_line(),
        many(message_header()),
        end_of_line(),
    )
        .map(|(request, _, headers, _)| (request, headers));

    request.parse(input)
}

static REQUESTS: &[u8] = include_bytes!("http-requests.txt");

fn http_requests_small(b: &mut Bencher<'_>) {
    http_requests_bench(b, easy::Stream(REQUESTS))
}

fn http_requests_large(b: &mut Bencher<'_>) {
    use std::iter;

    let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
    for buf in iter::repeat(REQUESTS).take(5) {
        buffer.extend_from_slice(buf);
    }
    http_requests_bench(b, easy::Stream(&buffer[..]))
}

fn http_requests_large_cheap_error(b: &mut Bencher<'_>) {
    use std::iter;

    let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
    for buf in iter::repeat(REQUESTS).take(5) {
        buffer.extend_from_slice(buf);
    }
    http_requests_bench(b, &buffer[..])
}

fn http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input)
where
    Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone,
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position> + fmt::Debug,
{
    b.iter(|| {
        let mut buf = black_box(buffer.clone());

        while buf.clone().uncons().is_ok() {
            match parse_http_request(buf) {
                Ok(((_, _), b)) => {
                    buf = b;
                }
                Err(err) => panic!("{:?}", err),
            }
        }
    });
}

fn http_requests(c: &mut Criterion) {
    c.bench_function("http_requests_small", http_requests_small);
    c.bench_function("http_requests_large", http_requests_large);
    c.bench_function(
        "http_requests_large_cheap_error",
        http_requests_large_cheap_error,
    );
}

criterion_group!(http, http_requests,);
criterion_main!(http);
<|endoftext|>


<commit_before>//! Functions for computing [local binary patterns](https://en.wikipedia.org/wiki/Local_binary_patterns).

use image::{
    GenericImage,
    Luma
};
use std::cmp;

/// Computes the basic local binary pattern of a pixel, or None
/// if it's too close to the image boundary.
pub fn local_binary_pattern<I>(image: &I, x: u32, y: u32) -> Option<u8>
    where I: GenericImage<Pixel=Luma<u8>> {

    let (width, height) = image.dimensions();
    if width == 0 || height == 0 {
        return None;
    }

    // TODO: It might be better to make this function private, and
    // TODO: require the caller to only provide valid x and y coordinates.
    if x == 0 || x >= width - 1 || y == 0 || y >= height - 1 {
        return None;
    }

    // TODO: As with the fast corner detectors, this would be more efficient if
    // TODO: generated a list of pixel offsets once per image, and used those
    // TODO: offsets directly when reading pixels. To do this we'd need some traits
    // TODO: for images whose pixels are stored in contiguous rows/columns.
    let mut pattern = 0u8;

    let center = image.get_pixel(x, y)[0];

    // The sampled pixels have the following labels.
    //
    // 7  0  1
    // 6  p  2
    // 5  4  3
    //
    // The nth bit of a pattern is 1 if the pixel p
    // is strictly brighter than the neighbor in position n.
    let neighbors = [
        image.get_pixel(x    , y - 1)[0],
        image.get_pixel(x + 1, y - 1)[0],
        image.get_pixel(x + 1, y    )[0],
        image.get_pixel(x + 1, y + 1)[0],
        image.get_pixel(x    , y + 1)[0],
        image.get_pixel(x - 1, y + 1)[0],
        image.get_pixel(x - 1, y    )[0],
        image.get_pixel(x - 1, y - 1)[0]
    ];

    for i in 0..8 {
        pattern |= (1 & (neighbors[i] < center) as u8) << i;
    }

    Some(pattern)
}

// TODO: add lookup tables from pattern to pattern class, one just assigning
// TODO: the least circular shift and the other lumping all non-uniform patterns together

/// Returns the minimum value over all rotations of a byte.
pub fn min_shift(byte: u8) -> u8 {
    let mut min = byte;
    for i in 1..8 {
        min = cmp::min(min, byte.rotate_right(i));
    }
    min
}

#[cfg(test)]
mod test {

    use super::{
        local_binary_pattern,
        min_shift
    };
    use image::{
        GenericImage,
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_local_binary_pattern() {

        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            06, 11, 14,
            09, 10, 10,
            19, 00, 22]).unwrap();

        let expected = 0b11010000;
        let pattern = local_binary_pattern(&image, 1, 1).unwrap();

        assert_eq!(pattern, expected);
    }

    #[test]
    fn test_min_shift() {
        let byte = 0b10110100;
        assert_eq!(min_shift(byte), 0b00101101);
    }
}
<commit_msg>Add MIN_SHIFT lookup table<commit_after>//! Functions for computing [local binary patterns](https://en.wikipedia.org/wiki/Local_binary_patterns).

use image::{
    GenericImage,
    Luma
};
use std::cmp;

/// Computes the basic local binary pattern of a pixel, or None
/// if it's too close to the image boundary.
pub fn local_binary_pattern<I>(image: &I, x: u32, y: u32) -> Option<u8>
    where I: GenericImage<Pixel=Luma<u8>> {

    let (width, height) = image.dimensions();
    if width == 0 || height == 0 {
        return None;
    }

    // TODO: It might be better to make this function private, and
    // TODO: require the caller to only provide valid x and y coordinates.
    if x == 0 || x >= width - 1 || y == 0 || y >= height - 1 {
        return None;
    }

    // TODO: As with the fast corner detectors, this would be more efficient if
    // TODO: generated a list of pixel offsets once per image, and used those
    // TODO: offsets directly when reading pixels. To do this we'd need some traits
    // TODO: for images whose pixels are stored in contiguous rows/columns.
    let mut pattern = 0u8;

    let center = image.get_pixel(x, y)[0];

    // The sampled pixels have the following labels.
    //
    // 7  0  1
    // 6  p  2
    // 5  4  3
    //
    // The nth bit of a pattern is 1 if the pixel p
    // is strictly brighter than the neighbor in position n.
    let neighbors = [
        image.get_pixel(x    , y - 1)[0],
        image.get_pixel(x + 1, y - 1)[0],
        image.get_pixel(x + 1, y    )[0],
        image.get_pixel(x + 1, y + 1)[0],
        image.get_pixel(x    , y + 1)[0],
        image.get_pixel(x - 1, y + 1)[0],
        image.get_pixel(x - 1, y    )[0],
        image.get_pixel(x - 1, y - 1)[0]
    ];

    for i in 0..8 {
        pattern |= (1 & (neighbors[i] < center) as u8) << i;
    }

    Some(pattern)
}

// TODO: add lookup tables from pattern to pattern class, one just assigning
// TODO: the least circular shift and the other lumping all non-uniform patterns together

/// Returns the least value of all rotations of a byte.
pub fn min_shift(byte: u8) -> u8 {
    let mut min = byte;
    for i in 1..8 {
        min = cmp::min(min, byte.rotate_right(i));
    }
    min
}

/// Lookup table for the least value of all rotations of a byte.
let static MIN_SHIFT: [u8; 256] = [
0,   // 0
1,   // 1
1,   // 2
3,   // 3
1,   // 4
5,   // 5
3,   // 6
7,   // 7
1,   // 8
9,   // 9
5,   // 10
11,  // 11
3,   // 12
13,  // 13
7,   // 14
15,  // 15
1,   // 16
17,  // 17
9,   // 18
19,  // 19
5,   // 20
21,  // 21
11,  // 22
23,  // 23
3,   // 24
25,  // 25
13,  // 26
27,  // 27
7,   // 28
29,  // 29
15,  // 30
31,  // 31
1,   // 32
9,   // 33
17,  // 34
25,  // 35
9,   // 36
37,  // 37
19,  // 38
39,  // 39
5,   // 40
37,  // 41
21,  // 42
43,  // 43
11,  // 44
45,  // 45
23,  // 46
47,  // 47
3,   // 48
19,  // 49
25,  // 50
51,  // 51
13,  // 52
53,  // 53
27,  // 54
55,  // 55
7,   // 56
39,  // 57
29,  // 58
59,  // 59
15,  // 60
61,  // 61
31,  // 62
63,  // 63
1,   // 64
5,   // 65
9,   // 66
13,  // 67
17,  // 68
21,  // 69
25,  // 70
29,  // 71
9,   // 72
37,  // 73
37,  // 74
45,  // 75
19,  // 76
53,  // 77
39,  // 78
61,  // 79
5,   // 80
21,  // 81
37,  // 82
53,  // 83
21,  // 84
85,  // 85
43,  // 86
87,  // 87
11,  // 88
43,  // 89
45,  // 90
91,  // 91
23,  // 92
87,  // 93
47,  // 94
95,  // 95
3,   // 96
11,  // 97
19,  // 98
27,  // 99
25,  // 100
43,  // 101
51,  // 102
59,  // 103
13,  // 104
45,  // 105
53,  // 106
91,  // 107
27,  // 108
91,  // 109
55,  // 110
111, // 111
7,   // 112
23,  // 113
39,  // 114
55,  // 115
29,  // 116
87,  // 117
59,  // 118
119, // 119
15,  // 120
47,  // 121
61,  // 122
111, // 123
31,  // 124
95,  // 125
63,  // 126
127, // 127
1,   // 128
3,   // 129
5,   // 130
7,   // 131
9,   // 132
11,  // 133
13,  // 134
15,  // 135
17,  // 136
19,  // 137
21,  // 138
23,  // 139
25,  // 140
27,  // 141
29,  // 142
31,  // 143
9,   // 144
25,  // 145
37,  // 146
39,  // 147
37,  // 148
43,  // 149
45,  // 150
47,  // 151
19,  // 152
51,  // 153
53,  // 154
55,  // 155
39,  // 156
59,  // 157
61,  // 158
63,  // 159
5,   // 160
13,  // 161
21,  // 162
29,  // 163
37,  // 164
45,  // 165
53,  // 166
61,  // 167
21,  // 168
53,  // 169
85,  // 170
87,  // 171
43,  // 172
91,  // 173
87,  // 174
95,  // 175
11,  // 176
27,  // 177
43,  // 178
59,  // 179
45,  // 180
91,  // 181
91,  // 182
111, // 183
23,  // 184
55,  // 185
87,  // 186
119, // 187
47,  // 188
111, // 189
95,  // 190
127, // 191
3,   // 192
7,   // 193
11,  // 194
15,  // 195
19,  // 196
23,  // 197
27,  // 198
31,  // 199
25,  // 200
39,  // 201
43,  // 202
47,  // 203
51,  // 204
55,  // 205
59,  // 206
63,  // 207
13,  // 208
29,  // 209
45,  // 210
61,  // 211
53,  // 212
87,  // 213
91,  // 214
95,  // 215
27,  // 216
59,  // 217
91,  // 218
111, // 219
55,  // 220
119, // 221
111, // 222
127, // 223
7,   // 224
15,  // 225
23,  // 226
31,  // 227
39,  // 228
47,  // 229
55,  // 230
63,  // 231
29,  // 232
61,  // 233
87,  // 234
95,  // 235
59,  // 236
111, // 237
119, // 238
127, // 239
15,  // 240
31,  // 241
47,  // 242
63,  // 243
61,  // 244
95,  // 245
111, // 246
127, // 247
31,  // 248
63,  // 249
95,  // 250
127, // 251
63,  // 252
127, // 253
127, // 254
255, // 255
];

#[cfg(test)]
mod test {

    use super::{
        local_binary_pattern,
        min_shift
    };
    use image::{
        GenericImage,
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_local_binary_pattern() {

        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            06, 11, 14,
            09, 10, 10,
            19, 00, 22]).unwrap();

        let expected = 0b11010000;
        let pattern = local_binary_pattern(&image, 1, 1).unwrap();

        assert_eq!(pattern, expected);
    }

    #[test]
    fn test_min_shift() {
        let byte = 0b10110100;
        assert_eq!(min_shift(byte), 0b00101101);
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use self::chrono::{DateTime, Duration, Local};
use self::chrono::offset::TimeZone;

use constant::STORAGE_DIR_NAME;
use lib::fs::*;
use lib::io::*;
use std::fs::{self, OpenOptions};
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Write};
use std::path::{Path, PathBuf};

pub struct Storage {
    now     : DateTime<Local>,
    date    : String,
    log_file: Option<String>,
}

impl Storage {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), STORAGE_DIR_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_dir()
    }

    pub fn create() -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!("Create \"{}\" directory", STORAGE_DIR_NAME));

        super::create_essential_dir(Self::path())
    }

    pub fn new() -> Self {
        let now  = Local::now();
        let date = now.format("%Y-%m-%d").to_string();
        Storage { now: now, date: date, log_file: None }
    }

    fn path_to_box(&self) -> PathBuf {
        path_buf![Storage::path(), &self.date]
    }

    fn path_to_dust_box(&self) -> PathBuf {
        path_buf![self.path_to_box(), "dusts"]
    }

    fn create_box(&self) -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!(r#"Create "{}" directory in "{}""#, self.date, Self::path().display()));

        fs::create_dir_all(self.path_to_dust_box())
    }

    fn path_to_log(&self) -> PathBuf {
        let file_name = match &self.log_file {
            &Some(ref s) => s,
            &None        => unreachable!("Wrong to use path_to_log()"),
        };
        path_buf![self.path_to_box(), &file_name]
    }

    fn write_log<U: AsRef<[u8]>>(&self, content: U) -> Result<usize, IoError> {
        let file = OpenOptions::new()
            .create(true)
            .write(true) // it's needed against doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append
            .append(true)
            .open(self.path_to_log());
        let mut file = try!(file);

        file.write(content.as_ref())
    }

    fn start_mark_for_log(&self) -> String {
        let datetime = self.now.format("%H:%M:%S").to_string();
        format!(
            "############
# {} #
############\n",
            datetime,
        )
    }

    fn create_log_file(mut self, file_name: &str) -> Result<Self, IoError> {
        let file_name = format!("{}.log", file_name);

        print_with_tag(Tag::Info, format!(r#"Create "{}" file in "{}""#, file_name, self.path_to_box().display()));

        self.log_file = Some(file_name);

        try!(self.write_log(self.start_mark_for_log()));

        Ok(self)
    }

    pub fn create_box_with_log(self, file_name: &str) -> Result<Self, IoError> {
        try!(self.create_box());
        self.create_log_file(file_name)
    }

    pub fn squeeze_dusts<P: AsRef<Path>>(&self, paths_to_dust: Vec<P>) -> Result<(), IoError> {
        let path_to_dust_box = self.path_to_dust_box();

        print_with_tag(Tag::Info, format!("Move dusts to \"{}\"", path_to_dust_box.display()));

        for path_to_dust in &paths_to_dust {
            let path_to_dust = path_to_dust.as_ref();
            let target_file  = match path_to_dust.file_name() {
                Some(f) => f,
                None    => unreachable!("Cannot get file name from path!!"),
            };
            let target_base = match path_to_dust.parent() {
                Some(b) => b,
                None    => unreachable!("Cannot get base name from path!!"),
            };

            let to = path_buf![&path_to_dust_box, target_base];

            let message = format!("  => \"{}\"", path_to_dust.display());
            print_with_tag(Tag::Info, message);

            try!(fs::create_dir_all(&to));

            // forcedly overwrite if the file exists with same name.
            match fs::rename(path_to_dust, path_buf![to, target_file]) {
                Ok(_)  => (),
                Err(e) => match e.kind() {
                    IoErrorKind::PermissionDenied => print_with_tag(Tag::Info, "     Interrupted for permission"),
                    _                             => return Err(From::from(e)),
                },
            };
        }

        Ok(())
    }

    pub fn squeeze_empty_dirs_only<P: AsRef<Path>>(&self, paths_to_dir: Vec<P>) -> Result<(), IoError> {
        let path_to_dust_box = self.path_to_dust_box();

        print_with_tag(Tag::Info, format!("Move empty dirs to \"{}\"", path_to_dust_box.display()));

        for path_to_dir in &paths_to_dir {
            if is_empty_dir(path_to_dir) {
                let path_to_dir = path_to_dir.as_ref();

                let message = format!("  => \"{}\"", path_to_dir.display());
                print_with_tag(Tag::Info, message);

                match fs::remove_dir(path_to_dir) {
                    Ok(_)  => try!(fs::create_dir_all(path_buf![&path_to_dust_box, path_to_dir])),
                    Err(e) => match e.kind() {
                        IoErrorKind::PermissionDenied => print_with_tag(Tag::Info, "     Interrupted for permission"),
                        _                             => return Err(From::from(e)),
                    },
                };
            }
        }

        Ok(())
    }

    pub fn delete_expired_boxes(&self, moratorium: Duration) -> Result<(), IoError> {
        let path_to_storage = Self::path();

        print_with_tag(Tag::Info, "Delete expired dusts");

        let target_boxes = try!(ls(&path_to_storage))
            .into_iter()
            .filter(|date| match Local.datetime_from_str(format!("{} 00:00:00", date).as_ref(), "%Y-%m-%d %H:%M:%S") {
                Ok(created_date) => created_date + moratorium < self.now,
                Err(_)           => false,
            })
            .map(|dir| path_to_storage.join(dir))
            .collect::<Vec<PathBuf>>();

        for target_box in &target_boxes {
            print_with_tag(Tag::Info, format!("  => \"{}\"", target_box.display()));
            try!(fs::remove_dir_all(target_box));
        };

        Ok(())
    }
}
<commit_msg>Update rust to 1.8.0<commit_after>extern crate chrono;

use self::chrono::{DateTime, Duration, Local};
use self::chrono::offset::TimeZone;

use constant::STORAGE_DIR_NAME;
use lib::fs::*;
use lib::io::*;
use std::fs::{self, OpenOptions};
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Write};
use std::path::{Path, PathBuf};

pub struct Storage {
    now     : DateTime<Local>,
    date    : String,
    log_file: Option<String>,
}

impl Storage {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), STORAGE_DIR_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_dir()
    }

    pub fn create() -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!("Create \"{}\" directory", STORAGE_DIR_NAME));

        super::create_essential_dir(Self::path())
    }

    pub fn new() -> Self {
        let now  = Local::now();
        let date = now.format("%Y-%m-%d").to_string();
        Storage { now: now, date: date, log_file: None }
    }

    fn path_to_box(&self) -> PathBuf {
        path_buf![Storage::path(), &self.date]
    }

    fn path_to_dust_box(&self) -> PathBuf {
        path_buf![self.path_to_box(), "dusts"]
    }

    fn create_box(&self) -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!(r#"Create "{}" directory in "{}""#, self.date, Self::path().display()));

        fs::create_dir_all(self.path_to_dust_box())
    }

    fn path_to_log(&self) -> PathBuf {
        let file_name = match &self.log_file {
            &Some(ref s) => s,
            &None        => unreachable!("Wrong to use path_to_log()"),
        };
        path_buf![self.path_to_box(), &file_name]
    }

    fn write_log<U: AsRef<[u8]>>(&self, content: U) -> Result<usize, IoError> {
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(self.path_to_log());
        let mut file = try!(file);

        file.write(content.as_ref())
    }

    fn start_mark_for_log(&self) -> String {
        let datetime = self.now.format("%H:%M:%S").to_string();
        format!(
            "############
# {} #
############\n",
            datetime,
        )
    }

    fn create_log_file(mut self, file_name: &str) -> Result<Self, IoError> {
        let file_name = format!("{}.log", file_name);

        print_with_tag(Tag::Info, format!(r#"Create "{}" file in "{}""#, file_name, self.path_to_box().display()));

        self.log_file = Some(file_name);

        try!(self.write_log(self.start_mark_for_log()));

        Ok(self)
    }

    pub fn create_box_with_log(self, file_name: &str) -> Result<Self, IoError> {
        try!(self.create_box());
        self.create_log_file(file_name)
    }

    pub fn squeeze_dusts<P: AsRef<Path>>(&self, paths_to_dust: Vec<P>) -> Result<(), IoError> {
        let path_to_dust_box = self.path_to_dust_box();

        print_with_tag(Tag::Info, format!("Move dusts to \"{}\"", path_to_dust_box.display()));

        for path_to_dust in &paths_to_dust {
            let path_to_dust = path_to_dust.as_ref();
            let target_file  = match path_to_dust.file_name() {
                Some(f) => f,
                None    => unreachable!("Cannot get file name from path!!"),
            };
            let target_base = match path_to_dust.parent() {
                Some(b) => b,
                None    => unreachable!("Cannot get base name from path!!"),
            };

            let to = path_buf![&path_to_dust_box, target_base];

            let message = format!("  => \"{}\"", path_to_dust.display());
            print_with_tag(Tag::Info, message);

            try!(fs::create_dir_all(&to));

            // forcedly overwrite if the file exists with same name.
            match fs::rename(path_to_dust, path_buf![to, target_file]) {
                Ok(_)  => (),
                Err(e) => match e.kind() {
                    IoErrorKind::PermissionDenied => print_with_tag(Tag::Info, "     Interrupted for permission"),
                    _                             => return Err(From::from(e)),
                },
            };
        }

        Ok(())
    }

    pub fn squeeze_empty_dirs_only<P: AsRef<Path>>(&self, paths_to_dir: Vec<P>) -> Result<(), IoError> {
        let path_to_dust_box = self.path_to_dust_box();

        print_with_tag(Tag::Info, format!("Move empty dirs to \"{}\"", path_to_dust_box.display()));

        for path_to_dir in &paths_to_dir {
            if is_empty_dir(path_to_dir) {
                let path_to_dir = path_to_dir.as_ref();

                let message = format!("  => \"{}\"", path_to_dir.display());
                print_with_tag(Tag::Info, message);

                match fs::remove_dir(path_to_dir) {
                    Ok(_)  => try!(fs::create_dir_all(path_buf![&path_to_dust_box, path_to_dir])),
                    Err(e) => match e.kind() {
                        IoErrorKind::PermissionDenied => print_with_tag(Tag::Info, "     Interrupted for permission"),
                        _                             => return Err(From::from(e)),
                    },
                };
            }
        }

        Ok(())
    }

    pub fn delete_expired_boxes(&self, moratorium: Duration) -> Result<(), IoError> {
        let path_to_storage = Self::path();

        print_with_tag(Tag::Info, "Delete expired dusts");

        let target_boxes = try!(ls(&path_to_storage))
            .into_iter()
            .filter(|date| match Local.datetime_from_str(format!("{} 00:00:00", date).as_ref(), "%Y-%m-%d %H:%M:%S") {
                Ok(created_date) => created_date + moratorium < self.now,
                Err(_)           => false,
            })
            .map(|dir| path_to_storage.join(dir))
            .collect::<Vec<PathBuf>>();

        for target_box in &target_boxes {
            print_with_tag(Tag::Info, format!("  => \"{}\"", target_box.display()));
            try!(fs::remove_dir_all(target_box));
        };

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;

#[macro_use]
extern crate reql;
extern crate reql_types;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

use futures::stream::Stream;
use tokio_core::reactor::Core;
use reql::{Client, Document, Run};

/**
 * rethinkdb changelog example
 * After running, access your admin panel at localhost:8080
 *
 * setup a "test" database, with a "test" table, and run:
 *
 * // Insert an item
 * r.db('test').table('test').insert({ test: 1 });
 *
 * // Give the first item a random number
 * r.db('test').table('test').nth(0).update({ test: r.random(0, 100) }, { nonAtomic: true });
 *
 * // Remove the first item
 * r.db('test').table('test').nth(0).delete();
 *
 */

#[derive(Debug, Serialize, Deserialize)]
struct TestItem {
    test: i32,
    id: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Change {
    // Upon deserialisation:
    // We'll change the rethinkdb change "type" to avoid  naming issues
    #[serde(rename(deserialize = "type"))]
    action: String,
    // new_val or old_val can be null, so we should use Options
    new_val: Option<TestItem>,
    old_val: Option<TestItem>,
}

fn main()
{
    // Create a new ReQL client
    let r = Client::new();

    // Create an even loop
    let core = Core::new().unwrap();

    // Create a connection pool
    let conn = r.connect(&core.handle()).unwrap();

    // Run the query
    let query =
        r.db("test")
        .table("test")
        .filter(args!(|doc| {

            // Filter only documents which match our current TestItem trait
            doc.has_fields("test").and(doc.get_field("test").type_of().eq("NUMBER"))
        }))
        .changes()

    // We want rethinkdb to inform us of the change type
        .with_args(args!({
            include_types: true
        }))
        .run::<Change>(conn)
        .unwrap();

    // Process the results
    let stati = query.and_then(|change| {
        match change {
            // The server returned the response we were expecting,
            // and deserialized the data into our Change structure
            Some(Document::Expected(change)) => {
                // Extract the change type
                print!("{} action received\n\t=> ", change.action);

                // Match the change type
                match change.action.as_ref() {
                    "add" => println!("{:?}", change.new_val),
                    "remove" => println!("{:?}", change.old_val),
                    "change" => println!("from {:?} to {:?}", change.old_val, change.new_val),

                    _ => println!("{:?}", change)
                }
            }
            // We got a response alright, but it wasn't the one we were
            // expecting plus it's not an error either, otherwise it would
            // have been returned as such (This simply means that the response
            // we got couldn't be serialised into the type we were expecting)
            Some(Document::Unexpected(change)) => {
                println!("Got unexpected change: {}",change)
            }
            // This is impossible in this particular example since there
            // needs to be at least one server available to give this
            // response otherwise we would have run into an error for
            // failing to connect
            None => {
                println!("got no documents in the database");
            }
        }
        Ok(())
    })
    // Our query ran into an error
        .or_else(|error| {
            println!("{:?}", error);
            Err(())
        });

    // Wait for all the results to be processed
    for _ in stati.wait() {}
}
<commit_msg>Upadted example, removed custom Change<commit_after>extern crate futures;
extern crate tokio_core;

#[macro_use]
extern crate reql;
extern crate reql_types;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

use reql_types::Change;
use futures::stream::Stream;
use tokio_core::reactor::Core;
use reql::{Client, Document, Run};

/**
 * rethinkdb changelog example
 * After running, access your admin panel at localhost:8080
 *
 * setup a "test" database, with a "test" table, and run:
 *
 * // Insert an item
 * r.db('test').table('test').insert({ test: 1 });
 *
 * // Give the first item a random number
 * r.db('test').table('test').nth(0).update({ test: r.random(0, 100) }, { nonAtomic: true });
 *
 * // Remove the first item
 * r.db('test').table('test').nth(0).delete();
 */

/**
 * Or, in another rust context, you could run the following:
 *
 * // Insert an item
 * r.db("test").table("test").insert(json!({ test: 1 })) // => run & unwrap
 *
 * // Give the first item a random number
 * r.db("test").table("test").nth(0).update(args!(
 *     { test: r.random(0, 100) },
 *     { nonAtomic: true }
 * ))  // => run & unwrap
 *
 * // Remove the first item
 * r.db("test").table("test").nth(0).delete() // => run & unwrap
 *
 */

#[derive(Debug, Serialize, Deserialize)]
struct TestItem {
    test: i32,
    id: String,
}

fn main()
{
    // Create a new ReQL client
    let r = Client::new();

    // Create an even loop
    let core = Core::new().unwrap();

    // Create a connection pool
    let conn = r.connect(&core.handle()).unwrap();

    // Run the query
    let query =
        r.db("test")
        .table("test")
        .filter(args!(|doc| {

            // Filter only documents which match our current TestItem trait
            doc.has_fields("test").and(doc.get_field("test").type_of().eq("NUMBER"))
        }))
        .changes()

    // We want rethinkdb to inform us of the change type
        .with_args(args!({
            include_types: true
        }))
        .run::<Change<TestItem, TestItem>>(conn)
        .unwrap();

    // Process the results
    let stati = query.and_then(|change| {
        match change {
            // The server returned the response we were expecting,
            // and deserialized the data into our Change structure
            Some(Document::Expected(change)) => {

                // Match our result_type vs rethinkdb's possible types
                match &change.result_type {

                    // We've got a String change-type
                    &Some(ref action) => {

                        // Extract the change type
                        print!("{:?} action received\n\t=> ", action);

                        match action.as_ref() {
                            "add" => println!("{:?}", change.new_val),
                            "remove" => println!("{:?}", change.old_val),
                            "change" => println!("from {:?} to {:?}", change.old_val, change.new_val),

                            _ => println!("{:?}", change)
                        }
                    },
                    &None => println!("Invalid change type!")
                };
            }

            // We got a response alright, but it wasn't the one we were
            // expecting plus it's not an error either, otherwise it would
            // have been returned as such (This simply means that the response
            // we got couldn't be serialised into the type we were expecting)
            Some(Document::Unexpected(change)) => {
                println!("Got unexpected change: {}",change)
            }
            // This is impossible in this particular example since there
            // needs to be at least one server available to give this
            // response otherwise we would have run into an error for
            // failing to connect
            None => {
                println!("got no documents in the database");
            }
        }
        Ok(())
    })
    // Our query ran into an error
        .or_else(|error| {
            println!("{:?}", error);
            Err(())
        });

    // Wait for all the results to be processed
    for _ in stati.wait() {}
}
<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        BasicMacroExpander,
                        ExtCtxt,
                        MacResult,
                        MacPat,
                        MacItem,
                        DummyResult,
                        MacExpr,
                        NormalTT};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
    reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

/// A convenience type for macros that return a single item.
pub struct MacItems {
    items: Vec<Gc<ast::Item>>
}

impl MacItems {
    pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
        box MacItems { items: items } as Box<MacResult>
    }
}
impl MacResult for MacItems {
    fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
        Some(SmallVector::many(self.items.clone()))
    }
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name");
   let path   = entries.shift().expect("should be given a CSV file path");
   let labels = entries.shift().expect("should be given column labels");

   println!("provide_csv_given_labels: name:   {}", name.str);
   println!("provide_csv_given_labels: path:   {}", path.str);
   println!("provide_csv_given_labels: labels: {}", labels.str);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/* OLD
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   fn define_my_csv(cx0: &mut ExtCtxt) -> Option<Gc<syntax::ast::Item>> {
      let item1: Option<Gc<syntax::ast::Item>>  = quote_item!(cx0,
         pub struct MyCSV {
            pub data: Vec<(String)>,
         }
      );
      return item1;
   }

   let item1 = define_my_csv(cx);  // Why is this necessary?

   let item2: Option<Gc<syntax::ast::Item>>  = quote_item!(cx,
      impl MyCSV {
         pub fn new() -> MyCSV {  // BUG: note removing the return time leads to goofy error messsages
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["zero".to_string(), "one".to_string(), "two".to_string()]),
            };
         }
      }
   );

   //let mut items = Vec::new();
   //items.push(item1.expect("Should be able to construct MyCSV."));
   //items.push(item2.expect("Should be able to construct MyCSV."));
   let items = vec![item1.expect("Should be able to construct MyCSV."),
                    item2.expect("Should be able to construct MyCSV.")];
   return MacItems::new(items);
}

<commit_msg>cleanup<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        BasicMacroExpander,
                        ExtCtxt,
                        MacResult,
                        MacPat,
                        MacItem,
                        DummyResult,
                        MacExpr,
                        NormalTT};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
    reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

/// A convenience type for macros that return a single item.
pub struct MacItems {
    items: Vec<Gc<ast::Item>>
}

impl MacItems {
    pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
        box MacItems { items: items } as Box<MacResult>
    }
}
impl MacResult for MacItems {
    fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
        Some(SmallVector::many(self.items.clone()))
    }
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name");
   let path   = entries.shift().expect("should be given a CSV file path");
   let labels = entries.shift().expect("should be given column labels");

   println!("provide_csv_given_labels: name:   {}", name.str);
   println!("provide_csv_given_labels: path:   {}", path.str);
   println!("provide_csv_given_labels: labels: {}", labels.str);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/* OLD
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   fn define_my_csv(cx0: &mut ExtCtxt) -> Option<Gc<syntax::ast::Item>> {
      let item1: Option<Gc<syntax::ast::Item>>  = quote_item!(cx0,
         pub struct MyCSV {
            pub data: Vec<(String)>,
         }
      );
      return item1;
   }

   let item1 = define_my_csv(cx);  // Why is this necessary?

   let item2: Option<Gc<syntax::ast::Item>>  = quote_item!(cx,
      impl MyCSV {
         pub fn new() -> MyCSV {  // BUG: note removing the { leads to goofy error messsages
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["zero".to_string(), "one".to_string(), "two".to_string()]),
            };
         }
      }
   );

   let items = vec![item1.expect("Should be able to construct MyCSV."),
                    item2.expect("Should be able to construct MyCSV.")];
   return MacItems::new(items);
}

<|endoftext|>


<commit_before>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use build_helper::{output, tracked_env_var_os};

fn detect_llvm_link() -> (&'static str, &'static str) {
    // Force the link mode we want, preferring static by default, but
    // possibly overridden by `configure --enable-llvm-link-shared`.
    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
        ("dylib", "--link-shared")
    } else {
        ("static", "--link-static")
    }
}

fn main() {
    if tracked_env_var_os("RUST_CHECK").is_some() {
        // If we're just running `check`, there's no need for LLVM to be built.
        return;
    }

    build_helper::restore_library_path();

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config =
        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir
                    .parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return Some(to_test);
                }
            }
            None
        });

    if let Some(llvm_config) = &llvm_config {
        println!("cargo:rerun-if-changed={}", llvm_config.display());
    }
    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what its host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config because it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let mut optional_components = vec![
        "x86",
        "arm",
        "aarch64",
        "amdgpu",
        "avr",
        "mips",
        "powerpc",
        "systemz",
        "jsbackend",
        "webassembly",
        "msp430",
        "sparc",
        "nvptx",
        "hexagon",
    ];

    let mut version_cmd = Command::new(&llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        (major, minor)
    } else {
        (6, 0)
    };

    if major > 6 {
        optional_components.push("riscv");
    }

    let required_components = &[
        "ipo",
        "bitreader",
        "bitwriter",
        "linker",
        "asmparser",
        "lto",
        "coverage",
        "instrumentation",
    ];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    if major >= 9 {
        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = cc::Build::new();
    cfg.warnings(false);
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        if flag.starts_with("-flto") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        // Include path contains host directory, replace it with target
        if is_crossed && flag.starts_with("-I") {
            cfg.flag(&flag.replace(&host, &target));
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("LLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.define(&flag, None);
    }

    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
        cfg.define("LLVM_RUSTLLVM", None);
    }

    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
        cfg.define("NDEBUG", None);
        cfg.debug(false);
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
        .file("../rustllvm/RustWrapper.cpp")
        .file("../rustllvm/ArchiveWrapper.cpp")
        .file("../rustllvm/CoverageMappingWrapper.cpp")
        .file("../rustllvm/Linker.cpp")
        .cpp(true)
        .cpp_link_stdlib(None) // we handle this below
        .compile("rustllvm");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link();

    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--libs");

    if !is_crossed {
        cmd.arg("--system-libs");
    } else if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=shell32");
        println!("cargo:rustc-link-lib=uuid");
    } else if target.contains("netbsd") {
        println!("cargo:rustc-link-lib=z");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with('-') {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_end_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_end_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if is_crossed {
            if lib.starts_with("-LIBPATH:") {
                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // Some LLVM linker flags (-L and -l) may be needed even when linking
    // librustc_llvm, for example when using static libc++, we may need to
    // manually specify the library search path and -ldl -lpthread as link
    // dependencies.
    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
    if let Some(s) = llvm_linker_flags {
        for lib in s.into_string().unwrap().split_whitespace() {
            if lib.starts_with("-l") {
                println!("cargo:rustc-link-lib={}", &lib[2..]);
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", &lib[2..]);
            }
        }
    }

    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");

    let stdcppname = if target.contains("openbsd") {
        if target.contains("sparc64") { "estdc++" } else { "c++" }
    } else if target.contains("freebsd") {
        "c++"
    } else if target.contains("darwin") {
        "c++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else if llvm_use_libcxx.is_some() {
        "c++"
    } else {
        "stdc++"
    };

    // RISC-V requires libatomic for sub-word atomic operations
    if target.starts_with("riscv") {
        println!("cargo:rustc-link-lib=atomic");
    }

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
            if target.contains("windows") {
                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
            } else {
                println!("cargo:rustc-link-lib=static={}", stdcppname);
            }
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
    // since nothing else requires it.
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<commit_msg>Link to zlib while cross-compiling LLVM on Haiku<commit_after>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use build_helper::{output, tracked_env_var_os};

fn detect_llvm_link() -> (&'static str, &'static str) {
    // Force the link mode we want, preferring static by default, but
    // possibly overridden by `configure --enable-llvm-link-shared`.
    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
        ("dylib", "--link-shared")
    } else {
        ("static", "--link-static")
    }
}

fn main() {
    if tracked_env_var_os("RUST_CHECK").is_some() {
        // If we're just running `check`, there's no need for LLVM to be built.
        return;
    }

    build_helper::restore_library_path();

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config =
        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir
                    .parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return Some(to_test);
                }
            }
            None
        });

    if let Some(llvm_config) = &llvm_config {
        println!("cargo:rerun-if-changed={}", llvm_config.display());
    }
    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what its host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config because it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let mut optional_components = vec![
        "x86",
        "arm",
        "aarch64",
        "amdgpu",
        "avr",
        "mips",
        "powerpc",
        "systemz",
        "jsbackend",
        "webassembly",
        "msp430",
        "sparc",
        "nvptx",
        "hexagon",
    ];

    let mut version_cmd = Command::new(&llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        (major, minor)
    } else {
        (6, 0)
    };

    if major > 6 {
        optional_components.push("riscv");
    }

    let required_components = &[
        "ipo",
        "bitreader",
        "bitwriter",
        "linker",
        "asmparser",
        "lto",
        "coverage",
        "instrumentation",
    ];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    if major >= 9 {
        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = cc::Build::new();
    cfg.warnings(false);
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        if flag.starts_with("-flto") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        // Include path contains host directory, replace it with target
        if is_crossed && flag.starts_with("-I") {
            cfg.flag(&flag.replace(&host, &target));
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("LLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.define(&flag, None);
    }

    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
        cfg.define("LLVM_RUSTLLVM", None);
    }

    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
        cfg.define("NDEBUG", None);
        cfg.debug(false);
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
        .file("../rustllvm/RustWrapper.cpp")
        .file("../rustllvm/ArchiveWrapper.cpp")
        .file("../rustllvm/CoverageMappingWrapper.cpp")
        .file("../rustllvm/Linker.cpp")
        .cpp(true)
        .cpp_link_stdlib(None) // we handle this below
        .compile("rustllvm");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link();

    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--libs");

    if !is_crossed {
        cmd.arg("--system-libs");
    } else if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=shell32");
        println!("cargo:rustc-link-lib=uuid");
    } else if target.contains("netbsd") || target.contains("haiku") {
        println!("cargo:rustc-link-lib=z");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with('-') {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_end_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_end_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg(llvm_link_arg).arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if is_crossed {
            if lib.starts_with("-LIBPATH:") {
                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // Some LLVM linker flags (-L and -l) may be needed even when linking
    // librustc_llvm, for example when using static libc++, we may need to
    // manually specify the library search path and -ldl -lpthread as link
    // dependencies.
    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
    if let Some(s) = llvm_linker_flags {
        for lib in s.into_string().unwrap().split_whitespace() {
            if lib.starts_with("-l") {
                println!("cargo:rustc-link-lib={}", &lib[2..]);
            } else if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", &lib[2..]);
            }
        }
    }

    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");

    let stdcppname = if target.contains("openbsd") {
        if target.contains("sparc64") { "estdc++" } else { "c++" }
    } else if target.contains("freebsd") {
        "c++"
    } else if target.contains("darwin") {
        "c++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else if llvm_use_libcxx.is_some() {
        "c++"
    } else {
        "stdc++"
    };

    // RISC-V requires libatomic for sub-word atomic operations
    if target.starts_with("riscv") {
        println!("cargo:rustc-link-lib=atomic");
    }

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
            if target.contains("windows") {
                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
            } else {
                println!("cargo:rustc-link-lib=static={}", stdcppname);
            }
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
    // since nothing else requires it.
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=static-nobundle=pthread");
    }
}
<|endoftext|>