<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for FreeBSD
extern crate std;

use crate::Error;
use std::io;
use core::ptr;
use core::num::NonZeroU32;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    let mib = [libc::CTL_KERN, libc::KERN_ARND];
    // kern.arandom permits a maximum buffer size of 256 bytes
    for chunk in dest.chunks_mut(256) {
        let mut len = chunk.len();
        let ret = unsafe {
            libc::sysctl(
                mib.as_ptr(), mib.len() as libc::c_uint,
                chunk.as_mut_ptr() as *mut _, &mut len, ptr::null(), 0,
            )
        };
        if ret == -1 || len != chunk.len() {
            error!("freebsd: kern.arandom syscall failed");
            return Err(io::Error::last_os_error().into());
        }
    }
    Ok(())
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
<commit_msg>freebsd: Increase consistancy with libc implementation (#36)<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for FreeBSD
extern crate std;

use crate::Error;
use std::io;
use core::ptr;
use core::num::NonZeroU32;

fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
    static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
    let mut len = buf.len();
    let ret = unsafe {
        libc::sysctl(
            MIB.as_ptr(),
            MIB.len() as libc::c_uint,
            buf.as_mut_ptr() as *mut _,
            &mut len,
            ptr::null(),
            0,
        )
    };
    if ret == -1 {
        error!("freebsd: kern.arandom syscall failed");
        return Err(io::Error::last_os_error().into());
    }
    Ok(len)
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    let mut start = 0;
    while start < dest.len() {
        start += kern_arnd(&mut dest[start..])?;
    }
    Ok(())
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
<|endoftext|>


<commit_before>//! JSON and serialization
//!
//! # What is JSON?
//!
//! JSON (JavaScript Object Notation) is a way to write data in JavaScript.  Like XML, it allows to
//! encode structured data in a text format that can be easily read by humans.  Its simple syntax
//! and native compatibility with JavaScript have made it a widely used format.
//!
//! Data types that can be encoded are JavaScript types (see the `serde_json:Value` enum for more
//! details):
//!
//! * `Boolean`: equivalent to rust's `bool`
//! * `I64`: equivalent to rust's `i64`
//! * `U64`: equivalent to rust's `u64`
//! * `F64`: equivalent to rust's `i64`
//! * `String`: equivalent to rust's `String`
//! * `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the
//!    same array
//! * `Object`: equivalent to rust's `BTreeMap<String, serde_json::Value>`
//! * `Null`
//!
//! An object is a series of string keys mapping to values, in `"key": value` format.  Arrays are
//! enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).  A simple JSON
//! document encoding a person, his/her age, address and phone numbers could look like
//!
//! ```ignore
//! {
//!     "FirstName": "John",
//!     "LastName": "Doe",
//!     "Age": 43,
//!     "Address": {
//!         "Street": "Downing Street 10",
//!         "City": "London",
//!         "Country": "Great Britain"
//!     },
//!     "PhoneNumbers": [
//!         "+44 1234567",
//!         "+44 2345678"
//!     ]
//! }
//! ```
//! If we assume that FirstName is optional and all other fields are mandatory, the above JSON could correspond to the following Rust structs:
//! ```rust
//! #[derive(Serialize, Deserialize)]
//! struct Data {
//!     FirstName: Option<String>,
//!     LastName: String,
//!     Age: u32,
//!     Address: Address,
//!     PhoneNumbers: Vec<String>
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Address {
//!     Street: String,
//!     City: String,
//!     Country: String
//! }
//! ```
//!
//! # Type-based Serialization and Deserialization
//!
//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
//! from JSON via the serialization API.  To be able to serialize a piece of data, it must implement
//! the `serde::Serialize` trait.  To be able to deserialize a piece of data, it must implement the
//! `serde::Deserialize` trait.  Serde provides provides an annotation to automatically generate
//! the code for these traits: `#[derive(Serialize, Deserialize)]`.
//!
//! The JSON API also provides an enum `serde_json::Value` and a method `to_value` to serialize
//! objects.  A `serde_json::Value` value can be serialized as a string or buffer using the
//! functions described above.  You can also use the `json::Serializer` object, which implements the
//! `Serializer` trait.
//!
//! # Examples of use
//!
//! ## Parsing a `str` to `Value` and reading the result
//!
//! ```rust
//! //#![feature(custom_derive, plugin)]
//! //#![plugin(serde_macros)]
//!
//! extern crate serde_json;
//!
//! use serde_json::Value;
//!
//! fn main() {
//!     let data: Value = serde_json::from_str("{\"foo\": 13, \"bar\": \"baz\"}").unwrap();
//!     println!("data: {:?}", data);
//!     // data: {"bar":"baz","foo":13}
//!     println!("object? {}", data.is_object());
//!     // object? true
//!
//!     let obj = data.as_object().unwrap();
//!     let foo = obj.get("foo").unwrap();
//!
//!     println!("array? {:?}", foo.as_array());
//!     // array? None
//!     println!("u64? {:?}", foo.as_u64());
//!     // u64? Some(13u64)
//!
//!     for (key, value) in obj.iter() {
//!         println!("{}: {}", key, match *value {
//!             Value::U64(v) => format!("{} (u64)", v),
//!             Value::String(ref v) => format!("{} (string)", v),
//!             _ => format!("other")
//!         });
//!     }
//!     // bar: baz (string)
//!     // foo: 13 (u64)
//! }
//! ```

#![deny(missing_docs)]

extern crate num;
extern crate serde;

pub use self::de::{
    Deserializer,
    from_iter,
    from_reader,
    from_slice,
    from_str,
};
pub use self::error::{Error, ErrorCode, Result};
pub use self::ser::{
    Serializer,
    to_writer,
    to_writer_pretty,
    to_vec,
    to_vec_pretty,
    to_string,
    to_string_pretty,
    escape_str,
};
pub use self::value::{Value, to_value, from_value};

pub mod builder;
pub mod de;
pub mod error;
pub mod ser;
pub mod value;
<commit_msg>docs(typos): Add necessary whitespace, add renaming example<commit_after>//! JSON and serialization
//!
//! # What is JSON?
//!
//! JSON (JavaScript Object Notation) is a way to write data in JavaScript.  Like XML, it allows to
//! encode structured data in a text format that can be easily read by humans.  Its simple syntax
//! and native compatibility with JavaScript have made it a widely used format.
//!
//! Data types that can be encoded are JavaScript types (see the `serde_json:Value` enum for more
//! details):
//!
//! * `Boolean`: equivalent to rust's `bool`
//! * `I64`: equivalent to rust's `i64`
//! * `U64`: equivalent to rust's `u64`
//! * `F64`: equivalent to rust's `i64`
//! * `String`: equivalent to rust's `String`
//! * `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the
//!    same array
//! * `Object`: equivalent to rust's `BTreeMap<String, serde_json::Value>`
//! * `Null`
//!
//! An object is a series of string keys mapping to values, in `"key": value` format.  Arrays are
//! enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).  A simple JSON
//! document encoding a person, his/her age, address and phone numbers could look like
//!
//! ```ignore
//! {
//!     "FirstName": "John",
//!     "LastName": "Doe",
//!     "Age": 43,
//!     "Address": {
//!         "Street": "Downing Street 10",
//!         "City": "London",
//!         "Country": "Great Britain"
//!     },
//!     "PhoneNumbers": [
//!         "+44 1234567",
//!         "+44 2345678"
//!     ]
//! }
//! ```
//!
//! If we assume that FirstName is optional and all other fields are mandatory, the above JSON could
//! correspond to the following Rust structs:
//!
//! ```rust
//! #[derive(Serialize, Deserialize)]
//! struct Data {
//!     #[serde(rename="FirstName")] // to comply with Rust coding standards
//!     first_name: Option<String>,
//!     LastName: String,
//!     Age: u32,
//!     Address: Address,
//!     PhoneNumbers: Vec<String>
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Address {
//!     Street: String,
//!     City: String,
//!     Country: String
//! }
//! ```
//!
//! # Type-based Serialization and Deserialization
//!
//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
//! from JSON via the serialization API.  To be able to serialize a piece of data, it must implement
//! the `serde::Serialize` trait.  To be able to deserialize a piece of data, it must implement the
//! `serde::Deserialize` trait.  Serde provides provides an annotation to automatically generate
//! the code for these traits: `#[derive(Serialize, Deserialize)]`.
//!
//! The JSON API also provides an enum `serde_json::Value` and a method `to_value` to serialize
//! objects.  A `serde_json::Value` value can be serialized as a string or buffer using the
//! functions described above.  You can also use the `json::Serializer` object, which implements the
//! `Serializer` trait.
//!
//! # Examples of use
//!
//! ## Parsing a `str` to `Value` and reading the result
//!
//! ```rust
//! //#![feature(custom_derive, plugin)]
//! //#![plugin(serde_macros)]
//!
//! extern crate serde_json;
//!
//! use serde_json::Value;
//!
//! fn main() {
//!     let data: Value = serde_json::from_str("{\"foo\": 13, \"bar\": \"baz\"}").unwrap();
//!     println!("data: {:?}", data);
//!     // data: {"bar":"baz","foo":13}
//!     println!("object? {}", data.is_object());
//!     // object? true
//!
//!     let obj = data.as_object().unwrap();
//!     let foo = obj.get("foo").unwrap();
//!
//!     println!("array? {:?}", foo.as_array());
//!     // array? None
//!     println!("u64? {:?}", foo.as_u64());
//!     // u64? Some(13u64)
//!
//!     for (key, value) in obj.iter() {
//!         println!("{}: {}", key, match *value {
//!             Value::U64(v) => format!("{} (u64)", v),
//!             Value::String(ref v) => format!("{} (string)", v),
//!             _ => format!("other")
//!         });
//!     }
//!     // bar: baz (string)
//!     // foo: 13 (u64)
//! }
//! ```

#![deny(missing_docs)]

extern crate num;
extern crate serde;

pub use self::de::{
    Deserializer,
    from_iter,
    from_reader,
    from_slice,
    from_str,
};
pub use self::error::{Error, ErrorCode, Result};
pub use self::ser::{
    Serializer,
    to_writer,
    to_writer_pretty,
    to_vec,
    to_vec_pretty,
    to_string,
    to_string_pretty,
    escape_str,
};
pub use self::value::{Value, to_value, from_value};

pub mod builder;
pub mod de;
pub mod error;
pub mod ser;
pub mod value;
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

const CARGO_FILE_NAME: &str = "Cargo.toml";

use crate::project::VutError;
use crate::util;
use crate::version::Version;
use crate::version_source::VersionSource;

use toml_edit;

pub struct CargoSource {
    pub path: PathBuf,
    pub cargo_file_path: PathBuf,
}

impl CargoSource {
    pub fn from_path(path: &Path) -> Option<Self> {
        let cargo_file_path = path.join(CARGO_FILE_NAME);

        if cargo_file_path.exists() {
            Some(Self {
                path: path.to_path_buf(),
                cargo_file_path,
            })
        } else {
            None
        }
    }

    fn read_cargo_file(&self) -> Result<String, VutError> {
        let mut file = util::open_file(&self.cargo_file_path).map_err(VutError::VersionFileOpen)?;

        let mut toml_str = String::new();

        file.read_to_string(&mut toml_str).map_err(VutError::VersionFileRead)?;

        Ok(toml_str)
    }

    fn write_cargo_file(&mut self, toml_str: &str) -> Result<(), VutError> {
        let mut file = util::create_file(&self.cargo_file_path).map_err(VutError::VersionFileOpen)?;

        file.write(toml_str.as_bytes()).map_err(VutError::VersionFileWrite)?;

        Ok(())
    }
}

impl VersionSource for CargoSource {
    fn get_path(&self) -> &Path {
        &self.path
    }

    fn exists(&self) -> bool {
        self.cargo_file_path.exists()
    }

    fn get_version(&self) -> Result<Version, VutError> {
        let version_str = {
            // Read TOML from cargo file
            let toml_str = self.read_cargo_file()?;

            // Parse as document
            let doc = toml_str
                .parse::<toml_edit::Document>()
                .map_err(|err| VutError::Other(Cow::Owned(err.to_string())))?;

            // Get version string
            let version_str = doc["package"]["version"].as_str().unwrap();

            version_str.to_owned()
        };

        // Parse version string
        let version = version_str.parse().map_err(|err| VutError::Other(Cow::Owned(err)))?;

        Ok(version)
    }

    fn set_version(&mut self, version: &Version) -> Result<(), VutError> {
        // Read TOML from cargo file
        let toml_str = self.read_cargo_file()?;

        // Parse as document
        let mut doc = toml_str
            .parse::<toml_edit::Document>()
            .map_err(|err| VutError::Other(Cow::Owned(err.to_string())))?;

        // Replace version number
        doc["package"]["version"] = toml_edit::value(version.to_string());

        // Serialize updated document to string
        let toml_str = doc.to_string();

        // Overwrite cargo file
        self.write_cargo_file(&toml_str)?;

        Ok(())
    }
}
<commit_msg>fix: Don't try to update Cargo.toml version sources with no [package] section<commit_after>use std::borrow::Cow;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

const CARGO_FILE_NAME: &str = "Cargo.toml";

use crate::project::VutError;
use crate::util;
use crate::version::Version;
use crate::version_source::VersionSource;

use toml_edit;

pub struct CargoSource {
    pub path: PathBuf,
    pub cargo_file_path: PathBuf,
}

impl CargoSource {
    pub fn from_path(path: &Path) -> Option<Self> {
        let cargo_file_path = path.join(CARGO_FILE_NAME);

        if cargo_file_path.exists() {
            Some(Self {
                path: path.to_path_buf(),
                cargo_file_path,
            })
        } else {
            None
        }
    }

    fn read_cargo_file(&self) -> Result<String, VutError> {
        let mut file = util::open_file(&self.cargo_file_path).map_err(VutError::VersionFileOpen)?;

        let mut toml_str = String::new();

        file.read_to_string(&mut toml_str).map_err(VutError::VersionFileRead)?;

        Ok(toml_str)
    }

    fn write_cargo_file(&mut self, toml_str: &str) -> Result<(), VutError> {
        let mut file = util::create_file(&self.cargo_file_path).map_err(VutError::VersionFileOpen)?;

        file.write(toml_str.as_bytes()).map_err(VutError::VersionFileWrite)?;

        Ok(())
    }
}

impl VersionSource for CargoSource {
    fn get_path(&self) -> &Path {
        &self.path
    }

    fn exists(&self) -> bool {
        self.cargo_file_path.exists()
    }

    fn get_version(&self) -> Result<Version, VutError> {
        let version_str = {
            // Read TOML from cargo file
            let toml_str = self.read_cargo_file()?;

            // Parse as document
            let doc = toml_str
                .parse::<toml_edit::Document>()
                .map_err(|err| VutError::Other(Cow::Owned(err.to_string())))?;

            // Get version string
            let version_str = doc["package"]["version"].as_str().unwrap();

            version_str.to_owned()
        };

        // Parse version string
        let version = version_str.parse().map_err(|err| VutError::Other(Cow::Owned(err)))?;

        Ok(version)
    }

    fn set_version(&mut self, version: &Version) -> Result<(), VutError> {
        // Read TOML from cargo file
        let toml_str = self.read_cargo_file()?;

        // Parse as document
        let mut doc = toml_str
            .parse::<toml_edit::Document>()
            .map_err(|err| VutError::Other(Cow::Owned(err.to_string())))?;

        // If the file does not contain a [package] section, don't try to update version.
        // This will typically be the case if the Cargo.toml is a workspace.
        if !doc.as_table().contains_key("package") {
            return Ok(());
        }

        // Replace version number
        doc["package"]["version"] = toml_edit::value(version.to_string());

        // Serialize updated document to string
        let toml_str = doc.to_string();

        // Overwrite cargo file
        self.write_cargo_file(&toml_str)?;

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


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

extern mod extra;
extern mod png;
extern mod std;

use std::io;
use std::io::{File, Reader};
use std::io::process::ExitStatus;
use std::num::abs;
use std::os;
use std::run::{Process, ProcessOptions};
use std::str;
use extra::test::{DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use extra::test::run_tests_console;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println("error: at least one reftest list must be given");
        os::set_exit_status(1);
        return;
    }

    let tests = parse_lists(args.tail());
    let test_opts = TestOpts {
        filter: None,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
    };

    if !run_tests_console(&test_opts, tests) {
        os::set_exit_status(1);
    }
}

#[deriving(Eq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: ~str,
    kind: ReftestKind,
    left: ~str,
    right: ~str,
    id: uint,
}

fn parse_lists(filenames: &[~str]) -> ~[TestDescAndFn] {
    let mut tests: ~[TestDescAndFn] = ~[];
    let mut next_id = 0;
    for file in filenames.iter() {
        let file_path = Path::new(file.clone());
        let contents = match File::open_mode(&file_path, io::Open, io::Read) {
            Some(mut f) => str::from_utf8_owned(f.read_to_end()),
            None => fail!("Could not open file")
        };

        for line in contents.lines() {
            let parts: ~[&str] = line.split(' ').filter(|p| !p.is_empty()).collect();

            if parts.len() != 3 {
                fail!("reftest line: '{:s}' doesn't match 'KIND LEFT RIGHT'", line);
            }

            let kind = match parts[0] {
                "==" => Same,
                "!=" => Different,
                "#" => continue,
                _ => fail!("reftest line: '{:s}' has invalid kind '{:s}'",
                           line, parts[0])
            };
            let src_path = file_path.dir_path();
            let src_dir = src_path.display().to_str();
            let file_left =  src_dir + "/" + parts[1];
            let file_right = src_dir + "/" + parts[2];

            let reftest = Reftest {
                name: parts[1] + " / " + parts[2],
                kind: kind,
                left: file_left,
                right: file_right,
                id: next_id,
            };

            next_id += 1;

            tests.push(make_test(reftest));
        }
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn check_reftest(reftest: Reftest) {
    let left_filename = format!("/tmp/servo-reftest-{:06u}-left.png", reftest.id);
    let right_filename = format!("/tmp/servo-reftest-{:06u}-right.png", reftest.id);

    let args = ~[~"-o", left_filename.clone(), reftest.left.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    let args = ~[~"-o", right_filename.clone(), reftest.right.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    // check the pngs are bit equal
    let left = png::load_png(&from_str::<Path>(left_filename).unwrap()).unwrap();
    let right = png::load_png(&from_str::<Path>(right_filename).unwrap()).unwrap();

    let pixels: ~[u8] = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
            let a_signed = a as i8;
            let b_signed = b as i8;
            255-abs(a_signed - b_signed) as u8
        }).collect();

    if pixels.iter().any(|&a| a < 255) {
        let output = from_str::<Path>(format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id)).unwrap();

        let img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&img, &output);
        assert!(res.is_ok());

        assert!(reftest.kind == Different);
    } else {
        assert!(reftest.kind == Same);
    }
}
<commit_msg>Change the values emitted for incorrect pixels to make small errors visible<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod extra;
extern mod png;
extern mod std;

use std::io;
use std::io::{File, Reader};
use std::io::process::ExitStatus;
use std::os;
use std::run::{Process, ProcessOptions};
use std::str;
use extra::test::{DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use extra::test::run_tests_console;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println("error: at least one reftest list must be given");
        os::set_exit_status(1);
        return;
    }

    let tests = parse_lists(args.tail());
    let test_opts = TestOpts {
        filter: None,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
    };

    if !run_tests_console(&test_opts, tests) {
        os::set_exit_status(1);
    }
}

#[deriving(Eq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: ~str,
    kind: ReftestKind,
    left: ~str,
    right: ~str,
    id: uint,
}

fn parse_lists(filenames: &[~str]) -> ~[TestDescAndFn] {
    let mut tests: ~[TestDescAndFn] = ~[];
    let mut next_id = 0;
    for file in filenames.iter() {
        let file_path = Path::new(file.clone());
        let contents = match File::open_mode(&file_path, io::Open, io::Read) {
            Some(mut f) => str::from_utf8_owned(f.read_to_end()),
            None => fail!("Could not open file")
        };

        for line in contents.lines() {
            let parts: ~[&str] = line.split(' ').filter(|p| !p.is_empty()).collect();

            if parts.len() != 3 {
                fail!("reftest line: '{:s}' doesn't match 'KIND LEFT RIGHT'", line);
            }

            let kind = match parts[0] {
                "==" => Same,
                "!=" => Different,
                "#" => continue,
                _ => fail!("reftest line: '{:s}' has invalid kind '{:s}'",
                           line, parts[0])
            };
            let src_path = file_path.dir_path();
            let src_dir = src_path.display().to_str();
            let file_left =  src_dir + "/" + parts[1];
            let file_right = src_dir + "/" + parts[2];

            let reftest = Reftest {
                name: parts[1] + " / " + parts[2],
                kind: kind,
                left: file_left,
                right: file_right,
                id: next_id,
            };

            next_id += 1;

            tests.push(make_test(reftest));
        }
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn check_reftest(reftest: Reftest) {
    let left_filename = format!("/tmp/servo-reftest-{:06u}-left.png", reftest.id);
    let right_filename = format!("/tmp/servo-reftest-{:06u}-right.png", reftest.id);

    let args = ~[~"-o", left_filename.clone(), reftest.left.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    let args = ~[~"-o", right_filename.clone(), reftest.right.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    // check the pngs are bit equal
    let left = png::load_png(&from_str::<Path>(left_filename).unwrap()).unwrap();
    let right = png::load_png(&from_str::<Path>(right_filename).unwrap()).unwrap();

    let pixels: ~[u8] = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
            if (a as i8 - b as i8 == 0) {
                // White for correct
                0xFF
            } else {
                // "1100" in the RGBA channel with an error for an incorrect value
                // This results in some number of C0 and FFs, which is much more
                // readable (and distinguishable) than the previous difference-wise
                // scaling but does not require reconstructing the actual RGBA pixel.
                0xC0
            }
        }).collect();

    if pixels.iter().any(|&a| a < 255) {
        let output = from_str::<Path>(format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id)).unwrap();

        let img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&img, &output);
        assert!(res.is_ok());

        assert!(reftest.kind == Different);
    } else {
        assert!(reftest.kind == Same);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str;
use std::slice;
use nom::IResult;
use expression::Expression;
use production::Production;
use term::Term;
use parsers;
use error::Error;
use rand::{Rng, SeedableRng, StdRng, thread_rng};
use stacker;

/// A Grammar is comprised of any number of Productions
#[derive(PartialEq, Debug, Clone)]
pub struct Grammar {
    productions: Vec<Production>,
}

impl Grammar {
    /// Construct a new `Grammar`
    pub fn new() -> Grammar {
        Grammar { productions: vec![] }
    }

    /// Construct an `Grammar` from `Production`s
    pub fn from_parts(v: Vec<Production>) -> Grammar {
        Grammar { productions: v }
    }

    // Get `Grammar` by parsing a string
    pub fn from_str(s: &str) -> Result<Self, Error> {
        match parsers::grammar_complete(s.as_bytes()) {
            IResult::Done(_, o) => Ok(o),
            IResult::Incomplete(n) => Err(Error::from(n)),
            IResult::Error(e) => Err(Error::from(e)),
        }
    }

    /// Add `Production` to the `Grammar`
    pub fn add_production(&mut self, prod: Production) {
        self.productions.push(prod)
    }

    /// Remove `Production` from the `Grammar`
    pub fn remove_production(&mut self, prod: &Production) -> Option<Production> {
        if let Some(pos) = self.productions.iter().position(|x| *x == *prod) {
            Some(self.productions.remove(pos))
        } else {
            None
        }
    }

    /// Get iterator of the `Grammar`'s `Production`s
    pub fn productions_iter(&self) -> Iter {
        Iter { iterator: self.productions.iter() }
    }

    /// Get mutable iterator of the `Grammar`'s `Production`s
    pub fn productions_iter_mut(&mut self) -> IterMut {
        IterMut { iterator: self.productions.iter_mut() }
    }

    fn eval_terminal(&self, term: &Term, rng: &mut StdRng) -> Result<String, Error> {
        match *term {
            Term::Nonterminal(ref nt) => self.traverse(&nt, rng),
            Term::Terminal(ref t) => Ok(t.clone()),
        }
    }

    fn traverse(&self, ident: &String, rng: &mut StdRng) -> Result<String, Error> {
        let stack_red_zone: usize = 32 * 1024; // 32KB
        // heavy recursion happening, we've hit out tolerable threshold
        if stacker::remaining_stack() < stack_red_zone {
            return Err(Error::RecursionLimit(format!(
                "Limit for recursion reached processing <{}>!",
                ident
            )));
        }

        let nonterm = Term::Nonterminal(ident.clone());
        let production;
        let find_lhs = self.productions_iter().find(|&x| x.lhs == nonterm);

        match find_lhs {
            Some(p) => production = p,
            None => return Ok(nonterm.to_string()),
        }

        let expression;
        let expressions = production.rhs_iter().collect::<Vec<&Expression>>();

        match rng.choose(&expressions) {
            Some(e) => expression = e.clone(),
            None => {
                return Err(Error::GenerateError(
                    String::from("Couldn't select random Expression!"),
                ));
            }
        }

        let mut result = String::new();
        for term in expression.terms_iter() {
            match self.eval_terminal(&term, rng) {
                Ok(s) => result = result + &s,
                Err(e) => return Err(e),
            }
        }

        return Ok(result);
    }

    /// Generate a random sentence from self and seed for random.
    /// Use if interested in reproducing the output generated.
    /// Begins from lhs of first production.
    ///
    /// # Example
    ///
    /// ```rust
    /// extern crate bnf;
    /// extern crate rand;
    /// use rand::{SeedableRng, StdRng};
    /// use bnf::Grammar;
    ///
    /// fn main() {
    ///     let input =
    ///         "<dna> ::= <base> | <base> <dna>
    ///         <base> ::= \"A\" | \"C\" | \"G\" | \"T\"";
    ///     let grammar = Grammar::from_str(input).unwrap();
    ///     let seed: &[_] = &[1,2,3,4];
    ///     let mut rng: StdRng = SeedableRng::from_seed(seed);
    ///     let sentence = grammar.generate_seeded(&mut rng);
    ///     # let sentence_clone = sentence.clone();
    ///     match sentence {
    ///         Ok(s) => println!("random sentence: {}", s),
    ///         Err(e) => println!("something went wrong: {}!", e)
    ///     }
    ///
    ///     # assert!(sentence_clone.is_ok());
    /// }
    /// ```
    pub fn generate_seeded(&self, rng: &mut StdRng) -> Result<String, Error> {
        let start_rule: String;
        let first_production = self.productions_iter().nth(0);

        match first_production {
            Some(term) => {
                match term.lhs {
                    Term::Nonterminal(ref nt) => start_rule = nt.clone(),
                    Term::Terminal(_) => {
                        return Err(Error::GenerateError(format!(
                            "Termainal type cannot define a production in '{}'!",
                            term
                        )));
                    }
                }
            }
            None => {
                return Err(Error::GenerateError(
                    String::from("Failed to get first production!"),
                ));
            }
        }
        self.traverse(&start_rule, rng)
    }

    /// Generate a random sentence from self.
    /// Begins from lhs of first production.
    ///
    /// # Example
    ///
    /// ```rust
    /// extern crate bnf;
    /// use bnf::Grammar;
    ///
    /// fn main() {
    ///     let input =
    ///         "<dna> ::= <base> | <base> <dna>
    ///         <base> ::= \"A\" | \"C\" | \"G\" | \"T\"";
    ///     let grammar = Grammar::from_str(input).unwrap();
    ///     let sentence = grammar.generate();
    ///     # let sentence_clone = sentence.clone();
    ///     match sentence {
    ///         Ok(s) => println!("random sentence: {}", s),
    ///         Err(e) => println!("something went wrong: {}!", e)
    ///     }
    ///
    ///     # assert!(sentence_clone.is_ok());
    /// }
    /// ```
    pub fn generate(&self) -> Result<String, Error> {
        // let seed: &[_] = &[1, 2, 3, 4];
        let seed: Vec<usize> = thread_rng()
            .gen_iter::<usize>()
            .take(1000)
            .collect::<Vec<usize>>();
        let mut rng: StdRng = SeedableRng::from_seed(&seed[..]);
        self.generate_seeded(&mut rng)
    }
}

impl fmt::Display for Grammar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}\n",
            self.productions
                .iter()
                .map(|s| s.to_string())
                .collect::<Vec<_>>()
                .join("\n")
        )
    }
}

