<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.

#![crate_name = "testpeg"]
#![experimental]
#![crate_type = "bin"]

#![feature(phase)]

#[phase(plugin)]
extern crate peg;

use std::os;
use std::io::File;
use std::io::fs;

pub mod ntcc;

enum ExpectedResult {
  Match,
  PartialMatch,
  Error
}

fn expected_to_string(expected: ExpectedResult) -> &'static str
{
  match expected {
    Match => "fully match",
    PartialMatch => "partially match",
    Error => "fail"
  }
}

fn parse_res_to_string<'a>(res: &Result<Option<&'a str>, String>) -> String
{
  match res {
    &Ok(None) => String::from_str("fully matched"),
    &Ok(Some(ref rest)) =>
      format!("partially matched (it remains `{}`)", rest),
    &Err(ref msg) =>
      format!("failed with the error \"{}\"", msg)
  }
}

struct Test
{
  name: String,
  parse: proc <'a>(input: &'a str) -> Result<Option<&'a str>, String>
}

impl Test
{
  fn test_directory(&self, directory: Path, expectation: ExpectedResult)
  {
    match fs::readdir(&directory) {
      Ok(dir_contents) => {
        for filepath in dir_contents.iter() {
          self.test_file(filepath, expectation);
        }
      }
      Err(io_err) => skip_test(self.name,
        &format!("Impossible to read the directory `{}`, the error is `{}`",
          directory.display(), io_err))
    }
  }

  fn test_file(&self, filepath: Path, expectation: ExpectedResult)
  {
    let mut file = File::open(filepath);
    match file {
      Err(io_err) => fail!(format!("[Failure] Could not open test `{}`, the error is `{}`",
        filepath.display(), io_err)),
      Ok(ref mut file) => {
        let contents = file.read_to_end();
        match contents {
          Err(io_err) => fail!(format!("[Failure] Could not read test `{}`, the error is `{}`",
            filepath.display(), io_err)),
          Ok(contents) => {
            let utf8_contents = std::str::from_utf8(contents.as_slice());
            self.test_input(utf8_contents.unwrap(), expectation);
          }
        }
      }
    }
  }

  fn test_input(&self, input: &str, expectation: ExpectedResult)
  {
    match (expectation, parse(input)) {
      (Match, Ok(None))
    | (PartialMatch, Ok(Some(_)))
    | (Error, Err(_)) => (),
      (expected, res) => {
        fail!(format!("Grammar {}: `{}` was expected to {} but {}.",
          self.name, input, expected_to_string(expectation),
          parse_res_to_string(&res)));
      }
    }
  }

  fn skip_test(test_name: &str, reason: &String)
  {
    fail!(format!("[Skip] {}: {}.", test_name, reason));
  }
}

struct TestEngine
{
  test_path : Path,
  tests : Vec<Test>,
  current_test_idx: uint
}

impl TestEngine
{
  fn new(test_path: Path) -> TestEngine
  {
    if !test_path.is_dir() {
      fail!(format!("`{}` is not a valid grammar directory.", test_path.display()));
    }
    TestEngine{
      test_path: test_path,
      tests : Vec::new(),
      current_test_idx : 0
    }
  }

  fn register(&mut self, name: &str,
    parse: proc <'a>(input: &'a str) -> Result<Option<&'a str>, String>)
  {
    self.tests.push(Test{name: String::from_str(name), parse: parse});
  }

  fn run(&self)
  {
    let ref test = self.tests.as_slice()[0];
    test.test_directory("run-fail", Error);
    test.test_directory("run-pass", Match);
  }
}

fn main()
{
  let args = os::args();
  if args.len() != 2 {
    fail!(format!("usage: {} <data-dir>", args.as_slice()[0]));
  }
  let data_path = Path::new(args.as_slice()[1].clone());
  if !data_path.is_dir() {
    fail!(format!("`{}` is not a valid data directory.", data_path.display()));
  }
  let mut test_path = data_path.clone();
  test_path.push("test");
  let mut test_engine = TestEngine::new(test_path);
  test_engine.register("ntcc", ntcc::ntcc::parse);
  test_engine.run();
}
<commit_msg>[CompilationFix] Store the parser structure instead of a procedure.<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.

#![crate_name = "testpeg"]
#![experimental]
#![crate_type = "bin"]

#![feature(phase,globs)]

#[phase(plugin,link)]
extern crate peg;

use std::os;
use std::io::File;
use std::io::fs;

use peg::Parser;

pub mod ntcc;

enum ExpectedResult {
  Match,
  PartialMatch,
  Error
}

fn expectation_to_string(expected: ExpectedResult) -> &'static str
{
  match expected {
    Match => "fully match",
    PartialMatch => "partially match",
    Error => "fail"
  }
}

fn parsing_result_to_string<'a>(res: &Result<Option<&'a str>, String>) -> String
{
  match res {
    &Ok(None) => String::from_str("fully matched"),
    &Ok(Some(ref rest)) =>
      format!("partially matched (it remains `{}`)", rest),
    &Err(ref msg) =>
      format!("failed with the error \"{}\"", msg)
  }
}

fn skip_test(test_name: &String, reason: &String)
{
  fail!(format!("[Skip] {}: {}.", test_name, reason));
}

struct Test
{
  name: String,
  parser: Box<Parser>
}

impl Test
{
  fn test_directory(&self, directory: Path, expectation: ExpectedResult)
  {
    match fs::readdir(&directory) {
      Ok(dir_entries) => {
        for entry in dir_entries.iter() {
          if entry.is_file() {
            self.test_file(entry, expectation);
          }
        }
      }
      Err(io_err) => skip_test(&self.name,
        &format!("Impossible to read the directory `{}`, the error is `{}`",
          directory.display(), io_err))
    }
  }

