<commit_before>// Kevin Cantu
// Boyer-Moore string searching

use std;

// Function: findn_bytes
//
// Boyer-Moore string search, which returns
// up to `nn` byte positions of matched substrings
fn findn_bytes (needle: str, haystack: str, nn: uint) -> [uint] {

   let results = [];

   // empties
   if str::len(haystack) == 0u {
      ret results;
   }

   if str::len(needle) == 0u {
      vec::push(results, 0u);
      ret results;
   }

   if str::len(haystack) < str::len(needle) {
      ret results;
   }

   // generate the tables
   let ct = char_table(needle);
   let pt = prefix_table(needle);

   // simplify the table referencing
   let getShift = fn@(pos: uint) -> uint {
      let charShift, prefShift; // also: ct, pt, needle

      assert needle != "";
      assert pos >= 0u;
      assert pos < str::len(needle);

      alt ct.find(needle[pos] as uint) {
         option::none { charShift = str::len(needle);}
         option::some(x) { charShift = x;}
      }

      let offset = str::len(needle) - pos;

      assert offset >= 0u;
      assert offset < str::len(needle);

      alt pt.find(offset) {
         option::none { fail "something is out of range" }
         option::some(x) { prefShift = x;}
      }

      ret greaterOf(charShift, prefShift);
   };

   let hii = 0u;
   let njj = 0u;

   while hii < str::len(haystack) {

      njj = str::len(needle);

      // reverse through needle
      while 0u < njj {
         njj -= 1u;

         if njj+hii >= str::len(needle) {
            ret results;
         }

         // if still matching
         if needle[njj] == haystack[hii+njj] {

            // if full match
            if njj == 0u {
               vec::push(results, hii);
               log(warn, results);
               if vec::len(results) >= nn { ret results; }
            }

            hii += 1u;

         // if partial match
         } else {
            let shift = getShift(njj);

            hii += shift;
            njj = str::len(needle);
         }
      }
   }

   ret results;
}

fn char_table (needle: str) -> std::map::map<uint, uint> {
   let mm: std::map::map<uint, uint> = std::map::new_uint_hash();
   let len = str::len(needle);

   let jj = len - 1u; // drop the last byte

   assert jj >= 0u;
   assert jj < str::len(needle);

   // from last-1 to first
   while jj > 0u {
      jj -= 1u;
      if ! mm.contains_key(needle[jj] as uint) {
         // store reverse indexes
         mm.insert(needle[jj] as uint, len - 1u - jj);
      }
   }

   ret mm;
}

#[test]
fn test_char_table () {
   let ct = char_table("ANPANMAN");
   assert 1u == ct.get('A' as uint);
   assert 2u == ct.get('M' as uint);
   assert 3u == ct.get('N' as uint);
   assert 5u == ct.get('P' as uint);
   assert option::none == ct.find('z' as uint);
}