impl str::FromStr for Grammar {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::from_str(s)
    }
}

pub struct Iter<'a> {
    iterator: slice::Iter<'a, Production>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a Production;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next()
    }
}

pub struct IterMut<'a> {
    iterator: slice::IterMut<'a, Production>,
}

impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut Production;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use term::Term;
    use expression::Expression;
    use production::Production;
    // use grammar::Grammar;

    #[test]
    fn new_grammars() {
        let lhs1: Term = Term::Nonterminal(String::from("STRING A"));
        let rhs1: Expression = Expression::from_parts(vec![
            Term::Terminal(String::from("STRING B")),
            Term::Nonterminal(String::from("STRING C")),
        ]);
        let p1: Production = Production::from_parts(lhs1, vec![rhs1]);

        let lhs2: Term = Term::Nonterminal(String::from("STRING A"));
        let rhs2: Expression = Expression::from_parts(vec![
            Term::Terminal(String::from("STRING B")),
            Term::Nonterminal(String::from("STRING C")),
        ]);
        let p2: Production = Production::from_parts(lhs2, vec![rhs2]);

        let mut g1: Grammar = Grammar::new();
        g1.add_production(p1.clone());
        g1.add_production(p2.clone());
        let g2: Grammar = Grammar::from_parts(vec![p1, p2]);
        assert_eq!(g1, g2);
    }

    #[test]
    fn add_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::new();

        // grammar starts empty
        assert_eq!(grammar.productions_iter().count(), 0);

        grammar.add_production(production.clone());

        // grammar now has production
        assert_eq!(grammar.productions_iter().count(), 1);

        // mutated grammar identical to new grammar built from same productions
        let filled_grammar = Grammar::from_parts(productions.clone());
        assert_eq!(grammar, filled_grammar);
    }

    #[test]
    fn remove_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::from_parts(productions.clone());

        // grammar has production
        assert_eq!(
            Some(&production),
            grammar.productions_iter().find(|&prod| *prod == production)
        );
        assert_eq!(grammar.productions_iter().count(), productions.len());

        // production has been removed
        let removed = grammar.remove_production(&production);
        assert_eq!(removed, Some(production.clone()));
        assert_eq!(grammar.productions_iter().count(), productions.len() - 1);
        assert_eq!(
            None,
            grammar.productions_iter().find(|&prod| *prod == production)
        );
    }

    #[test]
    fn remove_nonexistent_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::from_parts(productions.clone());

        let unused = Production::from_parts(Term::Nonterminal(String::from("nonexistent")), vec![]);

        // grammar has original production
        assert_eq!(
            Some(&production),
            grammar.productions_iter().find(|&prod| *prod == production)
        );
        assert_eq!(grammar.productions_iter().count(), productions.len());

        // unused production is not removed
        let removed = grammar.remove_production(&unused);
        assert_eq!(removed, None);
        assert_eq!(grammar.productions_iter().count(), productions.len());
        assert_eq!(
            None,
            grammar.productions_iter().find(|&prod| *prod == unused)
        );
    }

    #[test]
    fn parse_error() {
        let grammar = Grammar::from_str("<almost_grammar> ::= <test");
        assert!(grammar.is_err(), "{:?} should be error", grammar);
    }

    #[test]
    fn parse_incomplete() {
        let result = Grammar::from_str("");
        assert!(result.is_err(), "{:?} should be err", result);
        match result {
            Err(e) => {
                match e {
                    Error::ParseIncomplete(_) => (),
                    e => panic!("should should be Error::ParseIncomplete: {:?}", e),
                }
            }
            Ok(s) => panic!("should should be Error::ParseIncomplete: {}", s),
        }
    }

    #[test]
    fn recursion_limit() {
        let grammar = Grammar::from_str("<nonterm> ::= <nonterm>");
        assert!(grammar.is_ok(), "{:?} should be ok", grammar);
        let sentence = grammar.unwrap().generate();
        assert!(sentence.is_err(), "{:?} should be err", sentence);
        match sentence {
            Err(e) => {
                match e {
                    Error::RecursionLimit(_) => (),
                    e => panic!("should should be Error::RecursionLimit: {:?}", e),
                }
            }
            Ok(s) => panic!("should should be Error::RecursionLimit: {}", s),
        }
    }

    #[test]
    fn lhs_not_found() {
        let grammar = Grammar::from_str("<start> ::= <not-used>");
        assert!(grammar.is_ok(), "{:?} should be ok", grammar);
        let sentence = grammar.unwrap().generate();
        assert!(sentence.is_ok(), "{:?} should be ok", sentence);
        assert_eq!(sentence.unwrap(), String::from("<not-used>"));
    }

    #[test]
    fn lhs_is_terminal_parse() {
        let grammar = Grammar::from_str("\"wrong place\" ::= <not-used>");
        assert!(grammar.is_err(), "{:?} should be error", grammar);
    }

    #[test]
    fn lhs_is_terminal_generate() {
        let lhs = Term::Terminal(String::from("\"bad LHS\""));
        let terminal = Term::Terminal(String::from("\"good RHS\""));
        let expression = Expression::from_parts(vec![terminal]);
        let production = Production::from_parts(lhs, vec![expression]);
        let grammar = Grammar::from_parts(vec![production]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }

    #[test]
    fn no_productions() {
        let grammar = Grammar::from_parts(vec![]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }

    #[test]
    fn no_expressions() {
        let lhs = Term::Terminal(String::from("<good-lhs>"));
        let production = Production::from_parts(lhs, vec![]);
        let grammar = Grammar::from_parts(vec![production]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }
}
<commit_msg>Test Grammar to_string and from_str property<commit_after>use std::fmt;
use std::str;
use std::slice;
use nom::IResult;
use expression::Expression;
use production::Production;
use term::Term;
use parsers;
use error::Error;
use rand::{Rng, SeedableRng, StdRng, thread_rng};
use stacker;

/// A Grammar is comprised of any number of Productions
#[derive(PartialEq, Debug, Clone)]
pub struct Grammar {
    productions: Vec<Production>,
}

impl Grammar {
    /// Construct a new `Grammar`
    pub fn new() -> Grammar {
        Grammar { productions: vec![] }
    }

    /// Construct an `Grammar` from `Production`s
    pub fn from_parts(v: Vec<Production>) -> Grammar {
        Grammar { productions: v }
    }

    // Get `Grammar` by parsing a string
    pub fn from_str(s: &str) -> Result<Self, Error> {
        match parsers::grammar_complete(s.as_bytes()) {
            IResult::Done(_, o) => Ok(o),
            IResult::Incomplete(n) => Err(Error::from(n)),
            IResult::Error(e) => Err(Error::from(e)),
        }
    }

    /// Add `Production` to the `Grammar`
    pub fn add_production(&mut self, prod: Production) {
        self.productions.push(prod)
    }

    /// Remove `Production` from the `Grammar`
    pub fn remove_production(&mut self, prod: &Production) -> Option<Production> {
        if let Some(pos) = self.productions.iter().position(|x| *x == *prod) {
            Some(self.productions.remove(pos))
        } else {
            None
        }
    }

    /// Get iterator of the `Grammar`'s `Production`s
    pub fn productions_iter(&self) -> Iter {
        Iter { iterator: self.productions.iter() }
    }

    /// Get mutable iterator of the `Grammar`'s `Production`s
    pub fn productions_iter_mut(&mut self) -> IterMut {
        IterMut { iterator: self.productions.iter_mut() }
    }

    fn eval_terminal(&self, term: &Term, rng: &mut StdRng) -> Result<String, Error> {
        match *term {
            Term::Nonterminal(ref nt) => self.traverse(&nt, rng),
            Term::Terminal(ref t) => Ok(t.clone()),
        }
    }

    fn traverse(&self, ident: &String, rng: &mut StdRng) -> Result<String, Error> {
        let stack_red_zone: usize = 32 * 1024; // 32KB
        // heavy recursion happening, we've hit out tolerable threshold
        if stacker::remaining_stack() < stack_red_zone {
            return Err(Error::RecursionLimit(format!(
                "Limit for recursion reached processing <{}>!",
                ident
            )));
        }

        let nonterm = Term::Nonterminal(ident.clone());
        let production;
        let find_lhs = self.productions_iter().find(|&x| x.lhs == nonterm);

        match find_lhs {
            Some(p) => production = p,
            None => return Ok(nonterm.to_string()),
        }

        let expression;
        let expressions = production.rhs_iter().collect::<Vec<&Expression>>();

        match rng.choose(&expressions) {
            Some(e) => expression = e.clone(),
            None => {
                return Err(Error::GenerateError(
                    String::from("Couldn't select random Expression!"),
                ));
            }
        }

        let mut result = String::new();
        for term in expression.terms_iter() {
            match self.eval_terminal(&term, rng) {
                Ok(s) => result = result + &s,
                Err(e) => return Err(e),
            }
        }

        return Ok(result);
    }

    /// Generate a random sentence from self and seed for random.
    /// Use if interested in reproducing the output generated.
    /// Begins from lhs of first production.
    ///
    /// # Example
    ///
    /// ```rust
    /// extern crate bnf;
    /// extern crate rand;
    /// use rand::{SeedableRng, StdRng};
    /// use bnf::Grammar;
    ///
    /// fn main() {
    ///     let input =
    ///         "<dna> ::= <base> | <base> <dna>
    ///         <base> ::= \"A\" | \"C\" | \"G\" | \"T\"";
    ///     let grammar = Grammar::from_str(input).unwrap();
    ///     let seed: &[_] = &[1,2,3,4];
    ///     let mut rng: StdRng = SeedableRng::from_seed(seed);
    ///     let sentence = grammar.generate_seeded(&mut rng);
    ///     # let sentence_clone = sentence.clone();
    ///     match sentence {
    ///         Ok(s) => println!("random sentence: {}", s),
    ///         Err(e) => println!("something went wrong: {}!", e)
    ///     }
    ///
    ///     # assert!(sentence_clone.is_ok());
    /// }
    /// ```
    pub fn generate_seeded(&self, rng: &mut StdRng) -> Result<String, Error> {
        let start_rule: String;
        let first_production = self.productions_iter().nth(0);

        match first_production {
            Some(term) => {
                match term.lhs {
                    Term::Nonterminal(ref nt) => start_rule = nt.clone(),
                    Term::Terminal(_) => {
                        return Err(Error::GenerateError(format!(
                            "Termainal type cannot define a production in '{}'!",
                            term
                        )));
                    }
                }
            }
            None => {
                return Err(Error::GenerateError(
                    String::from("Failed to get first production!"),
                ));
            }
        }
        self.traverse(&start_rule, rng)
    }

    /// Generate a random sentence from self.
    /// Begins from lhs of first production.
    ///
    /// # Example
    ///
    /// ```rust
    /// extern crate bnf;
    /// use bnf::Grammar;
    ///
    /// fn main() {
    ///     let input =
    ///         "<dna> ::= <base> | <base> <dna>
    ///         <base> ::= \"A\" | \"C\" | \"G\" | \"T\"";
    ///     let grammar = Grammar::from_str(input).unwrap();
    ///     let sentence = grammar.generate();
    ///     # let sentence_clone = sentence.clone();
    ///     match sentence {
    ///         Ok(s) => println!("random sentence: {}", s),
    ///         Err(e) => println!("something went wrong: {}!", e)
    ///     }
    ///
    ///     # assert!(sentence_clone.is_ok());
    /// }
    /// ```
    pub fn generate(&self) -> Result<String, Error> {
        // let seed: &[_] = &[1, 2, 3, 4];
        let seed: Vec<usize> = thread_rng()
            .gen_iter::<usize>()
            .take(1000)
            .collect::<Vec<usize>>();
        let mut rng: StdRng = SeedableRng::from_seed(&seed[..]);
        self.generate_seeded(&mut rng)
    }
}

impl fmt::Display for Grammar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}\n",
            self.productions
                .iter()
                .map(|s| s.to_string())
                .collect::<Vec<_>>()
                .join("\n")
        )
    }
}