  fn test_file(&self, filepath: &Path, expectation: ExpectedResult)
  {
    let mut file = File::open(filepath);
    match file {
      Err(io_err) => fail!(format!("[Failure] Could not open test `{}`, the error is `{}`",
        filepath.display(), io_err)),
      Ok(ref mut file) => {
        let contents = file.read_to_end();
        match contents {
          Err(io_err) => fail!(format!("[Failure] Could not read test `{}`, the error is `{}`",
            filepath.display(), io_err)),
          Ok(contents) => {
            let utf8_contents = std::str::from_utf8(contents.as_slice());
            self.test_input(utf8_contents.unwrap(), expectation);
          }
        }
      }
    }
  }

  fn test_input(&self, input: &str, expectation: ExpectedResult)
  {
    match (expectation, self.parser.parse(input)) {
      (Match, Ok(None))
    | (PartialMatch, Ok(Some(_)))
    | (Error, Err(_)) => (),
      (_, res) => {
        fail!(format!("Grammar {}: `{}` was expected to {} but {}.",
          self.name, input, expectation_to_string(expectation),
          parsing_result_to_string(&res)));
      }
    }
  }
}

struct TestEngine
{
  test_path : Path,
  tests : Vec<Test>,
  current_test_idx: uint
}

impl TestEngine
{
  fn new(test_path: Path) -> TestEngine
  {
    if !test_path.is_dir() {
      fail!(format!("`{}` is not a valid grammar directory.", test_path.display()));
    }
    TestEngine{
      test_path: test_path,
      tests : Vec::new(),
      current_test_idx : 0
    }
  }

  fn register(&mut self, name: &str, parser: Box<Parser>)
  {
    self.tests.push(Test{name: String::from_str(name), parser: parser});
  }

  fn run(&self)
  {
    for test in self.tests.iter() {
      let path = self.test_path.join(Path::new(test.name.clone()));
      test.test_directory(path.join(Path::new("run-pass")), Match);
      test.test_directory(path.join(Path::new("run-fail")), Error);
    }
  }
}

fn main()
{
  let args = os::args();
  if args.len() != 2 {
    fail!(format!("usage: {} <data-dir>", args.as_slice()[0]));
  }
  let data_path = Path::new(args.as_slice()[1].clone());
  if !data_path.is_dir() {
    fail!(format!("`{}` is not a valid data directory.", data_path.display()));
  }
  let mut test_path = data_path.clone();
  test_path.push("test");
  let mut test_engine = TestEngine::new(test_path);
  test_engine.register("ntcc", box ntcc::ntcc::Parser::new());
  test_engine.run();
}
<|endoftext|>


<commit_before>/// The Toml Delete extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueDeleteExt {

    /// Extension function for deleting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// # Semantics
    ///
    /// The function does _not_ delete non-empty data structures. So deleting `array` from
    ///
    /// ```toml
    /// array = [ 1 ]
    /// ```
    ///
    /// does _not_ work.
    ///
    /// # Return value
    ///
    /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
    ///
    /// The `Option<Value>` part is `None` if no value was actually removed as there was no value
    /// there. For example, if you're deleting `table.a` and the Table `table` has no key `a`, then
    /// `Ok(None)` is returned. Also, if you're deleting from an Array, but there is nothing in the
    /// array, or the array is shorter than the index you're deleting.
    /// If the delete operation actually removed something from the toml document, this value is
    /// returned as `Ok(Some(Value))`.
    ///
    /// On failure, `Err(e)` is returned
    ///
    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn delete(&mut self, query: &String) -> Result<Option<Value>> {
        self.delete_with_seperator(query, '.')
    }

}

impl TomlValueDeleteExt for Value {

    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;
        use std::ops::Index;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last_token = tokens.pop_last();

        /// Check whether a structure (Table/Array) is empty. If the Value has not these types,
        /// the default value is returned
        #[inline]
        fn is_empty(val: Option<&Value>, default: bool) -> bool {
            val.map(|v| match v {
                    &Value::Table(ref tab) => tab.is_empty(),
                    &Value::Array(ref arr) => arr.is_empty(),
                    _                      => default
                })
                .unwrap_or(default)
        }