fn prefix_table (needle: str) -> std::map::map<uint, uint> {
   let mm: std::map::map<uint, uint> = std::map::new_uint_hash();

   let fill = fn@(kk: uint, vv: uint) {
      if ! mm.contains_key(kk) {
         mm.insert(kk, vv);
         std::io::println(#fmt("%u => %u", kk, vv));
      }
   };

   let lim   = str::len(needle);
   assert 0u < lim;

   // step to larger suffixes
   let sii = 0u;
   while sii < lim {
      //assert      lim-sii    < lim;
      //assert 0u <= lim-sii-1u;

      let suffix      = str::slice(needle, lim - sii,      lim);
      let suffix_plus = str::slice(needle, lim - sii - 1u, lim);
      let slen = str::len(suffix);

      // step to smaller prefixes
      let pii = lim - 1u;
      while pii > 0u {
         //assert       pii < lim;
         //assert 0u <= pii;

         let prefix = str::slice(needle, 0u, pii);
         let plen   = str::len(prefix);

         let msg = ("<"+suffix+"("+#fmt("%u",sii)+") × "+prefix+"("+#fmt("%u",pii)+")>");
         std::io::println(msg);

         // suffix might be fully matched
         if str::ends_with(prefix, suffix)
            && !str::ends_with(prefix, suffix_plus)
            &&  slen < plen
         {
            fill(sii, lim-pii);
         }

         // prefix is bigger than suffix, only tail can match
         if str::len(prefix) <= str::len(suffix)
            && str::ends_with(suffix, prefix)
         {
            fill(sii, lim-pii);
         }

         pii -= 1u;
      }

      // no matching prefix
      fill(sii, lim-pii);

      sii += 1u;
   }

   ret mm;
}

#[test]
fn test_prefix_table() {
   let pt = prefix_table("ANPANMAN");
                            //      ... 8

   assert 1u == pt.get(0u); //        (n)
   assert 8u == pt.get(1u); //       (a)n
   assert 3u == pt.get(2u); //      (m)an
   assert 6u == pt.get(3u); //     (n)man
   assert 6u == pt.get(4u); //    (a)nman
   assert 6u == pt.get(5u); //   (p)anman
   assert 6u == pt.get(6u); //  (n)panman
   assert 6u == pt.get(7u); // (a)npanman
   //assert 0u == pt.get(8u); // fail
}

////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////

// prefixes, smallest first
fn prefixes(ss: str) -> [str] unsafe {
   assert str::len(ss) > 0u;

   let vv = [];
   str::chars_iteri(ss) {|ii, _bb|
      vec::push(vv, str::slice(ss, 0u, ii));
   }
   vec::push(vv, ss);
   ret vv;
}

#[test]
fn test_prefs() {
   assert ["", "a", "ab", "abc", "abcd"] == prefixes("abcd");
}

// suffixes, largest first
fn suffixes(ss: str) -> [str] unsafe {
   assert str::len(ss) > 0u;

   let vv = [];
   let lim = str::len(ss);
   str::chars_iteri(ss) {|ii, _bb|
      vec::push(vv, str::slice(ss, ii, lim));
   }
   vec::push(vv, "");
   ret vv;
}

#[test]
fn test_sufs() {
   assert ["abcd", "bcd", "cd", "d", ""] == suffixes("abcd");
}

fn greaterOf(a: uint, b: uint) -> uint {
   if a > b { ret a; } else { ret b; }
}

#[test]
fn test_greaterOf() {
   assert greaterOf(15u, 17u) == 17u;
   assert greaterOf(17u, 15u) == 17u;
}

fn find_bytes_(needle: str, haystack: str) -> option<uint> {
   let found = findn_bytes(needle, haystack, 1u);

   alt vec::len(found) {
      0u { option::none }
      xx { option::some(found[0u]) }
   }
}

#[test]
fn test_findn() {
   assert [] == findn_bytes("banana", "apple pie", 1u);
}

#[test]
fn test_find_bytes_A() {
  // byte positions
  assert (find_bytes_("banana", "apple pie") == option::none);
}

#[test]
fn test_find_bytes_B() {
  assert (find_bytes_("", "") == option::none);
}

#[test]
fn test_find_bytes_C() {
  let data = "ประเทศไทย中华Việt Nam";
//  assert (find_bytes_("", data)     == option::some( 0u));
// PENDING
  assert (find_bytes_("ประเ", data) == option::some( 0u));
//  assert (find_bytes_("ะเ", data)   == option::some( 6u));
//  assert (find_bytes_("中华", data) == option::some(27u));
//  assert (find_bytes_("ไท华", data) == option::none);
}

<commit_msg>ok, now the tables work again...<commit_after>// Kevin Cantu
// Boyer-Moore string searching

use std;

// Function: findn_bytes
//
// Boyer-Moore string search, which returns
// up to `nn` byte positions of matched substrings
fn findn_bytes (needle: str, haystack: str, nn: uint) -> [uint] {

   let results = [];

   // empties
   if str::len(haystack) == 0u {
      ret results;
   }

   if str::len(needle) == 0u {
      vec::push(results, 0u);
      ret results;
   }

   if str::len(haystack) < str::len(needle) {
      ret results;
   }

   // generate the tables
   let ct = char_table(needle);
   let pt = prefix_table(needle);

   // simplify the table referencing
   let getShift = fn@(pos: uint) -> uint {
      let charShift, prefShift; // also: ct, pt, needle

      assert needle != "";
      assert pos >= 0u;
      assert pos < str::len(needle);

      alt ct.find(needle[pos] as uint) {
         option::none { charShift = str::len(needle);}
         option::some(x) { charShift = x;}
      }

      let offset = str::len(needle) - pos;

      assert offset >= 0u;
      assert offset < str::len(needle);

      alt pt.find(offset) {
         option::none { fail "something is out of range" }
         option::some(x) { prefShift = x;}
      }

      ret greaterOf(charShift, prefShift);
   };

   let hii = 0u;
   let njj = 0u;

   while hii < str::len(haystack) {

      njj = str::len(needle);

      // reverse through needle
      while 0u < njj {
         njj -= 1u;

         if njj+hii >= str::len(needle) {
            ret results;
         }

         // if still matching
         if needle[njj] == haystack[hii+njj] {

            // if full match
            if njj == 0u {
               vec::push(results, hii);
               log(warn, results);
               if vec::len(results) >= nn { ret results; }
            }

            hii += 1u;

         // if partial match
         } else {
            let shift = getShift(njj);

            hii += shift;
            njj = str::len(needle);
         }
      }
   }

   ret results;
}

fn char_table (needle: str) -> std::map::map<uint, uint> {
   let mm: std::map::map<uint, uint> = std::map::new_uint_hash();
   let len = str::len(needle);

   let jj = len - 1u; // drop the last byte

   assert jj >= 0u;
   assert jj < str::len(needle);

   // from last-1 to first
   while jj > 0u {
      jj -= 1u;
      if ! mm.contains_key(needle[jj] as uint) {
         // store reverse indexes
         mm.insert(needle[jj] as uint, len - 1u - jj);
      }
   }

   ret mm;
}

#[test]
fn test_char_table () {
   let ct = char_table("ANPANMAN");
   assert 1u == ct.get('A' as uint);
   assert 2u == ct.get('M' as uint);
   assert 3u == ct.get('N' as uint);
   assert 5u == ct.get('P' as uint);
   assert option::none == ct.find('z' as uint);
}

fn prefix_table (needle: str) -> std::map::map<uint, uint> {
   let mm: std::map::map<uint, uint> = std::map::new_uint_hash();

   // WAIT, HOW IS THIS ALLOWED TO MUTATE mm?
   let fill = fn@(kk: uint, vv: uint) {
      if ! mm.contains_key(kk) {
         mm.insert(kk, vv);
         //std::io::println(#fmt("%u => %u", kk, vv));
      }
   };

   let lim   = str::len(needle);
   assert 0u < lim;

   // step to larger suffixes
   let sii = 0u;
   while sii < lim {

      // tail of the needle we seek
      let suffix      = str::slice(needle, lim - sii,      lim);
      let suffix_plus = str::slice(needle, lim - sii - 1u, lim);
      let slen = str::len(suffix);

      // step to smaller prefixes
      let pii = lim - 1u;
      while pii > 0u {

         // a prefix of the needle that might be matched by
         // a partial match of a suffix
         // (which we might want to jump to)
         let prefix = str::slice(needle, 0u, pii);

         //let msg  = "<"+suffix+"("+#fmt("%u",sii)+")";
         //let msg2 = prefix+"("+#fmt("%u",pii)+")>";
         //std::io::println(msg + " × " + msg2);

         // suffix might be fully matched
         let is_suffix_matched =
            str::ends_with(prefix, suffix)
            && !str::ends_with(prefix, suffix_plus)
            &&  slen < str::len(prefix);

         // prefix is bigger than suffix, only tail can match
         let is_suffix_partially_matched =
            str::len(prefix) <= str::len(suffix)
            && str::ends_with(suffix, prefix);

         if is_suffix_matched || is_suffix_partially_matched {
            fill(sii, lim-pii);
         }

         pii -= 1u;
      }

      // no matching prefix
      fill(sii, lim-pii);

      sii += 1u;
   }

   ret mm;
}

#[test]
fn test_prefix_table() {
   let pt = prefix_table("ANPANMAN");
                            //      ... 8

   assert 1u == pt.get(0u); //        (n)
   assert 8u == pt.get(1u); //       (a)n
   assert 3u == pt.get(2u); //      (m)an
   assert 6u == pt.get(3u); //     (n)man
   assert 6u == pt.get(4u); //    (a)nman
   assert 6u == pt.get(5u); //   (p)anman
   assert 6u == pt.get(6u); //  (n)panman
   assert 6u == pt.get(7u); // (a)npanman
   //assert 0u == pt.get(8u); // fail
}

////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////

// prefixes, smallest first
fn prefixes(ss: str) -> [str] unsafe {
   assert str::len(ss) > 0u;

   let vv = [];
   str::chars_iteri(ss) {|ii, _bb|
      vec::push(vv, str::slice(ss, 0u, ii));
   }
   vec::push(vv, ss);
   ret vv;
}

#[test]
fn test_prefs() {
   assert ["", "a", "ab", "abc", "abcd"] == prefixes("abcd");
}

// suffixes, largest first
fn suffixes(ss: str) -> [str] unsafe {
   assert str::len(ss) > 0u;

   let vv = [];
   let lim = str::len(ss);
   str::chars_iteri(ss) {|ii, _bb|
      vec::push(vv, str::slice(ss, ii, lim));
   }
   vec::push(vv, "");
   ret vv;
}

#[test]
fn test_sufs() {
   assert ["abcd", "bcd", "cd", "d", ""] == suffixes("abcd");
}

fn greaterOf(a: uint, b: uint) -> uint {
   if a > b { ret a; } else { ret b; }
}

#[test]
fn test_greaterOf() {
   assert greaterOf(15u, 17u) == 17u;
   assert greaterOf(17u, 15u) == 17u;
}

fn find_bytes_(needle: str, haystack: str) -> option<uint> {
   let found = findn_bytes(needle, haystack, 1u);

   alt vec::len(found) {
      0u { option::none }
      xx { option::some(found[0u]) }
   }
}

#[test]
fn test_findn() {
   assert [] == findn_bytes("banana", "apple pie", 1u);
}

#[test]
fn test_find_bytes_A() {
  // byte positions
  assert (find_bytes_("banana", "apple pie") == option::none);
}

#[test]
fn test_find_bytes_B() {
  assert (find_bytes_("", "") == option::none);
}

#[test]
fn test_find_bytes_C() {
  let data = "ประเทศไทย中华Việt Nam";
//  assert (find_bytes_("", data)     == option::some( 0u));
// PENDING
  assert (find_bytes_("ประเ", data) == option::some( 0u));
//  assert (find_bytes_("ะเ", data)   == option::some( 6u));
//  assert (find_bytes_("中华", data) == option::some(27u));
//  assert (find_bytes_("ไท华", data) == option::none);
}

<|endoftext|>


<commit_before>// ToDO: add tests
<commit_msg>tests/tee: implemented tests for tee (#1804)<commit_after>use crate::common::util::*;

// tests for basic tee functionality.
// inspired by:
// https://github.com/coreutils/coreutils/tests/misc/tee.sh

#[test]
fn test_tee_processing_multiple_operands() {
    // POSIX says: "Processing of at least 13 file operands shall be supported."

    let content = "tee_sample_content";
    for &n in [1, 2, 12, 13].iter() {
        let files = (1..=n).map(|x| x.to_string()).collect::<Vec<_>>();
        let (at, mut ucmd) = at_and_ucmd!();

        ucmd.args(&files)
            .pipe_in(content)
            .succeeds()
            .stdout_is(content);

        for file in files.iter() {
            assert!(at.file_exists(file));
            assert_eq!(at.read(file), content);
        }
    }
}

#[test]
fn test_tee_treat_minus_as_filename() {
    // Ensure tee treats '-' as the name of a file, as mandated by POSIX.

    let (at, mut ucmd) = at_and_ucmd!();
    let content = "tee_sample_content";
    let file = "-";

    ucmd.arg("-").pipe_in(content).succeeds().stdout_is(content);

    assert!(at.file_exists(file));
    assert_eq!(at.read(file), content);
}

#[test]
fn test_tee_append() {
    let (at, mut ucmd) = at_and_ucmd!();
    let content = "tee_sample_content";
    let file = "tee_out";

    at.touch(file);
    at.write(file, content);
    assert_eq!(at.read(file), content);

    ucmd.arg("-a")
        .arg(file)
        .pipe_in(content)
        .succeeds()
        .stdout_is(content);
    assert!(at.file_exists(file));
    assert_eq!(at.read(file), content.repeat(2));
}

#[test]
#[cfg(target_os = "linux")]
fn test_tee_no_more_writeable_stdout() {
    let (_at, mut ucmd) = at_and_ucmd!();
    let content = (1..=10)
        .map(|x| format!("{}\n", x.to_string()))
        .collect::<String>();
    let file_out = "tee_file_out";

    let _result = ucmd
        .arg("/dev/full")
        .arg(file_out)
        .pipe_in(&content[..])
        .fails();

    // TODO: comment in after https://github.com/uutils/coreutils/issues/1805 is fixed
    // assert_eq!(at.read(file_out), content);
    // assert!(result.stdout.contains(&content));
    // assert!(result.stderr.contains("No space left on device"));
}

#[test]
#[cfg(target_os = "linux")]
fn test_tee_no_more_writeable_stdin() {
    let (_at, mut ucmd) = at_and_ucmd!();
    let _content = (1..=10)
        .map(|x| format!("{}\n", x.to_string()))
        .collect::<String>();
    let file_out_a = "tee_file_out_a";
    let file_out_b = "tee_file_out_b";

    let _result = ucmd
        .arg(file_out_a)
        .arg(file_out_b)
        .pipe_in("/dev/full")
        .succeeds(); // TODO: expected to succeed currently; change to fails() when required

    // TODO: comment in after https://github.com/uutils/coreutils/issues/1805 is fixed
    // assert_eq!(at.read(file_out_a), content);
    // assert_eq!(at.read(file_out_b), content);
    // assert!(result.stderr.contains("No space left on device"));
}
<|endoftext|>


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

use self::SmallVectorRepr::*;
use self::IntoIterRepr::*;

use core::ops;
use std::iter::{IntoIterator, FromIterator};
use std::mem;
use std::slice;
use std::vec;

use util::move_map::MoveMap;

/// A vector type optimized for cases where the size is almost always 0 or 1
#[derive(Clone)]
pub struct SmallVector<T> {
    repr: SmallVectorRepr<T>,
}

#[derive(Clone)]
enum SmallVectorRepr<T> {
    Zero,
    One(T),
    Many(Vec<T>),
}

impl<T> Default for SmallVector<T> {
    fn default() -> Self {
        SmallVector { repr: Zero }
    }
}

impl<T> Into<Vec<T>> for SmallVector<T> {
    fn into(self) -> Vec<T> {
        match self.repr {
            Zero => Vec::new(),
            One(t) => vec![t],
            Many(vec) => vec,
        }
    }
}

impl<T> FromIterator<T> for SmallVector<T> {
    fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> SmallVector<T> {
        let mut v = SmallVector::zero();
        v.extend(iter);
        v
    }
}

impl<T> Extend<T> for SmallVector<T> {
    fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
        for val in iter {
            self.push(val);
        }
    }
}

impl<T> SmallVector<T> {
    pub fn zero() -> SmallVector<T> {
        SmallVector { repr: Zero }
    }

    pub fn one(v: T) -> SmallVector<T> {
        SmallVector { repr: One(v) }
    }

    pub fn many(vs: Vec<T>) -> SmallVector<T> {
        SmallVector { repr: Many(vs) }
    }

    pub fn as_slice(&self) -> &[T] {
        self
    }

    pub fn as_mut_slice(&mut self) -> &mut [T] {
        self
    }

    pub fn pop(&mut self) -> Option<T> {
        match self.repr {
            Zero => None,
            One(..) => {
                let one = mem::replace(&mut self.repr, Zero);
                match one {
                    One(v1) => Some(v1),
                    _ => unreachable!()
                }
            }
            Many(ref mut vs) => vs.pop(),
        }
    }

    pub fn push(&mut self, v: T) {
        match self.repr {
            Zero => self.repr = One(v),
            One(..) => {
                let one = mem::replace(&mut self.repr, Zero);
                match one {
                    One(v1) => mem::replace(&mut self.repr, Many(vec![v1, v])),
                    _ => unreachable!()
                };
            }
            Many(ref mut vs) => vs.push(v)
        }
    }

    pub fn push_all(&mut self, other: SmallVector<T>) {
        for v in other.into_iter() {
            self.push(v);
        }
    }

    pub fn get(&self, idx: usize) -> &T {
        match self.repr {
            One(ref v) if idx == 0 => v,
            Many(ref vs) => &vs[idx],
            _ => panic!("out of bounds access")
        }
    }

    pub fn expect_one(self, err: &'static str) -> T {
        match self.repr {
            One(v) => v,
            Many(v) => {
                if v.len() == 1 {
                    v.into_iter().next().unwrap()
                } else {
                    panic!(err)
                }
            }
            _ => panic!(err)
        }
    }

    pub fn len(&self) -> usize {
        match self.repr {
            Zero => 0,
            One(..) => 1,
            Many(ref vals) => vals.len()
        }
    }

    pub fn is_empty(&self) -> bool { self.len() == 0 }

    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> SmallVector<U> {
        let repr = match self.repr {
            Zero => Zero,
            One(t) => One(f(t)),
            Many(vec) => Many(vec.into_iter().map(f).collect()),
        };
        SmallVector { repr: repr }
    }
}

impl<T> ops::Deref for SmallVector<T> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        match self.repr {
            Zero => {
                let result: &[T] = &[];
                result
            }
            One(ref v) => {
                unsafe { slice::from_raw_parts(v, 1) }
            }
            Many(ref vs) => vs
        }
    }
}