impl str::FromStr for Grammar {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::from_str(s)
    }
}

pub struct Iter<'a> {
    iterator: slice::Iter<'a, Production>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a Production;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next()
    }
}

pub struct IterMut<'a> {
    iterator: slice::IterMut<'a, Production>,
}

impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut Production;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next()
    }
}

#[cfg(test)]
mod tests {
    extern crate quickcheck;
    extern crate rand;

    use super::*;
    use term::Term;
    use expression::Expression;
    use production::Production;
    use self::quickcheck::{Arbitrary, Gen, QuickCheck, StdGen, TestResult};

    impl Arbitrary for Grammar {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            let mut productions = Vec::<Production>::arbitrary(g);
            // grammar must always have atleast one production
            if productions.len() < 1 {
                productions.push(Production::arbitrary(g));
            }
            Grammar { productions: productions }
        }
    }

    fn prop_to_string_and_back(gram: Grammar) -> TestResult {
        let to_string = gram.to_string();
        let from_str = Grammar::from_str(&to_string);
        match from_str {
            Ok(from_prod) => TestResult::from_bool(from_prod == gram),
            _ => TestResult::error(format!("{} to string and back should be safe", gram)),
        }
    }

    #[test]
    fn to_string_and_back() {
        QuickCheck::new()
            .tests(1000)
            .gen(StdGen::new(rand::thread_rng(), 12usize))
            .quickcheck(prop_to_string_and_back as fn(Grammar) -> TestResult)
    }

    #[test]
    fn new_grammars() {
        let lhs1: Term = Term::Nonterminal(String::from("STRING A"));
        let rhs1: Expression = Expression::from_parts(vec![
            Term::Terminal(String::from("STRING B")),
            Term::Nonterminal(String::from("STRING C")),
        ]);
        let p1: Production = Production::from_parts(lhs1, vec![rhs1]);

        let lhs2: Term = Term::Nonterminal(String::from("STRING A"));
        let rhs2: Expression = Expression::from_parts(vec![
            Term::Terminal(String::from("STRING B")),
            Term::Nonterminal(String::from("STRING C")),
        ]);
        let p2: Production = Production::from_parts(lhs2, vec![rhs2]);

        let mut g1: Grammar = Grammar::new();
        g1.add_production(p1.clone());
        g1.add_production(p2.clone());
        let g2: Grammar = Grammar::from_parts(vec![p1, p2]);
        assert_eq!(g1, g2);
    }

    #[test]
    fn add_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::new();

        // grammar starts empty
        assert_eq!(grammar.productions_iter().count(), 0);

        grammar.add_production(production.clone());

        // grammar now has production
        assert_eq!(grammar.productions_iter().count(), 1);

        // mutated grammar identical to new grammar built from same productions
        let filled_grammar = Grammar::from_parts(productions.clone());
        assert_eq!(grammar, filled_grammar);
    }

    #[test]
    fn remove_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::from_parts(productions.clone());

        // grammar has production
        assert_eq!(
            Some(&production),
            grammar.productions_iter().find(|&prod| *prod == production)
        );
        assert_eq!(grammar.productions_iter().count(), productions.len());

        // production has been removed
        let removed = grammar.remove_production(&production);
        assert_eq!(removed, Some(production.clone()));
        assert_eq!(grammar.productions_iter().count(), productions.len() - 1);
        assert_eq!(
            None,
            grammar.productions_iter().find(|&prod| *prod == production)
        );
    }

    #[test]
    fn remove_nonexistent_production() {
        let lhs = Term::Nonterminal(String::from("dna"));
        let last = Expression::from_parts(vec![Term::Terminal(String::from("base"))]);
        let one_more = Expression::from_parts(vec![
            Term::Terminal(String::from("base")),
            Term::Nonterminal(String::from("dna")),
        ]);
        let expression_list = vec![last, one_more];
        let production = Production::from_parts(lhs, expression_list);
        let productions = vec![production.clone()];
        let mut grammar = Grammar::from_parts(productions.clone());

        let unused = Production::from_parts(Term::Nonterminal(String::from("nonexistent")), vec![]);

        // grammar has original production
        assert_eq!(
            Some(&production),
            grammar.productions_iter().find(|&prod| *prod == production)
        );
        assert_eq!(grammar.productions_iter().count(), productions.len());

        // unused production is not removed
        let removed = grammar.remove_production(&unused);
        assert_eq!(removed, None);
        assert_eq!(grammar.productions_iter().count(), productions.len());
        assert_eq!(
            None,
            grammar.productions_iter().find(|&prod| *prod == unused)
        );
    }

    #[test]
    fn parse_error() {
        let grammar = Grammar::from_str("<almost_grammar> ::= <test");
        assert!(grammar.is_err(), "{:?} should be error", grammar);
    }

    #[test]
    fn parse_incomplete() {
        let result = Grammar::from_str("");
        assert!(result.is_err(), "{:?} should be err", result);
        match result {
            Err(e) => {
                match e {
                    Error::ParseIncomplete(_) => (),
                    e => panic!("should should be Error::ParseIncomplete: {:?}", e),
                }
            }
            Ok(s) => panic!("should should be Error::ParseIncomplete: {}", s),
        }
    }

    #[test]
    fn recursion_limit() {
        let grammar = Grammar::from_str("<nonterm> ::= <nonterm>");
        assert!(grammar.is_ok(), "{:?} should be ok", grammar);
        let sentence = grammar.unwrap().generate();
        assert!(sentence.is_err(), "{:?} should be err", sentence);
        match sentence {
            Err(e) => {
                match e {
                    Error::RecursionLimit(_) => (),
                    e => panic!("should should be Error::RecursionLimit: {:?}", e),
                }
            }
            Ok(s) => panic!("should should be Error::RecursionLimit: {}", s),
        }
    }

    #[test]
    fn lhs_not_found() {
        let grammar = Grammar::from_str("<start> ::= <not-used>");
        assert!(grammar.is_ok(), "{:?} should be ok", grammar);
        let sentence = grammar.unwrap().generate();
        assert!(sentence.is_ok(), "{:?} should be ok", sentence);
        assert_eq!(sentence.unwrap(), String::from("<not-used>"));
    }

    #[test]
    fn lhs_is_terminal_parse() {
        let grammar = Grammar::from_str("\"wrong place\" ::= <not-used>");
        assert!(grammar.is_err(), "{:?} should be error", grammar);
    }

    #[test]
    fn lhs_is_terminal_generate() {
        let lhs = Term::Terminal(String::from("\"bad LHS\""));
        let terminal = Term::Terminal(String::from("\"good RHS\""));
        let expression = Expression::from_parts(vec![terminal]);
        let production = Production::from_parts(lhs, vec![expression]);
        let grammar = Grammar::from_parts(vec![production]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }

    #[test]
    fn no_productions() {
        let grammar = Grammar::from_parts(vec![]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }

    #[test]
    fn no_expressions() {
        let lhs = Term::Terminal(String::from("<good-lhs>"));
        let production = Production::from_parts(lhs, vec![]);
        let grammar = Grammar::from_parts(vec![production]);
        let sentence = grammar.generate();
        assert!(sentence.is_err(), "{:?} should be error", sentence);
    }
}
<|endoftext|>


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

// pretty-expanded FIXME #23616

/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

#![allow(dead_code, unused_variables)]
#![feature(box_syntax, box_heap)]
#![feature(placement_in_syntax)]

// Tests that the new `box` syntax works with unique pointers.

use std::boxed::{Box, HEAP};

struct Structure {
    x: isize,
    y: isize,
}

pub fn main() {
    let x: Box<isize> = box(HEAP) 2;
    let y: Box<isize> = box 2;
    let b: Box<isize> = box()(1 + 2);
    let c = box()(3 + 4);

    let s: Box<Structure> = box Structure {
        x: 3,
        y: 4,
    };
}
<commit_msg>fix pretty printing tests by opting into the features that the expanded code needs.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// pretty-expanded FIXME #23616

/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

#![allow(dead_code, unused_variables)]
#![feature(box_syntax, box_heap)]
#![feature(placement_in_syntax)]

// during check-pretty, the expanded code needs to opt into these
// features
#![feature(placement_new_protocol, core_intrinsics)]

// Tests that the new `box` syntax works with unique pointers.

use std::boxed::{Box, HEAP};

struct Structure {
    x: isize,
    y: isize,
}

pub fn main() {
    let x: Box<isize> = box(HEAP) 2;
    let y: Box<isize> = box 2;
    let b: Box<isize> = box()(1 + 2);
    let c = box()(3 + 4);

    let s: Box<Structure> = box Structure {
        x: 3,
        y: 4,
    };
}
<|endoftext|>


<commit_before>use crate::RegistryType;
use clap::{App,
           Arg};
use habitat_common::PROGRAM_NAME;
use habitat_core::package::PackageIdent;
use std::{path::Path,
          result,
          str::FromStr};
use url::Url;

/// The version of this library and program when built.
const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

/// Create the Clap CLI for the Docker exporter
pub fn cli<'a, 'b>() -> App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates (and optionally pushes) a Docker image from a set of Habitat packages";

    let mut cli = Cli::new(name, about).add_base_packages_args()
                                       .add_builder_args()
                                       .add_tagging_args()
                                       .add_publishing_args()
                                       .add_memory_arg()
                                       .add_layer_arg()
                                       .add_pkg_ident_arg(PkgIdentArgOptions { multiple: true });
    if cfg!(windows) {
        cli = cli.add_base_image_arg();
    }
    cli.app
}