        #[inline]
        fn is_table(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Table(_))).unwrap_or(false)
        }

        #[inline]
        fn is_array(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Array(_))).unwrap_or(false)
        }

        #[inline]
        fn name_of_val(val: Option<&Value>) -> &'static str {
            val.map(|v| match v {
                &Value::Array(_)    => "Array",
                &Value::Boolean(_)  => "Boolean",
                &Value::Datetime(_) => "Datetime",
                &Value::Float(_)    => "Float",
                &Value::Integer(_)  => "Integer",
                &Value::String(_)   => "String",
                &Value::Table(_)    => "Table",
            }).unwrap_or("None")
        }

        if last_token.is_none() {
            match self {
                &mut Value::Table(ref mut tab) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            if is_empty(tab.get(&ident), true) {
                                Ok(tab.remove(&ident))
                            } else {
                                if is_table(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                    Err(Error::from(kind))
                                } else if is_array(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyArray(ident.clone());
                                    Err(Error::from(kind))
                                } else {
                                    let act = name_of_val(tab.get(&ident));
                                    let tbl = "table";
                                    let k   = ErrorKind::CannotAccessBecauseTypeMismatch(tbl, act);
                                    Err(Error::from(k))
                                }
                            }
                        },
                        _ => Ok(None)
                    }
                },
                &mut Value::Array(ref mut arr) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            let ek = ErrorKind::NoIdentifierInArray(ident);
                            Err(Error::from(ek))
                        },
                        Token::Index { idx , .. } => {
                            if is_empty(Some(arr.index(idx)), true) {
                                Ok(Some(arr.remove(idx)))
                            } else {
                                unimplemented!()
                            }
                        },
                    }
                },
                _ => unimplemented!()
            }
        } else {
            let mut val = try!(resolve(self, &tokens));
            let last_token = last_token.unwrap();
            match val {
                &mut Value::Table(ref mut tab) => {
                    match *last_token {
                        Token::Identifier { ref ident, .. } => {
                            if is_empty(tab.get(ident), true) {
                                Ok(tab.remove(ident))
                            } else {
                                if is_table(tab.get(ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                    Err(Error::from(kind))
                                } else if is_array(tab.get(ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyArray(ident.clone());
                                    Err(Error::from(kind))
                                } else {
                                    let act = name_of_val(tab.get(ident));
                                    let tbl = "table";
                                    let k   = ErrorKind::CannotAccessBecauseTypeMismatch(tbl, act);
                                    Err(Error::from(k))
                                }
                            }
                        },
                        Token::Index { idx, .. } => {
                            unimplemented!()
                        },
                    }
                },
                &mut Value::Array(ref mut arr) => {
                    match *last_token {
                        Token::Identifier { ref ident, .. } => {
                            unimplemented!()
                        },
                        Token::Index { idx, .. } => {
                            if is_empty(Some(&arr.index(idx)), true) {
                                Ok(Some(arr.remove(idx)))
                            } else {
                                unimplemented!()
                            }
                        },
                    }
                },
                _ => {
                    unimplemented!()
                }
            }
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_delete_from_empty_document() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.delete_with_seperator(&String::from("a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_from_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_integer() {
        let mut toml : Value = toml_from_str(r#"
        value = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(1)));
    }

    #[test]
    fn test_delete_string() {
        let mut toml : Value = toml_from_str(r#"
        value = "foo"
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::String(_)));
        match res {
            Value::String(ref s) => assert_eq!("foo", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Table(_)));
        match res {
            Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Array(_)));
        match res {
            Value::Array(ref a) => assert!(a.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_nonempty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyTable(_)));
    }

    #[test]
    fn test_delete_nonempty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyArray(_)));
    }

    #[test]
    fn test_delete_int_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        int = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.int"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_array_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Array(_))));
    }

    #[test]
    fn test_delete_int_from_array_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.array.[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_int_from_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array.[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_int_from_table_from_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ { table = { int = 1 } } ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array.[0].table.int"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_from_array_value() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_from_int_value() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array.[0]")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("nonexist"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::QueryingValueAsTable(_)));
    }

    #[test]
    fn test_delete_index_from_non_array() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = 1
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("[0]"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotAccessBecauseTypeMismatch(_, _)));
    }

}

<commit_msg>Impl Value::delete_with_seperator to fix test_delete_from_int_value<commit_after>/// The Toml Delete extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueDeleteExt {

    /// Extension function for deleting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// # Semantics
    ///
    /// The function does _not_ delete non-empty data structures. So deleting `array` from
    ///
    /// ```toml
    /// array = [ 1 ]
    /// ```
    ///
    /// does _not_ work.
    ///
    /// # Return value
    ///
    /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
    ///
    /// The `Option<Value>` part is `None` if no value was actually removed as there was no value
    /// there. For example, if you're deleting `table.a` and the Table `table` has no key `a`, then
    /// `Ok(None)` is returned. Also, if you're deleting from an Array, but there is nothing in the
    /// array, or the array is shorter than the index you're deleting.
    /// If the delete operation actually removed something from the toml document, this value is
    /// returned as `Ok(Some(Value))`.
    ///
    /// On failure, `Err(e)` is returned
    ///
    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn delete(&mut self, query: &String) -> Result<Option<Value>> {
        self.delete_with_seperator(query, '.')
    }

}

impl TomlValueDeleteExt for Value {

    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;
        use std::ops::Index;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last_token = tokens.pop_last();

        /// Check whether a structure (Table/Array) is empty. If the Value has not these types,
        /// the default value is returned
        #[inline]
        fn is_empty(val: Option<&Value>, default: bool) -> bool {
            val.map(|v| match v {
                    &Value::Table(ref tab) => tab.is_empty(),
                    &Value::Array(ref arr) => arr.is_empty(),
                    _                      => default
                })
                .unwrap_or(default)
        }