impl<T> ops::DerefMut for SmallVector<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        match self.repr {
            Zero => {
                let result: &mut [T] = &mut [];
                result
            }
            One(ref mut v) => {
                unsafe { slice::from_raw_parts_mut(v, 1) }
            }
            Many(ref mut vs) => vs
        }
    }
}

impl<T> IntoIterator for SmallVector<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> Self::IntoIter {
        let repr = match self.repr {
            Zero => ZeroIterator,
            One(v) => OneIterator(v),
            Many(vs) => ManyIterator(vs.into_iter())
        };
        IntoIter { repr: repr }
    }
}

pub struct IntoIter<T> {
    repr: IntoIterRepr<T>,
}

enum IntoIterRepr<T> {
    ZeroIterator,
    OneIterator(T),
    ManyIterator(vec::IntoIter<T>),
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        match self.repr {
            ZeroIterator => None,
            OneIterator(..) => {
                let mut replacement = ZeroIterator;
                mem::swap(&mut self.repr, &mut replacement);
                match replacement {
                    OneIterator(v) => Some(v),
                    _ => unreachable!()
                }
            }
            ManyIterator(ref mut inner) => inner.next()
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.repr {
            ZeroIterator => (0, Some(0)),
            OneIterator(..) => (1, Some(1)),
            ManyIterator(ref inner) => inner.size_hint()
        }
    }
}

impl<T> MoveMap<T> for SmallVector<T> {
    fn move_flat_map<F, I>(self, mut f: F) -> Self
        where F: FnMut(T) -> I,
              I: IntoIterator<Item=T>
    {
        match self.repr {
            Zero => Self::zero(),
            One(v) => f(v).into_iter().collect(),
            Many(vs) => SmallVector { repr: Many(vs.move_flat_map(f)) },
        }
    }
}

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

    #[test]
    fn test_len() {
        let v: SmallVector<isize> = SmallVector::zero();
        assert_eq!(0, v.len());

        assert_eq!(1, SmallVector::one(1).len());
        assert_eq!(5, SmallVector::many(vec![1, 2, 3, 4, 5]).len());
    }

    #[test]
    fn test_push_get() {
        let mut v = SmallVector::zero();
        v.push(1);
        assert_eq!(1, v.len());
        assert_eq!(&1, v.get(0));
        v.push(2);
        assert_eq!(2, v.len());
        assert_eq!(&2, v.get(1));
        v.push(3);
        assert_eq!(3, v.len());
        assert_eq!(&3, v.get(2));
    }

    #[test]
    fn test_from_iter() {
        let v: SmallVector<isize> = (vec![1, 2, 3]).into_iter().collect();
        assert_eq!(3, v.len());
        assert_eq!(&1, v.get(0));
        assert_eq!(&2, v.get(1));
        assert_eq!(&3, v.get(2));
    }

    #[test]
    fn test_move_iter() {
        let v = SmallVector::zero();
        let v: Vec<isize> = v.into_iter().collect();
        assert_eq!(v, Vec::new());

        let v = SmallVector::one(1);
        assert_eq!(v.into_iter().collect::<Vec<_>>(), [1]);

        let v = SmallVector::many(vec![1, 2, 3]);
        assert_eq!(v.into_iter().collect::<Vec<_>>(), [1, 2, 3]);
    }

    #[test]
    #[should_panic]
    fn test_expect_one_zero() {
        let _: isize = SmallVector::zero().expect_one("");
    }

    #[test]
    #[should_panic]
    fn test_expect_one_many() {
        SmallVector::many(vec![1, 2]).expect_one("");
    }

    #[test]
    fn test_expect_one_one() {
        assert_eq!(1, SmallVector::one(1).expect_one(""));
        assert_eq!(1, SmallVector::many(vec![1]).expect_one(""));
    }
}
<commit_msg>No need for core<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use self::SmallVectorRepr::*;
use self::IntoIterRepr::*;