/// A Docker-specific clap:App wrapper
#[derive(Clone)]
struct Cli<'a, 'b>
    where 'a: 'b
{
    pub app: App<'a, 'b>,
}

#[derive(Clone, Copy)]
pub struct PkgIdentArgOptions {
    pub multiple: bool,
}

impl<'a, 'b> Cli<'a, 'b> {
    fn new(name: &str, about: &'a str) -> Self {
        Cli { app: clap_app!(
              (name) =>
              (about: about)
              (version: VERSION)
              (author: "\nAuthors: The Habitat Maintainers <humans@habitat.sh>\n\n")
              (@arg IMAGE_NAME: --("image-name") -i +takes_value
                  "Image name (default: \"{{pkg_origin}}/{{pkg_name}}\" supports: \
                   {{pkg_origin}}, {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})")
              ), }
    }

    fn add_base_packages_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("HAB_PKG")
                    .long("hab-pkg")
                    .value_name("HAB_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Habitat CLI package identifier (ex: acme/redis) or filepath to a Habitat \
                         artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart) \
                         to install (default: core/hab)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_LAUNCHER_PKG")
                    .long("launcher-pkg")
                    .value_name("HAB_LAUNCHER_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Launcher package identifier (ex: core/hab-launcher) or filepath to a \
                         Habitat artifact (ex: \
                         /home/core-hab-launcher-6083-20171101045646-x86_64-linux.hart) to \
                         install (default: core/hab-launcher)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_SUP_PKG")
                    .long("sup-pkg")
                    .value_name("HAB_SUP_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Supervisor package identifier (ex: core/hab-sup) or filepath to a \
                         Habitat artifact (ex: \
                         /home/ore-hab-sup-0.39.1-20171118011657-x86_64-linux.hart) to install \
                         (default: core/hab-sup)",
                    ),
            );

        Cli { app }
    }

    fn add_builder_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("BLDR_URL")
                    .long("url")
                    .short("u")
                    .value_name("BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install packages from Builder at the specified URL \
                         (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("CHANNEL")
                    .long("channel")
                    .short("c")
                    .value_name("CHANNEL")
                    .help("Install packages from the specified release channel (default: stable)"),
            )
            .arg(
                Arg::with_name("BASE_PKGS_BLDR_URL")
                    .long("base-pkgs-url")
                    .value_name("BASE_PKGS_BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install base packages from Builder at the specified URL \
                         (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_CHANNEL")
                    .long("base-pkgs-channel")
                    .value_name("BASE_PKGS_CHANNEL")
                    .help(
                        "Install base packages from the specified release channel \
                         (default: stable)",
                    ),
            )
            .arg(
                Arg::with_name("BLDR_AUTH_TOKEN")
                    .long("auth")
                    .short("z")
                    .value_name("BLDR_AUTH_TOKEN")
                    .help("Provide a Builder auth token for private pkg export"),
            );

        Cli { app }
    }

    fn add_tagging_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("TAG_VERSION_RELEASE")
                    .long("tag-version-release")
                    .conflicts_with("NO_TAG_VERSION_RELEASE")
                    .help("Tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION_RELEASE")
                    .long("no-tag-version-release")
                    .conflicts_with("TAG_VERSION_RELEASE")
                    .help(
                        "Do not tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: no)",
                    ),
            )
            .arg(
                Arg::with_name("TAG_VERSION")
                    .long("tag-version")
                    .conflicts_with("NO_TAG_VERSION")
                    .help("Tag image with :\"{{pkg_version}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION")
                    .long("no-tag-version")
                    .conflicts_with("TAG_VERSION")
                    .help("Do not tag image with :\"{{pkg_version}}\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_LATEST")
                    .long("tag-latest")
                    .conflicts_with("NO_TAG_LATEST")
                    .help("Tag image with :\"latest\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_LATEST")
                    .long("no-tag-latest")
                    .conflicts_with("TAG_LATEST")
                    .help("Do not tag image with :\"latest\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_CUSTOM")
                    .long("tag-custom")
                    .value_name("TAG_CUSTOM")
                    .help(
                        "Tag image with additional custom tag (supports: {{pkg_origin}}, \
                         {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})",
                    ),
            );

        Cli { app }
    }

    fn add_publishing_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("PUSH_IMAGE")
                    .long("push-image")
                    .conflicts_with("NO_PUSH_IMAGE")
                    .requires_all(&["REGISTRY_USERNAME", "REGISTRY_PASSWORD"])
                    .help("Push image to remote registry (default: no)"),
            )
            .arg(
                Arg::with_name("NO_PUSH_IMAGE")
                    .long("no-push-image")
                    .conflicts_with("PUSH_IMAGE")
                    .help("Do not push image to remote registry (default: yes)"),
            )
            .arg(
                Arg::with_name("REGISTRY_USERNAME")
                    .long("username")
                    .short("U")
                    .value_name("REGISTRY_USERNAME")
                    .requires("REGISTRY_PASSWORD")
                    .help(
                        "Remote registry username, required for pushing image to remote registry",
                    ),
            )
            .arg(
                Arg::with_name("REGISTRY_PASSWORD")
                    .long("password")
                    .short("P")
                    .value_name("REGISTRY_PASSWORD")
                    .requires("REGISTRY_USERNAME")
                    .help(
                        "Remote registry password, required for pushing image to remote registry",
                    ),
            )
            .arg(
                Arg::with_name("REGISTRY_TYPE")
                    .possible_values(RegistryType::variants())
                    .long("registry-type")
                    .short("R")
                    .value_name("REGISTRY_TYPE")
                    .help("Remote registry type (default: docker)"),
            )
            .arg(
                Arg::with_name("REGISTRY_URL")
                    // This is not strictly a requirement but will keep someone from
                    // making a mistake when inputing an ECR URL
                    .required_if("REGISTRY_TYPE", "amazon")
                    .required_if("REGISTRY_TYPE", "azure")
                    .long("registry-url")
                    .short("G")
                    .value_name("REGISTRY_URL")
                    .help("Remote registry url"),
            )
            // Cleanup
            .arg(
                Arg::with_name("RM_IMAGE")
                    .long("rm-image")
                    .help("Remove local image from engine after build and/or push (default: no)"),
            );

        Cli { app }
    }

    fn add_pkg_ident_arg(self, options: PkgIdentArgOptions) -> Self {
        let help = if options.multiple {
            "One or more Habitat package identifiers (ex: acme/redis) and/or filepaths to a \
             Habitat Artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart)"
        } else {
            "A Habitat package identifier (ex: acme/redis) and/or filepath to a Habitat Artifact \
             (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart)"
        };

        let app =
            self.app
                .arg(Arg::with_name("PKG_IDENT_OR_ARTIFACT").value_name("PKG_IDENT_OR_ARTIFACT")
                                                            .required(true)
                                                            .multiple(options.multiple)
                                                            .help(help));

        Cli { app }
    }

    fn add_memory_arg(self) -> Self {
        let app = self.app
                      .arg(Arg::with_name("MEMORY_LIMIT").value_name("MEMORY_LIMIT")
                                                         .long("memory")
                                                         .short("m")
                                                         .help("Memory limit passed to docker \
                                                                build's --memory arg (ex: 2bg)"));

        Cli { app }
    }

    fn add_base_image_arg(self) -> Self {
        let app = self.app
                      .arg(Arg::with_name("BASE_IMAGE").value_name("BASE_IMAGE")
                                                       .long("base-image")
                                                       .help("Base image of the final exported \
                                                              image --base-image \
                                                              mcr.microsoft.com/windows/\
                                                              servercore:ltsc2019"));

        Cli { app }
    }

    fn add_layer_arg(self) -> Self {
        let app =
            self.app
                .arg(Arg::with_name("MULTI_LAYER").value_name("MULTI_LAYER")
                                                  .long("multi-layer")
                                                  .required(false)
                                                  .takes_value(false)
                                                  .multiple(false)
                                                  .help("If specified, creates an image where \
                                                         each Habitat package is added in its \
                                                         own layer, in dependency order (that \
                                                         is, low-level dependencies are added \
                                                         first, with user packages added last). \
                                                         This will allow for reusable layers, \
                                                         reducing storage and network \
                                                         transmission costs. If the resulting \
                                                         image cannot be built because there \
                                                         are too many layers, re-build without \
                                                         specifying this option to add all \
                                                         Habitat packages in a single layer \
                                                         (which is the default behavior)."));
        Cli { app }
    }
}