        #[inline]
        fn is_table(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Table(_))).unwrap_or(false)
        }

        #[inline]
        fn is_array(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Array(_))).unwrap_or(false)
        }

        #[inline]
        fn name_of_val(val: Option<&Value>) -> &'static str {
            val.map(|v| match v {
                &Value::Array(_)    => "Array",
                &Value::Boolean(_)  => "Boolean",
                &Value::Datetime(_) => "Datetime",
                &Value::Float(_)    => "Float",
                &Value::Integer(_)  => "Integer",
                &Value::String(_)   => "String",
                &Value::Table(_)    => "Table",
            }).unwrap_or("None")
        }

        if last_token.is_none() {
            match self {
                &mut Value::Table(ref mut tab) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            if is_empty(tab.get(&ident), true) {
                                Ok(tab.remove(&ident))
                            } else {
                                if is_table(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                    Err(Error::from(kind))
                                } else if is_array(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyArray(ident.clone());
                                    Err(Error::from(kind))
                                } else {
                                    let act = name_of_val(tab.get(&ident));
                                    let tbl = "table";
                                    let k   = ErrorKind::CannotAccessBecauseTypeMismatch(tbl, act);
                                    Err(Error::from(k))
                                }
                            }
                        },
                        _ => Ok(None)
                    }
                },
                &mut Value::Array(ref mut arr) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            let ek = ErrorKind::NoIdentifierInArray(ident);
                            Err(Error::from(ek))
                        },
                        Token::Index { idx , .. } => {
                            if is_empty(Some(arr.index(idx)), true) {
                                Ok(Some(arr.remove(idx)))
                            } else {
                                unimplemented!()
                            }
                        },
                    }
                },
                _ => {
                    let kind = match tokens {
                        Token::Identifier { ident, .. } => ErrorKind::QueryingValueAsTable(ident),
                        Token::Index { idx , .. } => ErrorKind::QueryingValueAsArray(idx),
                    };
                    Err(Error::from(kind))
                }
            }
        } else {
            let mut val = try!(resolve(self, &tokens));
            let last_token = last_token.unwrap();
            match val {
                &mut Value::Table(ref mut tab) => {
                    match *last_token {
                        Token::Identifier { ref ident, .. } => {
                            if is_empty(tab.get(ident), true) {
                                Ok(tab.remove(ident))
                            } else {
                                if is_table(tab.get(ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                    Err(Error::from(kind))
                                } else if is_array(tab.get(ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyArray(ident.clone());
                                    Err(Error::from(kind))
                                } else {
                                    let act = name_of_val(tab.get(ident));
                                    let tbl = "table";
                                    let k   = ErrorKind::CannotAccessBecauseTypeMismatch(tbl, act);
                                    Err(Error::from(k))
                                }
                            }
                        },
                        Token::Index { idx, .. } => {
                            unimplemented!()
                        },
                    }
                },
                &mut Value::Array(ref mut arr) => {
                    match *last_token {
                        Token::Identifier { ref ident, .. } => {
                            unimplemented!()
                        },
                        Token::Index { idx, .. } => {
                            if is_empty(Some(&arr.index(idx)), true) {
                                Ok(Some(arr.remove(idx)))
                            } else {
                                unimplemented!()
                            }
                        },
                    }
                },
                _ => {
                    unimplemented!()
                }
            }
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_delete_from_empty_document() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.delete_with_seperator(&String::from("a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_from_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_integer() {
        let mut toml : Value = toml_from_str(r#"
        value = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(1)));
    }

    #[test]
    fn test_delete_string() {
        let mut toml : Value = toml_from_str(r#"
        value = "foo"
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::String(_)));
        match res {
            Value::String(ref s) => assert_eq!("foo", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Table(_)));
        match res {
            Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Array(_)));
        match res {
            Value::Array(ref a) => assert!(a.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_nonempty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyTable(_)));
    }

    #[test]
    fn test_delete_nonempty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyArray(_)));
    }

    #[test]
    fn test_delete_int_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        int = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.int"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_array_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Array(_))));
    }

    #[test]
    fn test_delete_int_from_array_from_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.array.[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_int_from_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array.[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_int_from_table_from_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ { table = { int = 1 } } ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array.[0].table.int"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_from_array_value() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("[0]"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(is_match!(res, Some(Value::Integer(1))));
    }

    #[test]
    fn test_delete_from_int_value() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array.[0]")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("nonexist"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::QueryingValueAsTable(_)));
    }

    #[test]
    fn test_delete_index_from_non_array() {
        use read::TomlValueReadExt;

        let mut toml : Value = toml_from_str(r#"
        array = 1
        "#).unwrap();

        let mut ary = toml.read_mut(&String::from("array")).unwrap();
        let res     = ary.delete_with_seperator(&String::from("[0]"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotAccessBecauseTypeMismatch(_, _)));
    }

}

<|endoftext|>


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

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, name: &[u8]) -> () {
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name, &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, uuid: &[u8]) -> () {
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid, &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, u64)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), devno));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());
        // TODO: concat uuid or something?
        //Self::hdr_set_uuid(&mut hdr, lv.uuid);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(pv::Device::from(hdr.dev as i64));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg.ty.as_bytes(), &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<commit_msg>Change dm::list_devices to return a Device instead of u64<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv::Device;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, name: &[u8]) -> () {
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name, &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, uuid: &[u8]) -> () {
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid, &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, Device)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), Device::from(devno as i64)));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());
        // TODO: concat uuid or something?
        //Self::hdr_set_uuid(&mut hdr, lv.uuid);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(Device::from(hdr.dev as i64));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg.ty.as_bytes(), &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<|endoftext|>


<commit_before>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;
extern crate rustc_middle;

use rustc_interface::interface;
use rustc_middle::ty::TyCtxt;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {},
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = 0;
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
    let hook = panic::take_hook();
    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
    hook
});

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    TyCtxt::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        // this check ensures that dependencies are built but not linted and the final
        // crate is linted but not built
        let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
            || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();

        if clippy_enabled {
            args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
            if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
                args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| {
                    if s.is_empty() {
                        None
                    } else {
                        Some(s.to_string())
                    }
                }));
            }
        }
        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };
        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<commit_msg>Improve warnings on incompatible options involving -Zinstrument-coverage<commit_after>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;
extern crate rustc_middle;

use rustc_interface::interface;
use rustc_middle::ty::TyCtxt;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {}
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = Some(0);
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
    SyncLazy::new(|| {
        let hook = panic::take_hook();
        panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
        hook
    });

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    TyCtxt::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode =
            orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode
            && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1)
        {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        // this check ensures that dependencies are built but not linted and the final
        // crate is linted but not built
        let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
            || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();

        if clippy_enabled {
            args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
            if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
                args.extend(
                    extra_args
                        .split("__CLIPPY_HACKERY__")
                        .filter_map(|s| if s.is_empty() { None } else { Some(s.to_string()) }),
                );
            }
        }
        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };
        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<|endoftext|>


<commit_before>
use super::Context;
use super::buffer::VertexBuffer;
use super::vertexarray::VertexArray;

pub struct VertexBufferEditor<'a> {
    #[allow(dead_code)]
    context: &'a mut Context,
    vertex_buffer: &'a VertexBuffer
}

impl<'a> VertexBufferEditor<'a> {
    pub fn data<D>(&mut self, data: &[D]) {
        self.context.vbo_tracker.bind(self.vertex_buffer);
        self.vertex_buffer.data(data);
    }