use std::ops;
use std::iter::{IntoIterator, FromIterator};
use std::mem;
use std::slice;
use std::vec;

use util::move_map::MoveMap;

/// A vector type optimized for cases where the size is almost always 0 or 1
#[derive(Clone)]
pub struct SmallVector<T> {
    repr: SmallVectorRepr<T>,
}

#[derive(Clone)]
enum SmallVectorRepr<T> {
    Zero,
    One(T),
    Many(Vec<T>),
}

impl<T> Default for SmallVector<T> {
    fn default() -> Self {
        SmallVector { repr: Zero }
    }
}

impl<T> Into<Vec<T>> for SmallVector<T> {
    fn into(self) -> Vec<T> {
        match self.repr {
            Zero => Vec::new(),
            One(t) => vec![t],
            Many(vec) => vec,
        }
    }
}

impl<T> FromIterator<T> for SmallVector<T> {
    fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> SmallVector<T> {
        let mut v = SmallVector::zero();
        v.extend(iter);
        v
    }
}

impl<T> Extend<T> for SmallVector<T> {
    fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
        for val in iter {
            self.push(val);
        }
    }
}

impl<T> SmallVector<T> {
    pub fn zero() -> SmallVector<T> {
        SmallVector { repr: Zero }
    }

    pub fn one(v: T) -> SmallVector<T> {
        SmallVector { repr: One(v) }
    }

    pub fn many(vs: Vec<T>) -> SmallVector<T> {
        SmallVector { repr: Many(vs) }
    }

    pub fn as_slice(&self) -> &[T] {
        self
    }

    pub fn as_mut_slice(&mut self) -> &mut [T] {
        self
    }

    pub fn pop(&mut self) -> Option<T> {
        match self.repr {
            Zero => None,
            One(..) => {
                let one = mem::replace(&mut self.repr, Zero);
                match one {
                    One(v1) => Some(v1),
                    _ => unreachable!()
                }
            }
            Many(ref mut vs) => vs.pop(),
        }
    }

    pub fn push(&mut self, v: T) {
        match self.repr {
            Zero => self.repr = One(v),
            One(..) => {
                let one = mem::replace(&mut self.repr, Zero);
                match one {
                    One(v1) => mem::replace(&mut self.repr, Many(vec![v1, v])),
                    _ => unreachable!()
                };
            }
            Many(ref mut vs) => vs.push(v)
        }
    }

    pub fn push_all(&mut self, other: SmallVector<T>) {
        for v in other.into_iter() {
            self.push(v);
        }
    }

    pub fn get(&self, idx: usize) -> &T {
        match self.repr {
            One(ref v) if idx == 0 => v,
            Many(ref vs) => &vs[idx],
            _ => panic!("out of bounds access")
        }
    }

    pub fn expect_one(self, err: &'static str) -> T {
        match self.repr {
            One(v) => v,
            Many(v) => {
                if v.len() == 1 {
                    v.into_iter().next().unwrap()
                } else {
                    panic!(err)
                }
            }
            _ => panic!(err)
        }
    }

    pub fn len(&self) -> usize {
        match self.repr {
            Zero => 0,
            One(..) => 1,
            Many(ref vals) => vals.len()
        }
    }

    pub fn is_empty(&self) -> bool { self.len() == 0 }

    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> SmallVector<U> {
        let repr = match self.repr {
            Zero => Zero,
            One(t) => One(f(t)),
            Many(vec) => Many(vec.into_iter().map(f).collect()),
        };
        SmallVector { repr: repr }
    }
}

impl<T> ops::Deref for SmallVector<T> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        match self.repr {
            Zero => {
                let result: &[T] = &[];
                result
            }
            One(ref v) => {
                unsafe { slice::from_raw_parts(v, 1) }
            }
            Many(ref vs) => vs
        }
    }
}

impl<T> ops::DerefMut for SmallVector<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        match self.repr {
            Zero => {
                let result: &mut [T] = &mut [];
                result
            }
            One(ref mut v) => {
                unsafe { slice::from_raw_parts_mut(v, 1) }
            }
            Many(ref mut vs) => vs
        }
    }
}

impl<T> IntoIterator for SmallVector<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> Self::IntoIter {
        let repr = match self.repr {
            Zero => ZeroIterator,
            One(v) => OneIterator(v),
            Many(vs) => ManyIterator(vs.into_iter())
        };
        IntoIter { repr: repr }
    }
}

pub struct IntoIter<T> {
    repr: IntoIterRepr<T>,
}

enum IntoIterRepr<T> {
    ZeroIterator,
    OneIterator(T),
    ManyIterator(vec::IntoIter<T>),
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        match self.repr {
            ZeroIterator => None,
            OneIterator(..) => {
                let mut replacement = ZeroIterator;
                mem::swap(&mut self.repr, &mut replacement);
                match replacement {
                    OneIterator(v) => Some(v),
                    _ => unreachable!()
                }
            }
            ManyIterator(ref mut inner) => inner.next()
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.repr {
            ZeroIterator => (0, Some(0)),
            OneIterator(..) => (1, Some(1)),
            ManyIterator(ref inner) => inner.size_hint()
        }
    }
}