#[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
fn valid_ident_or_hart(val: String) -> result::Result<(), String> {
    if Path::new(&val).is_file() {
        Ok(())
    } else if val.ends_with(".hart") {
        Err(format!("Habitat artifact file: '{}' not found", &val))
    } else {
        match PackageIdent::from_str(&val) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{}", e)),
        }
    }
}

#[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
fn valid_url(val: String) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}
<commit_msg>[DOCKER] Remove PkgIdentArgOptions struct<commit_after>use crate::RegistryType;
use clap::{App,
           Arg};
use habitat_common::PROGRAM_NAME;
use habitat_core::package::PackageIdent;
use std::{path::Path,
          result,
          str::FromStr};
use url::Url;

/// The version of this library and program when built.
const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

/// Create the Clap CLI for the Docker exporter
pub fn cli<'a, 'b>() -> App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates (and optionally pushes) a Docker image from a set of Habitat packages";

    let mut cli = Cli::new(name, about).add_base_packages_args()
                                       .add_builder_args()
                                       .add_tagging_args()
                                       .add_publishing_args()
                                       .add_memory_arg()
                                       .add_layer_arg()
                                       .add_pkg_ident_arg();
    if cfg!(windows) {
        cli = cli.add_base_image_arg();
    }
    cli.app
}