    pub fn sub_data<D>(&mut self, data: &[D], offset: uint) {
        self.context.vbo_tracker.bind(self.vertex_buffer);
        self.vertex_buffer.sub_data(data, offset);
    }
}

pub fn new_vertex_buffer_editor<'a>(context: &'a mut Context, vertex_buffer: &'a VertexBuffer) -> VertexBufferEditor<'a> {
    VertexBufferEditor { context: context, vertex_buffer: vertex_buffer }
}

pub struct IndexBufferEditor<'a> {
    #[allow(dead_code)]
    context: &'a mut Context,
    vertex_array: &'a VertexArray
}

impl<'a> IndexBufferEditor<'a> {
    pub fn data<D>(&mut self, data: &[D]) {
        self.context.vao_tracker.bind(self.vertex_array);
        if let Some(ref index_buffer) = self.vertex_array.index_buffer() {
            index_buffer.data(data);
        }
    }

    pub fn sub_data<D>(&mut self, data: &[D], offset: uint) {
        self.context.vao_tracker.bind(self.vertex_array);
        if let Some(ref index_buffer) = self.vertex_array.index_buffer() {
            index_buffer.sub_data(data, offset);
        }
    }
}

pub fn new_index_buffer_editor<'a>(context: &'a mut Context, vertex_array: &'a VertexArray) -> IndexBufferEditor<'a> {
    IndexBufferEditor { context: context, vertex_array: vertex_array }
}<commit_msg>Make index data functions specialized. Not sure if good idea?<commit_after>
use super::Context;
use super::buffer::VertexBuffer;
use super::vertexarray::VertexArray;

pub struct VertexBufferEditor<'a> {
    #[allow(dead_code)]
    context: &'a mut Context,
    vertex_buffer: &'a VertexBuffer
}

impl<'a> VertexBufferEditor<'a> {
    pub fn data<D>(&mut self, data: &[D]) {
        self.context.vbo_tracker.bind(self.vertex_buffer);
        self.vertex_buffer.data(data);
    }

    pub fn sub_data<D>(&mut self, data: &[D], offset: uint) {
        self.context.vbo_tracker.bind(self.vertex_buffer);
        self.vertex_buffer.sub_data(data, offset);
    }
}

pub fn new_vertex_buffer_editor<'a>(context: &'a mut Context, vertex_buffer: &'a VertexBuffer) -> VertexBufferEditor<'a> {
    VertexBufferEditor { context: context, vertex_buffer: vertex_buffer }
}

pub struct IndexBufferEditor<'a> {
    #[allow(dead_code)]
    context: &'a mut Context,
    vertex_array: &'a VertexArray
}

impl<'a> IndexBufferEditor<'a> {
    pub fn data_u8(&mut self, data: &[u8]) {
        self.data(data);
    }

    pub fn data_u16(&mut self, data: &[u16]) {
        self.data(data);
    }

    pub fn data_u32(&mut self, data: &[u32]) {
        self.data(data);
    }

    pub fn sub_data_u8(&mut self, data: &[u8], offset: uint) {
        self.sub_data(data, offset);
    }

    pub fn sub_data_u16(&mut self, data: &[u16], offset: uint) {
        self.sub_data(data, offset);
    }

    pub fn sub_data_u32(&mut self, data: &[u32], offset: uint) {
        self.sub_data(data, offset);
    }

    fn data<D>(&mut self, data: &[D]) {
        self.context.vao_tracker.bind(self.vertex_array);
        if let Some(ref index_buffer) = self.vertex_array.index_buffer() {
            index_buffer.data(data);
        }
    }

    fn sub_data<D>(&mut self, data: &[D], offset: uint) {
        self.context.vao_tracker.bind(self.vertex_array);
        if let Some(ref index_buffer) = self.vertex_array.index_buffer() {
            index_buffer.sub_data(data, offset);
        }
    }
}

pub fn new_index_buffer_editor<'a>(context: &'a mut Context, vertex_array: &'a VertexArray) -> IndexBufferEditor<'a> {
    IndexBufferEditor { context: context, vertex_array: vertex_array }
}<|endoftext|>


<commit_before>//! Handles encoding of a Bitterlemon image

extern crate arrayvec;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Run {
	Set(u8),
	Clear(u8)
}

impl Run {
	fn size(&self) -> u8 {
		match *self {
			Run::Set(x) => x,
			Run::Clear(x) => x,
		}
	}

	fn size_mut<'a>(&'a mut self) -> &'a mut u8 {
		match *self {
			Run::Set(ref mut x) => x,
			Run::Clear(ref mut x) => x,
		}
	}
}

#[cfg(test)]
mod run_tests {

	use super::Run::*;

	#[test]
	fn size() {
		assert_eq!(50, Set(50).size());
		assert_eq!(50, Clear(50).size());
		assert_eq!(Set(50).size(), Clear(50).size());
	}

	#[test]
	fn size_mut() {
		macro_rules! implement {
			($e:ident) => (
				let mut run = super::Run::$e(20);
				assert_eq!(20, *run.size_mut());

				*run.size_mut() += 2;
				assert_eq!($e(22), run);
			)
		}

		implement!(Set);
		implement!(Clear);
	}
}

impl Into<u8> for Run {
	fn into(self) -> u8 {

		use self::Run::*;

		match self {
			Set(n)   => {
				assert!(n <= RLE_MAX_RUN);
				let n_out = if n == RLE_MAX_RUN {0u8} else {n};
				0xc0u8 + n_out
			},
			Clear(n) => {
				assert!(n <= RLE_MAX_RUN);
				let n_out = if n == RLE_MAX_RUN {0u8} else {n};
				0x80u8 + n_out
			},
		}

	}
}