impl<T> MoveMap<T> for SmallVector<T> {
    fn move_flat_map<F, I>(self, mut f: F) -> Self
        where F: FnMut(T) -> I,
              I: IntoIterator<Item=T>
    {
        match self.repr {
            Zero => Self::zero(),
            One(v) => f(v).into_iter().collect(),
            Many(vs) => SmallVector { repr: Many(vs.move_flat_map(f)) },
        }
    }
}

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

    #[test]
    fn test_len() {
        let v: SmallVector<isize> = SmallVector::zero();
        assert_eq!(0, v.len());

        assert_eq!(1, SmallVector::one(1).len());
        assert_eq!(5, SmallVector::many(vec![1, 2, 3, 4, 5]).len());
    }

    #[test]
    fn test_push_get() {
        let mut v = SmallVector::zero();
        v.push(1);
        assert_eq!(1, v.len());
        assert_eq!(&1, v.get(0));
        v.push(2);
        assert_eq!(2, v.len());
        assert_eq!(&2, v.get(1));
        v.push(3);
        assert_eq!(3, v.len());
        assert_eq!(&3, v.get(2));
    }

    #[test]
    fn test_from_iter() {
        let v: SmallVector<isize> = (vec![1, 2, 3]).into_iter().collect();
        assert_eq!(3, v.len());
        assert_eq!(&1, v.get(0));
        assert_eq!(&2, v.get(1));
        assert_eq!(&3, v.get(2));
    }

    #[test]
    fn test_move_iter() {
        let v = SmallVector::zero();
        let v: Vec<isize> = v.into_iter().collect();
        assert_eq!(v, Vec::new());

        let v = SmallVector::one(1);
        assert_eq!(v.into_iter().collect::<Vec<_>>(), [1]);

        let v = SmallVector::many(vec![1, 2, 3]);
        assert_eq!(v.into_iter().collect::<Vec<_>>(), [1, 2, 3]);
    }

    #[test]
    #[should_panic]
    fn test_expect_one_zero() {
        let _: isize = SmallVector::zero().expect_one("");
    }

    #[test]
    #[should_panic]
    fn test_expect_one_many() {
        SmallVector::many(vec![1, 2]).expect_one("");
    }

    #[test]
    fn test_expect_one_one() {
        assert_eq!(1, SmallVector::one(1).expect_one(""));
        assert_eq!(1, SmallVector::many(vec![1]).expect_one(""));
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate loopdev;
extern crate tempfile;
#[macro_use]
extern crate lazy_static;
extern crate serde;
extern crate serde_json;

use loopdev::{LoopControl, LoopDevice};
use std::path::PathBuf;

mod util;
use util::{attach_file, create_backing_file, detach_all, list_device, setup};

#[test]
fn get_next_free_device() {
    let num_devices_at_start = list_device(None).len();
    let _lock = setup();

    let lc = LoopControl::open().expect("should be able to open the LoopControl device");
    let ld0 = lc
        .next_free()
        .expect("should not error finding the next free loopback device");

    assert_eq!(
        ld0.path(),
        Some(PathBuf::from(&format!(
            "/dev/loop{}",
            num_devices_at_start
        ))),
        "should find the first loopback device"
    );
}

#[test]
fn attach_a_backing_file_default() {
    attach_a_backing_file(0, 0, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_offset() {
    attach_a_backing_file(128 * 1024, 0, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_sizelimit() {
    attach_a_backing_file(0, 128 * 1024, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_offset_sizelimit() {
    attach_a_backing_file(128 * 1024, 128 * 1024, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn attach_a_backing_file_with_offset_overflow() {
    attach_a_backing_file(128 * 1024 * 1024 * 2, 0, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn attach_a_backing_file_with_sizelimit_overflow() {
    attach_a_backing_file(0, 128 * 1024 * 1024 * 2, 128 * 1024 * 1024);
}

fn attach_a_backing_file(offset: u64, sizelimit: u64, file_size: i64) {
    let _lock = setup();

    let (devices, ld0_path, file_path) = {
        let lc = LoopControl::open().expect("should be able to open the LoopControl device");

        let file = create_backing_file(file_size);
        let file_path = file.to_path_buf();
        let ld0 = lc
            .next_free()
            .expect("should not error finding the next free loopback device");

        ld0.with()
            .offset(offset)
            .size_limit(sizelimit)
            .attach(&file)
            .expect("should not error attaching the backing file to the loopdev");

        let devices = list_device(Some(ld0.path().unwrap().to_str().unwrap()));
        file.close().expect("should delete the temp backing file");

        (devices, ld0.path().unwrap(), file_path)
    };

    assert_eq!(
        devices.len(),
        1,
        "there should be only one loopback mounted device"
    );
    assert_eq!(
        devices[0].name.as_str(),
        ld0_path.to_str().unwrap(),
        "the attached devices name should match the input name"
    );
    assert_eq!(
        devices[0].back_file.clone().unwrap().as_str(),
        file_path.to_str().unwrap(),
        "the backing file should match the given file"
    );
    assert_eq!(
        devices[0].offset,
        Some(offset),
        "the offset should match the requested offset"
    );
    assert_eq!(
        devices[0].size_limit,
        Some(sizelimit),
        "the sizelimit should match the requested sizelimit"
    );

    detach_all();
}

#[test]
fn detach_a_backing_file_default() {
    detach_a_backing_file(0, 0, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_offset() {
    detach_a_backing_file(128 * 1024, 0, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_sizelimit() {
    detach_a_backing_file(0, 128 * 1024, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_offset_sizelimit() {
    detach_a_backing_file(128 * 1024, 128 * 1024, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn detach_a_backing_file_with_offset_overflow() {
    detach_a_backing_file(128 * 1024 * 1024 * 2, 0, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn detach_a_backing_file_with_sizelimit_overflow() {
    detach_a_backing_file(0, 128 * 1024 * 1024 * 2, 128 * 1024 * 1024);
}

fn detach_a_backing_file(offset: u64, sizelimit: u64, file_size: i64) {
    let num_devices_at_start = list_device(None).len();
    let _lock = setup();

    {
        let file = create_backing_file(file_size);
        attach_file(
            "/dev/loop3",
            file.to_path_buf().to_str().unwrap(),
            offset,
            sizelimit,
        );

        let ld0 = LoopDevice::open("/dev/loop3")
            .expect("should be able to open the created loopback device");

        ld0.detach()
            .expect("should not error detaching the backing file from the loopdev");

        file.close().expect("should delete the temp backing file");
    };

    std::thread::sleep(std::time::Duration::from_millis(100));

    assert_eq!(
        list_device(None).len(),
        num_devices_at_start,
        "there should be no loopback devices mounted"
    );
    detach_all();
}
<commit_msg>Remove an unused crate (#48)<commit_after>extern crate libc;
extern crate loopdev;
extern crate tempfile;
#[macro_use]
extern crate lazy_static;
extern crate serde;

use loopdev::{LoopControl, LoopDevice};
use std::path::PathBuf;

mod util;
use util::{attach_file, create_backing_file, detach_all, list_device, setup};

#[test]
fn get_next_free_device() {
    let num_devices_at_start = list_device(None).len();
    let _lock = setup();

    let lc = LoopControl::open().expect("should be able to open the LoopControl device");
    let ld0 = lc
        .next_free()
        .expect("should not error finding the next free loopback device");

    assert_eq!(
        ld0.path(),
        Some(PathBuf::from(&format!(
            "/dev/loop{}",
            num_devices_at_start
        ))),
        "should find the first loopback device"
    );
}

#[test]
fn attach_a_backing_file_default() {
    attach_a_backing_file(0, 0, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_offset() {
    attach_a_backing_file(128 * 1024, 0, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_sizelimit() {
    attach_a_backing_file(0, 128 * 1024, 128 * 1024 * 1024);
}

#[test]
fn attach_a_backing_file_with_offset_sizelimit() {
    attach_a_backing_file(128 * 1024, 128 * 1024, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn attach_a_backing_file_with_offset_overflow() {
    attach_a_backing_file(128 * 1024 * 1024 * 2, 0, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn attach_a_backing_file_with_sizelimit_overflow() {
    attach_a_backing_file(0, 128 * 1024 * 1024 * 2, 128 * 1024 * 1024);
}

fn attach_a_backing_file(offset: u64, sizelimit: u64, file_size: i64) {
    let _lock = setup();

    let (devices, ld0_path, file_path) = {
        let lc = LoopControl::open().expect("should be able to open the LoopControl device");

        let file = create_backing_file(file_size);
        let file_path = file.to_path_buf();
        let ld0 = lc
            .next_free()
            .expect("should not error finding the next free loopback device");

        ld0.with()
            .offset(offset)
            .size_limit(sizelimit)
            .attach(&file)
            .expect("should not error attaching the backing file to the loopdev");

        let devices = list_device(Some(ld0.path().unwrap().to_str().unwrap()));
        file.close().expect("should delete the temp backing file");

        (devices, ld0.path().unwrap(), file_path)
    };

    assert_eq!(
        devices.len(),
        1,
        "there should be only one loopback mounted device"
    );
    assert_eq!(
        devices[0].name.as_str(),
        ld0_path.to_str().unwrap(),
        "the attached devices name should match the input name"
    );
    assert_eq!(
        devices[0].back_file.clone().unwrap().as_str(),
        file_path.to_str().unwrap(),
        "the backing file should match the given file"
    );
    assert_eq!(
        devices[0].offset,
        Some(offset),
        "the offset should match the requested offset"
    );
    assert_eq!(
        devices[0].size_limit,
        Some(sizelimit),
        "the sizelimit should match the requested sizelimit"
    );

    detach_all();
}

#[test]
fn detach_a_backing_file_default() {
    detach_a_backing_file(0, 0, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_offset() {
    detach_a_backing_file(128 * 1024, 0, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_sizelimit() {
    detach_a_backing_file(0, 128 * 1024, 128 * 1024 * 1024);
}

#[test]
fn detach_a_backing_file_with_offset_sizelimit() {
    detach_a_backing_file(128 * 1024, 128 * 1024, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn detach_a_backing_file_with_offset_overflow() {
    detach_a_backing_file(128 * 1024 * 1024 * 2, 0, 128 * 1024 * 1024);
}

// This is also allowed by losetup, not sure what happens if you try to write to the file though.
#[test]
fn detach_a_backing_file_with_sizelimit_overflow() {
    detach_a_backing_file(0, 128 * 1024 * 1024 * 2, 128 * 1024 * 1024);
}

fn detach_a_backing_file(offset: u64, sizelimit: u64, file_size: i64) {
    let num_devices_at_start = list_device(None).len();
    let _lock = setup();

    {
        let file = create_backing_file(file_size);
        attach_file(
            "/dev/loop3",
            file.to_path_buf().to_str().unwrap(),
            offset,
            sizelimit,
        );

        let ld0 = LoopDevice::open("/dev/loop3")
            .expect("should be able to open the created loopback device");

        ld0.detach()
            .expect("should not error detaching the backing file from the loopdev");

        file.close().expect("should delete the temp backing file");
    };

    std::thread::sleep(std::time::Duration::from_millis(100));

    assert_eq!(
        list_device(None).len(),
        num_devices_at_start,
        "there should be no loopback devices mounted"
    );
    detach_all();
}
<|endoftext|>


<commit_before>//! A tool for querying Rust source files wired into Bazel and running Rustfmt on them.

use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str;

/// The Bazel Rustfmt tool entry point
fn main() {
    // Gather all command line and environment settings
    let options = parse_args();

    // Gather a list of all formattable targets
    let targets = query_rustfmt_targets(&options);

    // Run rustfmt on these targets
    apply_rustfmt(&options, &targets);
}

/// The edition to use in cases where the default edition is unspecified by Bazel
const FALLBACK_EDITION: &str = "2018";

/// Determine the Rust edition to use in cases where a target has not explicitly
/// specified the edition via an `edition` attribute.
fn get_default_edition() -> &'static str {
    if !env!("RUST_DEFAULT_EDITION").is_empty() {
        env!("RUST_DEFAULT_EDITION")
    } else {
        FALLBACK_EDITION
    }
}

/// Get a list of all editions to run formatting for
fn get_editions() -> Vec<String> {
    vec!["2015".to_owned(), "2018".to_owned(), "2021".to_owned()]
}

/// Run a bazel command, capturing stdout while streaming stderr to surface errors
fn bazel_command(bazel_bin: &Path, args: &[String], current_dir: &Path) -> Vec<String> {
    let child = Command::new(bazel_bin)
        .current_dir(current_dir)
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn bazel command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        eprintln!("Failed to perform `bazel query` command.");
        std::process::exit(output.status.code().unwrap_or(1));
    }

    str::from_utf8(&output.stdout)
        .expect("Invalid stream from command")
        .split('\n')
        .filter(|line| !line.is_empty())
        .map(|line| line.to_string())
        .collect()
}

/// The regex representation of an empty `edition` attribute
const EMPTY_EDITION: &str = "^$";

/// Query for all `*.rs` files in a workspace that are dependencies of targets with the requested edition.
fn edition_query(bazel_bin: &Path, edition: &str, scope: &str, current_dir: &Path) -> Vec<String> {
    let query_args = vec![
        "query".to_owned(),
        // Query explanation:
        // Filter all local targets ending in `*.rs`.
        //     Get all source files.
        //         Get direct dependencies.
        //             Get all targets with the specified `edition` attribute.
        //             Except for targets tagged with `norustfmt`.
        //             And except for targets with a populated `crate` attribute since `crate` defines edition for this target
        format!(
            r#"let scope = set({scope}) in filter("^//.*\.rs$", kind("source file", deps(attr(edition, "{edition}", $scope) except attr(tags, "(^\[|, )(no-format|no-rustfmt|norustfmt)(, |\]$)", $scope) + attr(crate, ".*", $scope), 1)))"#,
            edition = edition,
            scope = scope,
        ),
        "--keep_going".to_owned(),
        "--noimplicit_deps".to_owned(),
    ];

    bazel_command(bazel_bin, &query_args, current_dir)
}

/// Perform a `bazel` query to determine all source files which are to be
/// formatted for particular Rust editions.
fn query_rustfmt_targets(options: &Config) -> HashMap<String, Vec<String>> {
    let scope = options
        .packages
        .clone()
        .into_iter()
        .reduce(|acc, item| acc + " " + &item)
        .unwrap_or_else(|| "//...:all".to_owned());

    let editions = get_editions();
    let default_edition = get_default_edition();

    editions
        .into_iter()
        .map(|edition| {
            let mut targets = edition_query(&options.bazel, &edition, &scope, &options.workspace);

            // For all targets relying on the toolchain for it's edition,
            // query anything with an unset edition
            if edition == default_edition {
                targets.extend(edition_query(
                    &options.bazel,
                    EMPTY_EDITION,
                    &scope,
                    &options.workspace,
                ))
            }

            (edition, targets)
        })
        .collect()
}

/// Run rustfmt on a set of Bazel targets
fn apply_rustfmt(options: &Config, editions_and_targets: &HashMap<String, Vec<String>>) {
    // There is no work to do if the list of targets is empty
    if editions_and_targets.is_empty() {
        return;
    }

    for (edition, targets) in editions_and_targets.iter() {
        if targets.is_empty() {
            continue;
        }

        // Get paths to all formattable sources
        let sources: Vec<String> = targets
            .iter()
            .map(|target| target.replace(':', "/").trim_start_matches('/').to_owned())
            .collect();

        // Run rustfmt
        let status = Command::new(&options.rustfmt_config.rustfmt)
            .current_dir(&options.workspace)
            .arg("--edition")
            .arg(edition)
            .arg("--config-path")
            .arg(&options.rustfmt_config.config)
            .args(sources)
            .status()
            .expect("Failed to run rustfmt");

        if !status.success() {
            std::process::exit(status.code().unwrap_or(1));
        }
    }
}

/// A struct containing details used for executing rustfmt.
#[derive(Debug)]
struct Config {
    /// The path of the Bazel workspace root.
    pub workspace: PathBuf,

    /// The Bazel executable to use for builds and queries.
    pub bazel: PathBuf,

    /// Information about the current rustfmt binary to run.
    pub rustfmt_config: rustfmt_lib::RustfmtConfig,

    /// Optionally, users can pass a list of targets/packages/scopes
    /// (eg `//my:target` or `//my/pkg/...`) to control the targets
    /// to be formatted. If empty, all targets in the workspace will
    /// be formatted.
    pub packages: Vec<String>,
}

/// Parse command line arguments and environment variables to
/// produce config data for running rustfmt.
fn parse_args() -> Config {
    Config{
        workspace: PathBuf::from(
            env::var("BUILD_WORKSPACE_DIRECTORY")
            .expect("The environment variable BUILD_WORKSPACE_DIRECTORY is required for finding the workspace root")
        ),
        bazel: PathBuf::from(
            env::var("BAZEL_REAL")
            .unwrap_or_else(|_| "bazel".to_owned())
        ),
        rustfmt_config: rustfmt_lib::parse_rustfmt_config(),
        packages: env::args().skip(1).collect(),
    }
}
<commit_msg>fix: incorrect rustfmt edition query (#1582)<commit_after>//! A tool for querying Rust source files wired into Bazel and running Rustfmt on them.

use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str;

/// The Bazel Rustfmt tool entry point
fn main() {
    // Gather all command line and environment settings
    let options = parse_args();

    // Gather a list of all formattable targets
    let targets = query_rustfmt_targets(&options);

    // Run rustfmt on these targets
    apply_rustfmt(&options, &targets);
}

/// The edition to use in cases where the default edition is unspecified by Bazel
const FALLBACK_EDITION: &str = "2018";

/// Determine the Rust edition to use in cases where a target has not explicitly
/// specified the edition via an `edition` attribute.
fn get_default_edition() -> &'static str {
    if !env!("RUST_DEFAULT_EDITION").is_empty() {
        env!("RUST_DEFAULT_EDITION")
    } else {
        FALLBACK_EDITION
    }
}

/// Get a list of all editions to run formatting for
fn get_editions() -> Vec<String> {
    vec!["2015".to_owned(), "2018".to_owned(), "2021".to_owned()]
}

/// Run a bazel command, capturing stdout while streaming stderr to surface errors
fn bazel_command(bazel_bin: &Path, args: &[String], current_dir: &Path) -> Vec<String> {
    let child = Command::new(bazel_bin)
        .current_dir(current_dir)
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn bazel command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        eprintln!("Failed to perform `bazel query` command.");
        std::process::exit(output.status.code().unwrap_or(1));
    }

    str::from_utf8(&output.stdout)
        .expect("Invalid stream from command")
        .split('\n')
        .filter(|line| !line.is_empty())
        .map(|line| line.to_string())
        .collect()
}

/// The regex representation of an empty `edition` attribute
const EMPTY_EDITION: &str = "^$";

/// Query for all `*.rs` files in a workspace that are dependencies of targets with the requested edition.
fn edition_query(bazel_bin: &Path, edition: &str, scope: &str, current_dir: &Path) -> Vec<String> {
    let query_args = vec![
        "query".to_owned(),
        // Query explanation:
        // Filter all local targets ending in `*.rs`.
        //     Get all source files.
        //         Get direct dependencies.
        //             Get all targets with the specified `edition` attribute.
        //             Except for targets tagged with `norustfmt`, `no-rustfmt`, or `no-format`.
        //             And except for targets with a populated `crate` attribute since `crate` defines edition for this target
        format!(
            r#"let scope = set({scope}) in filter("^//.*\.rs$", kind("source file", deps(attr(edition, "{edition}", $scope) except attr(tags, "(^\[|, )(no-format|no-rustfmt|norustfmt)(, |\]$)", $scope) except attr(crate, ".*", $scope), 1)))"#,
            edition = edition,
            scope = scope,
        ),
        "--keep_going".to_owned(),
        "--noimplicit_deps".to_owned(),
    ];

    bazel_command(bazel_bin, &query_args, current_dir)
}

/// Perform a `bazel` query to determine all source files which are to be
/// formatted for particular Rust editions.
fn query_rustfmt_targets(options: &Config) -> HashMap<String, Vec<String>> {
    let scope = options
        .packages
        .clone()
        .into_iter()
        .reduce(|acc, item| acc + " " + &item)
        .unwrap_or_else(|| "//...:all".to_owned());

    let editions = get_editions();
    let default_edition = get_default_edition();

    editions
        .into_iter()
        .map(|edition| {
            let mut targets = edition_query(&options.bazel, &edition, &scope, &options.workspace);

            // For all targets relying on the toolchain for it's edition,
            // query anything with an unset edition
            if edition == default_edition {
                targets.extend(edition_query(
                    &options.bazel,
                    EMPTY_EDITION,
                    &scope,
                    &options.workspace,
                ))
            }

            (edition, targets)
        })
        .collect()
}

/// Run rustfmt on a set of Bazel targets
fn apply_rustfmt(options: &Config, editions_and_targets: &HashMap<String, Vec<String>>) {
    // There is no work to do if the list of targets is empty
    if editions_and_targets.is_empty() {
        return;
    }

    for (edition, targets) in editions_and_targets.iter() {
        if targets.is_empty() {
            continue;
        }

        // Get paths to all formattable sources
        let sources: Vec<String> = targets
            .iter()
            .map(|target| target.replace(':', "/").trim_start_matches('/').to_owned())
            .collect();

        // Run rustfmt
        let status = Command::new(&options.rustfmt_config.rustfmt)
            .current_dir(&options.workspace)
            .arg("--edition")
            .arg(edition)
            .arg("--config-path")
            .arg(&options.rustfmt_config.config)
            .args(sources)
            .status()
            .expect("Failed to run rustfmt");

        if !status.success() {
            std::process::exit(status.code().unwrap_or(1));
        }
    }
}

/// A struct containing details used for executing rustfmt.
#[derive(Debug)]
struct Config {
    /// The path of the Bazel workspace root.
    pub workspace: PathBuf,

    /// The Bazel executable to use for builds and queries.
    pub bazel: PathBuf,

    /// Information about the current rustfmt binary to run.
    pub rustfmt_config: rustfmt_lib::RustfmtConfig,

    /// Optionally, users can pass a list of targets/packages/scopes
    /// (eg `//my:target` or `//my/pkg/...`) to control the targets
    /// to be formatted. If empty, all targets in the workspace will
    /// be formatted.
    pub packages: Vec<String>,
}

/// Parse command line arguments and environment variables to
/// produce config data for running rustfmt.
fn parse_args() -> Config {
    Config{
        workspace: PathBuf::from(
            env::var("BUILD_WORKSPACE_DIRECTORY")
            .expect("The environment variable BUILD_WORKSPACE_DIRECTORY is required for finding the workspace root")
        ),
        bazel: PathBuf::from(
            env::var("BAZEL_REAL")
            .unwrap_or_else(|_| "bazel".to_owned())
        ),
        rustfmt_config: rustfmt_lib::parse_rustfmt_config(),
        packages: env::args().skip(1).collect(),
    }
}
<|endoftext|>


<commit_before>use super::*;
use super::debug::*;
use std::io::Write;
use std::str::from_utf8;

pub struct SerializeToStr<'a>  {
    pub s : &'a mut String,
}

impl<'a> super::EventsHandler for SerializeToStr<'a> {
    fn event(&mut self, e :super::Event) {
        use super::Event::*;

        let np = match e {
            Begin(x)    => format!("b {:?},", x),
            Data(ref x) => format!("d {:?},", x),
            End(x)      => format!("e {:?},", x),
            Resync      => format!("r,"),
        };

        self.s.push_str(&np);
    }
}

macro_rules! t {
    ($input:expr, $output:expr) => {{{
        let mut s : String = Default::default();
        {
            let mut strdumper = SerializeToStr { s:  &mut s };
            let mut p = super::new();
            p.feed_bytes(&$input, &mut strdumper);
        }
        assert_eq!(s, $output);
    }{
        let mut s : String = Default::default();
        {
            let mut strdumper = SerializeToStr { s: &mut s };
            let mut p = super::new();
            for i in $input.iter() {
                p.feed_bytes(&[*i], &mut strdumper);
            }
        }
        assert_eq!(s, $output);
    }}}
}

#[test] fn t1() {
t!( [0xA3, 0x82, 33, 44] , "b SimpleBlock(offset:0, rawlen:4),d Binary([33, 44], Full),e SimpleBlock(offset:0, rawlen:4),");}

#[test] fn t2() {
t!( [255, 255, 255, 255], "r,");}

#[test] fn t3() {
t!( [
  0x1a, 0x45, 0xdf, 0xa3, 0xa3, 0x42, 0x86, 0x81, 0x01, 0x42, 0xf7, 0x81,
  0x01, 0x42, 0xf2, 0x81, 0x04, 0x42, 0xf3, 0x81, 0x08, 0x42, 0x82, 0x88,
  0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x42, 0x87, 0x81, 0x02,
  0x42, 0x85, 0x81, 0x02
],
"\
b EBML(offset:0, rawlen:40),\
b EBMLVersion(offset:5, rawlen:4),\
d Unsigned(1),\
e EBMLVersion(offset:5, rawlen:4),\
b EBMLReadVersion(offset:9, rawlen:4),\
d Unsigned(1),\
e EBMLReadVersion(offset:9, rawlen:4),\
b EBMLMaxIDLength(offset:13, rawlen:4),\
d Unsigned(4),\
e EBMLMaxIDLength(offset:13, rawlen:4),\
b EBMLMaxSizeLength(offset:17, rawlen:4),\
d Unsigned(8),\
e EBMLMaxSizeLength(offset:17, rawlen:4),\
b DocType(offset:21, rawlen:11),\
d Text(\"matroska\"),\
e DocType(offset:21, rawlen:11),\
b DocTypeVersion(offset:32, rawlen:4),\
d Unsigned(2),\
e DocTypeVersion(offset:32, rawlen:4),\
b DocTypeReadVersion(offset:36, rawlen:4),\
d Unsigned(2),\
e DocTypeReadVersion(offset:36, rawlen:4),\
e EBML(offset:0, rawlen:40),\
");}

#[test] fn t4() {
t!( [ 0xff, 0xff, 0x1a, 0x45, 0xdf, 0xa3, 0xa3], "r,b EBML(offset:2, rawlen:40),");}

#[test] fn t5() {
t!( [ 0x44, 0x89, 0x84, 0x42, 0xe8, 0x00, 0x00 ],
        "b Duration(offset:0, rawlen:7),d Float(116),e Duration(offset:0, rawlen:7),");}

#[test] fn t6() {
t!( [ 0x86, 0x85, 0x41, 0x42, 0x43, 0x44, 0x45 ],
        "b CodecID(offset:0, rawlen:7),d Text(\"ABCDE\"),e CodecID(offset:0, rawlen:7),");}

#[test] fn t7() {
t!( [0xA3, 0x80] , "b SimpleBlock(offset:0, rawlen:2),d Binary([], Full),e SimpleBlock(offset:0, rawlen:2),");}

#[test] fn t8() {
t!( [0x1a, 0x45, 0xdf, 0xa3, 0x80] , "b EBML(offset:0, rawlen:5),e EBML(offset:0, rawlen:5),");}
<commit_msg>Fix the test caused by Debug break<commit_after>use super::*;
use super::debug::*;
use std::io::Write;
use std::str::from_utf8;

pub struct SerializeToStr<'a>  {
    pub s : &'a mut String,
}

impl<'a> super::EventsHandler for SerializeToStr<'a> {
    fn event(&mut self, e :super::Event) {
        use super::Event::*;

        let np = match e {
            Begin(x)    => format!("b {:?},", x),
            Data(ref x) => format!("d {:?},", x),
            End(x)      => format!("e {:?},", x),
            Resync      => format!("r,"),
        };

        self.s.push_str(&np);
    }
}

macro_rules! t {
    ($input:expr, $output:expr) => {{{
        let mut s : String = Default::default();
        {
            let mut strdumper = SerializeToStr { s:  &mut s };
            let mut p = super::new();
            p.feed_bytes(&$input, &mut strdumper);
        }
        assert_eq!(s, $output);
    }{
        let mut s : String = Default::default();
        {
            let mut strdumper = SerializeToStr { s: &mut s };
            let mut p = super::new();
            for i in $input.iter() {
                p.feed_bytes(&[*i], &mut strdumper);
            }
        }
        assert_eq!(s, $output);
    }}}
}

#[test] fn t1() {
t!( [0xA3, 0x82, 33, 44] , "b SimpleBlock(offset:0, rawlen:4),d Binary([33, 44], Full),e SimpleBlock(offset:0, rawlen:4),");}

#[test] fn t2() {
t!( [255, 255, 255, 255], "r,");}

#[test] fn t3() {
t!( [
  0x1a, 0x45, 0xdf, 0xa3, 0xa3, 0x42, 0x86, 0x81, 0x01, 0x42, 0xf7, 0x81,
  0x01, 0x42, 0xf2, 0x81, 0x04, 0x42, 0xf3, 0x81, 0x08, 0x42, 0x82, 0x88,
  0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x42, 0x87, 0x81, 0x02,
  0x42, 0x85, 0x81, 0x02
],
"\
b EBML(offset:0, rawlen:40),\
b EBMLVersion(offset:5, rawlen:4),\
d Unsigned(1),\
e EBMLVersion(offset:5, rawlen:4),\
b EBMLReadVersion(offset:9, rawlen:4),\
d Unsigned(1),\
e EBMLReadVersion(offset:9, rawlen:4),\
b EBMLMaxIDLength(offset:13, rawlen:4),\
d Unsigned(4),\
e EBMLMaxIDLength(offset:13, rawlen:4),\
b EBMLMaxSizeLength(offset:17, rawlen:4),\
d Unsigned(8),\
e EBMLMaxSizeLength(offset:17, rawlen:4),\
b DocType(offset:21, rawlen:11),\
d Text(\"matroska\"),\
e DocType(offset:21, rawlen:11),\
b DocTypeVersion(offset:32, rawlen:4),\
d Unsigned(2),\
e DocTypeVersion(offset:32, rawlen:4),\
b DocTypeReadVersion(offset:36, rawlen:4),\
d Unsigned(2),\
e DocTypeReadVersion(offset:36, rawlen:4),\
e EBML(offset:0, rawlen:40),\
");}

#[test] fn t4() {
t!( [ 0xff, 0xff, 0x1a, 0x45, 0xdf, 0xa3, 0xa3], "r,b EBML(offset:2, rawlen:40),");}

#[test] fn t5() {
t!( [ 0x44, 0x89, 0x84, 0x42, 0xe8, 0x00, 0x00 ],
        "b Duration(offset:0, rawlen:7),d Float(116.0),e Duration(offset:0, rawlen:7),");}

#[test] fn t6() {
t!( [ 0x86, 0x85, 0x41, 0x42, 0x43, 0x44, 0x45 ],
        "b CodecID(offset:0, rawlen:7),d Text(\"ABCDE\"),e CodecID(offset:0, rawlen:7),");}

#[test] fn t7() {
t!( [0xA3, 0x80] , "b SimpleBlock(offset:0, rawlen:2),d Binary([], Full),e SimpleBlock(offset:0, rawlen:2),");}

#[test] fn t8() {
t!( [0x1a, 0x45, 0xdf, 0xa3, 0x80] , "b EBML(offset:0, rawlen:5),e EBML(offset:0, rawlen:5),");}
<|endoftext|>


<commit_before>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);

            if part.is_number() {
                if !self.last_was_an_operator {
                    debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                    if self.paren_level > 0 {
                        while !self.op_stack.is_empty() &&
                              self.op_stack.last().unwrap().operator !=
                                  Operator::LeftParen {

                            debug!("op stack last {:#?}", self.op_stack.last());
                            self.math_machine();
                        }
                    }
                }

                if self.last_was_an_operator {
                    debug!("Push on value stack {:#?}", part);
                    self.value_stack.push(part);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );

                    debug!("Push on list on value stack {:#?}", list);

                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;

            } else if part.is_right_paren() {
                debug!("RIGHT PAREN");
                debug!("op stack = {:#?}", self.op_stack);

                let mut last_operator = self.op_stack.pop();
                while last_operator.is_some() &&
                      last_operator.unwrap().operator !=
                          Operator::LeftParen {

                    debug!("last operator before right paren = {:#?}", last_operator);
                    self.op_stack.push(last_operator.unwrap());
                    self.math_machine();
                    last_operator = self.op_stack.pop();
                }
                self.last_was_an_operator = false;
                self.paren_level -= 1;
            } else if part.is_left_paren() {
                let oo = part.extract_operator_offset();
                debug!("Push on op stack Leftparen");
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
                self.paren_level += 1;
            } else if part.is_operator() {
                let oo = part.extract_operator_offset();
                if let Some(&last_operator) = self.op_stack.last() {
                    if last_operator
                           .operator
                           .same_or_greater_precedence(oo.operator) {
                        self.math_machine();
                    }
                }
                debug!("Push on op stack {:#?}", oo);
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
            } else if part.is_string() {
                let t = part.extract_token_offset();
                let var_eval = self.context.get_variable(&t)
                                .unwrap_or(Expression::Value(
                                    OperatorOrToken::Token(t)
                                ));

                if self.last_was_an_operator {
                    self.value_stack.push(var_eval);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        var_eval,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            } else if let Expression::List(list) = part {
                debug!("Push list on value stack {:#?}", list);
                self.value_stack.push(Expression::List(list));
                self.last_was_an_operator = false;
            } else {
                if self.last_was_an_operator {
                    self.value_stack.push(part);
                } else {
                    let list = Expression::create_list(
                        self.value_stack.pop(),
                        part,
                    );
                    self.value_stack.push(list);
                }
                self.last_was_an_operator = false;
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op     = self.get_operator();
        let second = self.get_value();
        let first  = self.get_value();

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }

    fn get_operator(&mut self) -> OperatorOffset {
        // Math machine only gets called while there are operators on
        // the operator stack, so unwrap should be fine.
        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);
        op
    }

    fn get_value(&mut self) -> Expression {
        // TODO: Turn this into a SassError
        let val = self.value_stack.pop()
                      .expect("Expected an argument on the value stack");
        debug!("val = {:#?}", val);
        val
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<commit_msg>Extract a method for pushing on the value stack<commit_after>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);

            if part.is_number() {
                if !self.last_was_an_operator {
                    debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                    if self.paren_level > 0 {
                        while !self.op_stack.is_empty() &&
                              self.op_stack.last().unwrap().operator !=
                                  Operator::LeftParen {

                            debug!("op stack last {:#?}", self.op_stack.last());
                            self.math_machine();
                        }
                    }
                }
                self.push_on_value_stack(part);

            } else if part.is_right_paren() {
                debug!("RIGHT PAREN");
                debug!("op stack = {:#?}", self.op_stack);

                let mut last_operator = self.op_stack.pop();
                while last_operator.is_some() &&
                      last_operator.unwrap().operator !=
                          Operator::LeftParen {

                    debug!("last operator before right paren = {:#?}", last_operator);
                    self.op_stack.push(last_operator.unwrap());
                    self.math_machine();
                    last_operator = self.op_stack.pop();
                }
                self.last_was_an_operator = false;
                self.paren_level -= 1;
            } else if part.is_left_paren() {
                let oo = part.extract_operator_offset();
                debug!("Push on op stack Leftparen");
                self.op_stack.push(oo);
                self.last_was_an_operator = true;
                self.paren_level += 1;
            } else if part.is_operator() {
                let oo = part.extract_operator_offset();
                if let Some(&last_operator) = self.op_stack.last() {
                    if last_operator
                           .operator
                           .same_or_greater_precedence(oo.operator) {
                        self.math_machine();
                    }
                }
                debug!("Push on op stack {:#?}", oo);
                self.op_stack.push(oo);
                self.last_was_an_operator = true;

            } else if part.is_string() {

                let t = part.extract_token_offset();
                let var_eval = self.context.get_variable(&t)
                                .unwrap_or(Expression::Value(
                                    OperatorOrToken::Token(t)
                                ));
                self.push_on_value_stack(var_eval);

            } else {
                self.push_on_value_stack(part);
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn push_on_value_stack(&mut self, expr: Expression) {
        if self.last_was_an_operator {
            self.value_stack.push(expr);
        } else {
            let list = Expression::create_list(
                self.value_stack.pop(),
                expr,
            );
            self.value_stack.push(list);
        }
        self.last_was_an_operator = false;
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op     = self.get_operator();
        let second = self.get_value();
        let first  = self.get_value();

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }

    fn get_operator(&mut self) -> OperatorOffset {
        // Math machine only gets called while there are operators on
        // the operator stack, so unwrap should be fine.
        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);
        op
    }

    fn get_value(&mut self) -> Expression {
        // TODO: Turn this into a SassError
        let val = self.value_stack.pop()
                      .expect("Expected an argument on the value stack");
        debug!("val = {:#?}", val);
        val
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<|endoftext|>