/// A Docker-specific clap:App wrapper
#[derive(Clone)]
struct Cli<'a, 'b>
    where 'a: 'b
{
    pub app: App<'a, 'b>,
}

impl<'a, 'b> Cli<'a, 'b> {
    fn new(name: &str, about: &'a str) -> Self {
        Cli { app: clap_app!(
              (name) =>
              (about: about)
              (version: VERSION)
              (author: "\nAuthors: The Habitat Maintainers <humans@habitat.sh>\n\n")
              (@arg IMAGE_NAME: --("image-name") -i +takes_value
                  "Image name (default: \"{{pkg_origin}}/{{pkg_name}}\" supports: \
                   {{pkg_origin}}, {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})")
              ), }
    }

    fn add_base_packages_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("HAB_PKG")
                    .long("hab-pkg")
                    .value_name("HAB_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Habitat CLI package identifier (ex: acme/redis) or filepath to a Habitat \
                         artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart) \
                         to install (default: core/hab)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_LAUNCHER_PKG")
                    .long("launcher-pkg")
                    .value_name("HAB_LAUNCHER_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Launcher package identifier (ex: core/hab-launcher) or filepath to a \
                         Habitat artifact (ex: \
                         /home/core-hab-launcher-6083-20171101045646-x86_64-linux.hart) to \
                         install (default: core/hab-launcher)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_SUP_PKG")
                    .long("sup-pkg")
                    .value_name("HAB_SUP_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Supervisor package identifier (ex: core/hab-sup) or filepath to a \
                         Habitat artifact (ex: \
                         /home/ore-hab-sup-0.39.1-20171118011657-x86_64-linux.hart) to install \
                         (default: core/hab-sup)",
                    ),
            );

        Cli { app }
    }

    fn add_builder_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("BLDR_URL")
                    .long("url")
                    .short("u")
                    .value_name("BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install packages from Builder at the specified URL \
                         (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("CHANNEL")
                    .long("channel")
                    .short("c")
                    .value_name("CHANNEL")
                    .help("Install packages from the specified release channel (default: stable)"),
            )
            .arg(
                Arg::with_name("BASE_PKGS_BLDR_URL")
                    .long("base-pkgs-url")
                    .value_name("BASE_PKGS_BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install base packages from Builder at the specified URL \
                         (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_CHANNEL")
                    .long("base-pkgs-channel")
                    .value_name("BASE_PKGS_CHANNEL")
                    .help(
                        "Install base packages from the specified release channel \
                         (default: stable)",
                    ),
            )
            .arg(
                Arg::with_name("BLDR_AUTH_TOKEN")
                    .long("auth")
                    .short("z")
                    .value_name("BLDR_AUTH_TOKEN")
                    .help("Provide a Builder auth token for private pkg export"),
            );

        Cli { app }
    }

    fn add_tagging_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("TAG_VERSION_RELEASE")
                    .long("tag-version-release")
                    .conflicts_with("NO_TAG_VERSION_RELEASE")
                    .help("Tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION_RELEASE")
                    .long("no-tag-version-release")
                    .conflicts_with("TAG_VERSION_RELEASE")
                    .help(
                        "Do not tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: no)",
                    ),
            )
            .arg(
                Arg::with_name("TAG_VERSION")
                    .long("tag-version")
                    .conflicts_with("NO_TAG_VERSION")
                    .help("Tag image with :\"{{pkg_version}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION")
                    .long("no-tag-version")
                    .conflicts_with("TAG_VERSION")
                    .help("Do not tag image with :\"{{pkg_version}}\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_LATEST")
                    .long("tag-latest")
                    .conflicts_with("NO_TAG_LATEST")
                    .help("Tag image with :\"latest\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_LATEST")
                    .long("no-tag-latest")
                    .conflicts_with("TAG_LATEST")
                    .help("Do not tag image with :\"latest\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_CUSTOM")
                    .long("tag-custom")
                    .value_name("TAG_CUSTOM")
                    .help(
                        "Tag image with additional custom tag (supports: {{pkg_origin}}, \
                         {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})",
                    ),
            );

        Cli { app }
    }

    fn add_publishing_args(self) -> Self {
        let app = self
            .app
            .arg(
                Arg::with_name("PUSH_IMAGE")
                    .long("push-image")
                    .conflicts_with("NO_PUSH_IMAGE")
                    .requires_all(&["REGISTRY_USERNAME", "REGISTRY_PASSWORD"])
                    .help("Push image to remote registry (default: no)"),
            )
            .arg(
                Arg::with_name("NO_PUSH_IMAGE")
                    .long("no-push-image")
                    .conflicts_with("PUSH_IMAGE")
                    .help("Do not push image to remote registry (default: yes)"),
            )
            .arg(
                Arg::with_name("REGISTRY_USERNAME")
                    .long("username")
                    .short("U")
                    .value_name("REGISTRY_USERNAME")
                    .requires("REGISTRY_PASSWORD")
                    .help(
                        "Remote registry username, required for pushing image to remote registry",
                    ),
            )
            .arg(
                Arg::with_name("REGISTRY_PASSWORD")
                    .long("password")
                    .short("P")
                    .value_name("REGISTRY_PASSWORD")
                    .requires("REGISTRY_USERNAME")
                    .help(
                        "Remote registry password, required for pushing image to remote registry",
                    ),
            )
            .arg(
                Arg::with_name("REGISTRY_TYPE")
                    .possible_values(RegistryType::variants())
                    .long("registry-type")
                    .short("R")
                    .value_name("REGISTRY_TYPE")
                    .help("Remote registry type (default: docker)"),
            )
            .arg(
                Arg::with_name("REGISTRY_URL")
                    // This is not strictly a requirement but will keep someone from
                    // making a mistake when inputing an ECR URL
                    .required_if("REGISTRY_TYPE", "amazon")
                    .required_if("REGISTRY_TYPE", "azure")
                    .long("registry-url")
                    .short("G")
                    .value_name("REGISTRY_URL")
                    .help("Remote registry url"),
            )
            // Cleanup
            .arg(
                Arg::with_name("RM_IMAGE")
                    .long("rm-image")
                    .help("Remove local image from engine after build and/or push (default: no)"),
            );

        Cli { app }
    }

    fn add_pkg_ident_arg(self) -> Self {
        let help = "One or more Habitat package identifiers (ex: acme/redis) and/or filepaths to \
                    a Habitat Artifact (ex: \
                    /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart)";

        let app =
            self.app
                .arg(Arg::with_name("PKG_IDENT_OR_ARTIFACT").value_name("PKG_IDENT_OR_ARTIFACT")
                                                            .required(true)
                                                            .multiple(true)
                                                            .help(help));

        Cli { app }
    }

    fn add_memory_arg(self) -> Self {
        let app = self.app
                      .arg(Arg::with_name("MEMORY_LIMIT").value_name("MEMORY_LIMIT")
                                                         .long("memory")
                                                         .short("m")
                                                         .help("Memory limit passed to docker \
                                                                build's --memory arg (ex: 2bg)"));

        Cli { app }
    }

    fn add_base_image_arg(self) -> Self {
        let app = self.app
                      .arg(Arg::with_name("BASE_IMAGE").value_name("BASE_IMAGE")
                                                       .long("base-image")
                                                       .help("Base image of the final exported \
                                                              image --base-image \
                                                              mcr.microsoft.com/windows/\
                                                              servercore:ltsc2019"));

        Cli { app }
    }

    fn add_layer_arg(self) -> Self {
        let app =
            self.app
                .arg(Arg::with_name("MULTI_LAYER").value_name("MULTI_LAYER")
                                                  .long("multi-layer")
                                                  .required(false)
                                                  .takes_value(false)
                                                  .multiple(false)
                                                  .help("If specified, creates an image where \
                                                         each Habitat package is added in its \
                                                         own layer, in dependency order (that \
                                                         is, low-level dependencies are added \
                                                         first, with user packages added last). \
                                                         This will allow for reusable layers, \
                                                         reducing storage and network \
                                                         transmission costs. If the resulting \
                                                         image cannot be built because there \
                                                         are too many layers, re-build without \
                                                         specifying this option to add all \
                                                         Habitat packages in a single layer \
                                                         (which is the default behavior)."));
        Cli { app }
    }
}

#[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
fn valid_ident_or_hart(val: String) -> result::Result<(), String> {
    if Path::new(&val).is_file() {
        Ok(())
    } else if val.ends_with(".hart") {
        Err(format!("Habitat artifact file: '{}' not found", &val))
    } else {
        match PackageIdent::from_str(&val) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{}", e)),
        }
    }
}

#[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
fn valid_url(val: String) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}
<|endoftext|>