#[derive(Debug)]
pub struct RunIterator<S> {
	state: Option<Run>,
	source: S,
}

fn rle_new_run(pp: bool) -> Run {
	if pp {Run::Set(1)} else {Run::Clear(1)}
}

const RLE_MAX_RUN: u8 = 64;

impl<S: Iterator<Item=bool>> RunIterator<S> {

	pub fn from_pixels(source: S) -> RunIterator<S> {
		RunIterator {
			state: None,
			source: source
		}
	}
}

impl<S: Iterator<Item=bool>> Iterator for RunIterator<S> {
	type Item = Run;

	fn next(&mut self) -> Option<Run> {
		use self::Run::*;
		loop {

			// Grab next bit from source; early return if None upstream
			let pp = match self.source.next() {
				Some(b) => b,
				None    => return match self.state {
					Some(x) => {
						// Push out current state and clear for next time
						let old = x;
						self.state = None;
						Some(old)
					},
					// All done
					None => None
				}
			};

			match self.state {

				// Can't fit any more in this run
				Some(Set(RLE_MAX_RUN)) => {
					self.state = Some(rle_new_run(pp));
					return Some(Set(RLE_MAX_RUN));
				},

				// Can't fit any more in this run
				Some(Clear(RLE_MAX_RUN)) => {
					self.state = Some(rle_new_run(pp));
					return Some(Clear(RLE_MAX_RUN));
				}

				// Increase existing Set run
				Some(Set(count)) if pp => {
					self.state = Some(Set(count + 1));
					continue;
				},

				// Swap from Set to Clear
				Some(Set(_)) if ! pp => {
					let old = self.state;
					self.state = Some(Clear(1));
					return old;
				},

				// Increase existing Clear run
				Some(Clear(count)) if ! pp => {
					self.state = Some(Clear(count + 1));
					continue;
				},

				// Swap from Clear to Set
				Some(Clear(_)) if pp => {
					let old = self.state;
					self.state = Some(Set(1));
					return old;
				},

				// All cases covered, but can't convince rustc
				Some(_) => {
					unreachable!();
				}

				None => {
					self.state = Some(rle_new_run(pp));
					continue;
				}
			}

		}

	}
}

type RunHolding = arrayvec::ArrayVec<[Run; 128]>;

#[derive(Debug, PartialEq, Eq)]
pub struct WithFrames<S> {
	runs: RunHolding,
	source: S
}

impl<S: Iterator<Item=Run>> WithFrames<S> {
	pub fn new(source: S) -> WithFrames<S> {
		WithFrames {
			runs: RunHolding::new(),
			source: source,
		}
	}
}

fn bytes_as_frame(runs: &RunHolding) -> usize {
	let total_bits: usize = runs.iter().map(|&run| match run {
		Run::Set(x) => x as usize,
		Run::Clear(x) => x as usize,
	}).sum();

	(total_bits + 15) >> 3 // round up to next byte, add header
}

fn bytes_as_runs(runs: &RunHolding) -> usize {
	// runs in bytes are next highest multiples of 64
	runs.iter().map(|&run| {
		let found = match run {
			Run::Set(x) => x,
			Run::Clear(x) => x,
		};
		debug_assert!(found > 0);
		(found + 63) >> 6
	} as usize).sum()
}

#[cfg(test)]
mod test_frame_sizer {

	use super::*;
	use super::Run::*;

	fn op(input: &[Run], frame_size: usize, run_size: usize) {
		let mut builder = RunHolding::new();

		for element in input {
			assert_eq!(None, builder.push(element.clone()));
		}

		assert_eq!(frame_size, bytes_as_frame(&builder));
		assert_eq!(run_size, bytes_as_runs(&builder));
	}

	#[test]
	fn empty() {
		op(&[], 1, 0);
	}

	#[test]
	fn single_set() {
		op(&[Set(1)], 2, 1);
	}

	#[test]
	fn single_clear() {
		op(&[Clear(1)], 2, 1);
	}
	#[test]
	fn enough_to_overflow_a_frame_to_2_post_header_bytes() {
		op(&[Set(5), Clear(5)], 3, 2);
	}

	#[test]
	fn can_pack_3_runs_into_a_single_frame_byte() {
		op(&[Clear(3), Set(2), Clear(3)], 2, 3);
	}
}

impl<S> Iterator for WithFrames<S> {
	type Item = u8;

	fn next(&mut self) -> Option<Self::Item> {
		unimplemented!();
	}

}

#[cfg(test)]
mod test_runs {

	use std::iter::Iterator;

	use super::{Run, RunIterator};
	use super::Run::*;

	#[test]
	fn runs() {

		let op = |input: &[u8], output: &[Run]| {

			let bool_stream : Vec<bool> = input.iter().map(|&c| c == b'1').collect();
			let collected : Vec<Run> = RunIterator::from_pixels(bool_stream.iter().map(|&b| b)).collect();

			assert_eq!(output, collected.as_slice());
		};

		// simple single runs
		op(b"1", &[Set(1)]);
		op(b"1111", &[Set(4)]);
		op(b"00", &[Clear(2)]);
		op(b"", &[]);

		// alterations
		op(b"011", &[Clear(1), Set(2)]);
		op(b"110", &[Set(2), Clear(1)]);
		op(b"0101", &[Clear(1), Set(1), Clear(1), Set(1)]);

		// run size limits
		let large_set = [b'1'; (super::RLE_MAX_RUN + 1) as usize];
		op(&large_set, &[Set(super::RLE_MAX_RUN), Set(1)]);

		// clear size limits
		let large_clear = [b'0' ; (super::RLE_MAX_RUN + 1) as usize];
		op(&large_clear, &[Clear(super::RLE_MAX_RUN), Clear(1)]);
	}
}

<commit_msg>Beginnings of Frame wrapping<commit_after>//! Handles encoding of a Bitterlemon image

extern crate arrayvec;

use std::iter;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Run {
	Set(u8),
	Clear(u8)
}

impl Run {
	fn size(&self) -> u8 { // TODO rename this to len for Rusticity
		match *self {
			Run::Set(x) => x,
			Run::Clear(x) => x,
		}
	}

	fn size_mut<'a>(&'a mut self) -> &'a mut u8 {
		match *self {
			Run::Set(ref mut x) => x,
			Run::Clear(ref mut x) => x,
		}
	}

	fn bit(&self) -> u8 {
		match *self {
			Run::Set(_) => 1,
			Run::Clear(_) => 0,
		}
	}
}

#[cfg(test)]
mod test_runs {

	use super::Run::*;

	#[test]
	fn size() {
		assert_eq!(50, Set(50).size());
		assert_eq!(50, Clear(50).size());
		assert_eq!(Set(50).size(), Clear(50).size());
	}

	#[test]
	fn size_mut() {
		macro_rules! implement {
			($e:ident) => (
				let mut run = super::Run::$e(20);
				assert_eq!(20, *run.size_mut());

				*run.size_mut() += 2;
				assert_eq!($e(22), run);
			)
		}

		implement!(Set);
		implement!(Clear);
	}
}

impl Into<u8> for Run {
	fn into(self) -> u8 {

		use self::Run::*;

		match self {
			Set(n)   => {
				assert!(n <= RLE_MAX_RUN);
				let n_out = if n == RLE_MAX_RUN {0u8} else {n};
				0xc0u8 + n_out
			},
			Clear(n) => {
				assert!(n <= RLE_MAX_RUN);
				let n_out = if n == RLE_MAX_RUN {0u8} else {n};
				0x80u8 + n_out
			},
		}

	}
}

#[derive(Debug)]
pub struct RunIterator<S> {
	state: Option<Run>,
	source: S,
}

fn rle_new_run(pp: bool) -> Run {
	if pp {Run::Set(1)} else {Run::Clear(1)}
}

const RLE_MAX_RUN: u8 = 64;

impl<S: Iterator<Item=bool>> RunIterator<S> {

	pub fn from_pixels(source: S) -> RunIterator<S> {
		RunIterator {
			state: None,
			source: source
		}
	}
}

impl<S: Iterator<Item=bool>> Iterator for RunIterator<S> {
	type Item = Run;

	fn next(&mut self) -> Option<Run> {
		use self::Run::*;
		loop {

			// Grab next bit from source; early return if None upstream
			let pp = match self.source.next() {
				Some(b) => b,
				None    => return match self.state {
					Some(x) => {
						// Push out current state and clear for next time
						let old = x;
						self.state = None;
						Some(old)
					},
					// All done
					None => None
				}
			};

			match self.state {

				// Can't fit any more in this run
				Some(Set(RLE_MAX_RUN)) => {
					self.state = Some(rle_new_run(pp));
					return Some(Set(RLE_MAX_RUN));
				},

				// Can't fit any more in this run
				Some(Clear(RLE_MAX_RUN)) => {
					self.state = Some(rle_new_run(pp));
					return Some(Clear(RLE_MAX_RUN));
				}

				// Increase existing Set run
				Some(Set(count)) if pp => {
					self.state = Some(Set(count + 1));
					continue;
				},

				// Swap from Set to Clear
				Some(Set(_)) if ! pp => {
					let old = self.state;
					self.state = Some(Clear(1));
					return old;
				},

				// Increase existing Clear run
				Some(Clear(count)) if ! pp => {
					self.state = Some(Clear(count + 1));
					continue;
				},

				// Swap from Clear to Set
				Some(Clear(_)) if pp => {
					let old = self.state;
					self.state = Some(Set(1));
					return old;
				},

				// All cases covered, but can't convince rustc
				Some(_) => {
					unreachable!();
				}

				None => {
					self.state = Some(rle_new_run(pp));
					continue;
				}
			}

		}

	}
}

#[cfg(test)]
mod test_run_builder {

	use std::iter::Iterator;

	use super::{Run, RunIterator};
	use super::Run::*;

	#[test]
	fn runs() {

		let op = |input: &[u8], output: &[Run]| {

			let bool_stream : Vec<bool> = input.iter().map(|&c| c == b'1').collect();
			let collected : Vec<Run> = RunIterator::from_pixels(bool_stream.iter().map(|&b| b)).collect();

			assert_eq!(output, collected.as_slice());
		};

		// simple single runs
		op(b"1", &[Set(1)]);
		op(b"1111", &[Set(4)]);
		op(b"00", &[Clear(2)]);
		op(b"", &[]);

		// alterations
		op(b"011", &[Clear(1), Set(2)]);
		op(b"110", &[Set(2), Clear(1)]);
		op(b"0101", &[Clear(1), Set(1), Clear(1), Set(1)]);

		// run size limits
		let large_set = [b'1'; (super::RLE_MAX_RUN + 1) as usize];
		op(&large_set, &[Set(super::RLE_MAX_RUN), Set(1)]);

		// clear size limits
		let large_clear = [b'0' ; (super::RLE_MAX_RUN + 1) as usize];
		op(&large_clear, &[Clear(super::RLE_MAX_RUN), Clear(1)]);
	}
}

type RunHolding = arrayvec::ArrayVec<[Run; 128]>;

trait RunHoldingExtensions {
	fn bytes_as_frame(&self) -> (u8, u8);
	fn bytes_as_runs(&self) -> u16;
	fn num_pixels(&self) -> u8;
}

impl RunHoldingExtensions for RunHolding {
	fn bytes_as_frame(&self) -> (u8, u8) {
		let total_bits: usize = self.iter().map(|&run| match run {
			Run::Set(x) => x as usize,
			Run::Clear(x) => x as usize,
		}).sum();

		let total_bits = total_bits + 8; // for the header
		let bytes = ((total_bits + 7) >> 3) as u8;
		let padding = (total_bits & 7) as u8;
		(bytes, padding)
	}

	fn bytes_as_runs(&self) -> u16 {
		// runs in bytes are next highest multiples of 64
		self.iter().map(|&run| {
			let found = match run {
				Run::Set(x) => x,
				Run::Clear(x) => x,
			};
			debug_assert!(found > 0);
			(found + 63) >> 6
		} as u16).sum()
	}

	fn num_pixels(&self) -> u8 {
		self.iter().map(Run::size).sum()
	}
}

#[cfg(test)]
mod run_holding_extensions {

	use super::*;
	use super::Run::*;

	fn op(input: &[Run], frame_size: u8, run_size: u16) {
		let mut builder = RunHolding::new();

		for element in input {
			assert_eq!(None, builder.push(element.clone()));
		}

		assert_eq!(frame_size, builder.bytes_as_frame().0);
		assert_eq!(run_size, builder.bytes_as_runs());
	}

	#[test]
	fn empty() {
		op(&[], 1, 0);
	}

	#[test]
	fn single_set() {
		op(&[Set(1)], 2, 1);
	}

	#[test]
	fn single_clear() {
		op(&[Clear(1)], 2, 1);
	}
	#[test]
	fn enough_to_overflow_a_frame_to_2_post_header_bytes() {
		op(&[Set(5), Clear(5)], 3, 2);
	}

	#[test]
	fn can_pack_3_runs_into_a_single_frame_byte() {
		op(&[Clear(3), Set(2), Clear(3)], 2, 3);
	}
}

#[derive(Debug, PartialEq, Eq)]
pub struct WithFrames<S> {
	runs: RunHolding,
	mode: WithFramesMode,
	source: S,
	next_run: Option<Run>,
}

#[derive(Debug, PartialEq, Eq)]
enum WithFramesMode {
	Filling,
	FlushingFrame(u8, u8), // ptr, length
}

impl<S: Iterator<Item=Run>> WithFrames<S> {
	pub fn new(source: S) -> WithFrames<S> {
		WithFrames {
			runs: RunHolding::new(),
			mode: WithFramesMode::Filling,
			source: source,
			next_run: None,
		}
	}

	fn next_should_expand_frame(&mut self) -> bool {
		let run_size = self.next_run.unwrap().size();

		// let's get some clear cases out of the way first
		if run_size < 8 { return true; }
		if run_size >= 16 { return false; }

		let (_, padding) = self.runs.bytes_as_frame();
		// only add to the frame if the frame size increase is < 2 bytes
		// IOW, run size - frame padding < 16 bits
		run_size - padding < 16
	}

	fn next_add_to_frame(&mut self)  {
		let next_run = self.next_run.take().unwrap();
		self.runs.push(next_run);
	}

	fn next_continue_purge(&mut self) -> Option<<Self as Iterator>::Item> {
		println!("{:?}", self.mode);
		let (to_return, next_mode) : (Option<u8>, Option<WithFramesMode>) = match self.mode {
			WithFramesMode::Filling => {

				if let Some(_) = self.next_run {
					// expecting to fill a frame, but was told not to do it here
					// move the run out instead
					let moved_run = self.next_run.take().unwrap().into();
					(Some(moved_run), None)
				}
				else if self.runs.len() > 0 {
					// return header for new frame to output
					// and prime next mode to be WithFramesMode::FlushingFrame
					(Some(self.runs.num_pixels()), Some(WithFramesMode::FlushingFrame(0, 0)))
				}
				else {
					// if here, must have previously purged the frame that finishes all iteration
					(None, None)
				}
			},
			WithFramesMode::FlushingFrame(ref mut ptr, ref size) => {
				// mid-frame
				let mut run = self.runs.get(*ptr as usize).unwrap().clone(); // clone to eliminate borrow of self.runs

				// drain run to fill a byte
				let mut byte = 0u8;
				for _ in 0..8 {
					byte = (byte << 1) | run.bit();
					let mut run_size = run.size_mut();
					*run_size -= 1;
					if *run_size == 0 {
						// we exhausted this run filling a byte, try the next
						*ptr += 1;
						if ptr == run_size {
							// oops, nothing left
							break;
						}
					}
				}

				let next_mode = if ptr == size {
					self.runs = RunHolding::new(); // reset run holder
					Some(WithFramesMode::Filling)
				} else { None };
				(Some(byte), next_mode)
			},
		};

		if let Some(next_mode) = next_mode {
			self.mode = next_mode;
		}

		to_return
	}
}

impl<S: Iterator<Item=Run>> Iterator for WithFrames<S> {
	type Item = u8;

	fn next(&mut self) -> Option<Self::Item> {
		// check next run size.
		// if size < 8:
		//   add run to current frame
		// if 8 <= size < 16:
		//   let increase = see how much the frame would increase by, in bytes if you added this run
		//   if increase > 1:
		//     flush current frame, allowing next run to part-pour into frame padding
		//   else:
		//     add run to current frame
		// else:
		//   flush current frame

		loop {

			// get next element, if there is one
			if self.next_run.is_none() {
				self.next_run = self.source.next();
			}

			if self.next_run.is_none() {
				return self.next_continue_purge();
			}

			if self.next_should_expand_frame() {
				self.next_add_to_frame();
				continue;
			}

			return self.next_continue_purge();
		}
	}
}

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

	macro_rules! from_these {
		($slice:expr) => {
			WithFrames::new(($slice as &[Run]).into_iter().cloned())
		}
	}

	#[test]
	fn empty_frame() {
		let mut src = from_these!(&[]);
		assert!(src.next() == None);
	}
}
<|endoftext|>