<commit_before>
use super::DocBuilder;
use super::crates::crates_from_path;
use {DocBuilderError, get_package, source_path, copy_dir, copy_doc_dir, update_sources};
use db::{connect_db, add_package_into_database, add_build_into_database};
use cargo::core::Package;
use std::process::{Command, Output};
use std::path::PathBuf;

use regex::Regex;

type CommandResult = Result<String, String>;

#[derive(Debug)]
pub struct ChrootBuilderResult {
    pub output: String,
    pub build_success: bool,
    pub have_doc: bool,
    pub rustc_version: String,
    pub cratesfyi_version: String,
}

impl DocBuilder {
    /// Builds every package documentation in chroot environment
    pub fn build_world(&mut self) -> Result<(), DocBuilderError> {
        try!(update_sources());

        let mut count = 0;

        crates(self.options.crates_io_index_path.clone(), |name, version| {
            match self.build_package(name, version) {
                Ok(status) => {
                    if status && count % 10 == 0 {
                        let _ = self.save_cache();
                    }
                },
                Err(err) => info!("Failed to build package {}-{}: {}", name, version, err)
            }

            self.cache.insert(format!("{}-{}", name, version));
            count += 1;
        })
    }

    /// Builds package documentation in chroot environment and adds into cratesfyi database
    pub fn build_package(&mut self, name: &str, version: &str) -> Result<bool, DocBuilderError> {
        info!("Building package {}-{}", name, version);

        // Database connection
        let conn = try!(connect_db());

        // Skip crates according to options
        if (self.options.skip_if_log_exists &&
            self.cache.contains(&format!("{}-{}", name, version)[..])) ||
           (self.options.skip_if_exists &&
            self.db_cache.contains(&format!("{}-{}", name, version)[..])) {
            info!("Skipping");
            return Ok(false);
        }

        // get_package (and cargo) is using semver, add '=' in front of version.
        let pkg = try!(get_package(name, Some(&format!("={}", version)[..])));
        let res = self.build_package_in_chroot(&pkg);

        // copy sources and documentation
        try!(self.copy_sources(&pkg));
        if res.have_doc {
            try!(self.copy_documentation(&pkg, &res.rustc_version));
        }

        let release_id = try!(add_package_into_database(&conn, &pkg, &res));
        try!(add_build_into_database(&conn, &release_id, &res));

        // remove source and build directory after we are done
        try!(self.remove_build_dir(&pkg));

        // add package into build cache
        self.cache.insert(format!("{}-{}", name, version));

        Ok(res.build_success)
    }

    /// Builds documentation of a package with cratesfyi in chroot environment
    fn build_package_in_chroot(&self, package: &Package) -> ChrootBuilderResult {
        debug!("Building package in chroot");
        let (rustc_version, cratesfyi_version) = self.get_versions();
        let cmd = format!("cratesfyi doc {} ={}",
                          package.manifest().name(),
                          package.manifest().version());
        match self.chroot_command(cmd) {
            Ok(o) => {
                ChrootBuilderResult {
                    output: o,
                    build_success: true,
                    have_doc: self.have_documentation(&package),
                    rustc_version: rustc_version,
                    cratesfyi_version: cratesfyi_version,
                }
            }
            Err(e) => {
                ChrootBuilderResult {
                    output: e,
                    build_success: false,
                    have_doc: false,
                    rustc_version: rustc_version,
                    cratesfyi_version: cratesfyi_version,
                }
            }
        }
    }

    /// Copies source files of a package into source_path
    fn copy_sources(&self, package: &Package) -> Result<(), DocBuilderError> {
        debug!("Copying sources");
        let destination = PathBuf::from(&self.options.sources_path).join(format!("{}/{}",
                          package.manifest().name(),
                          package.manifest().version()));
        // unwrap is safe here, this function will be always called after get_package
        match copy_dir(source_path(&package).unwrap(), &destination) {
            Ok(_) => Ok(()),
            Err(e) => Err(DocBuilderError::Io(e)),
        }
    }

    /// Copies documentation to destination directory
    fn copy_documentation(&self,
                          package: &Package,
                          rustc_version: &str)
                          -> Result<(), DocBuilderError> {
        debug!("Copying codumentation");
        let crate_doc_path = PathBuf::from(&self.options.chroot_path)
                                 .join("home")
                                 .join(&self.options.chroot_user)
                                 .join(canonical_name(&package));
        let destination = PathBuf::from(&self.options.destination).join(format!("{}/{}",
                          package.manifest().name(),
                          package.manifest().version()));
        copy_doc_dir(crate_doc_path,
                     destination,
                     parse_rustc_version(rustc_version).trim())
            .map_err(DocBuilderError::Io)
    }

    /// Removes build directory of a package
    fn remove_build_dir(&self, package: &Package) -> Result<(), DocBuilderError> {
        debug!("Removing build directory");
        let _ = self.chroot_command(format!("rm -rf {}", canonical_name(&package)));
        Ok(())
    }

    /// Runs a command in a chroot environment
    fn chroot_command<T: AsRef<str>>(&self, cmd: T) -> CommandResult {
        command_result(Command::new("sudo")
                           .arg("lxc-attach")
                           .arg("-n")
                           .arg(&self.options.container_name)
                           .arg("--")
                           .arg("su")
                           .arg("-")
                           .arg(&self.options.chroot_user)
                           .arg("-c")
                           .arg(cmd.as_ref())
                           .output()
                           .unwrap())
    }

    /// Checks a package build directory to determine if package have docs
    ///
    /// This function is checking first target in targets to see if documentation exists for a
    /// crate. Package must be successfully built in chroot environment first.
    fn have_documentation(&self, package: &Package) -> bool {
        let crate_doc_path = PathBuf::from(&self.options.chroot_path)
                                 .join("home")
                                 .join(&self.options.chroot_user)
                                 .join(canonical_name(&package))
                                 .join("doc")
                                 .join(package.targets()[0].name().to_string());
        crate_doc_path.exists()
    }

    /// Gets rustc and cratesfyi version from chroot environment
    fn get_versions(&self) -> (String, String) {
        // It is safe to use expect here
        // chroot environment must always have rustc and cratesfyi installed
        (String::from(self.chroot_command("rustc --version")
                          .expect("Failed to get rustc version")
                          .trim()),
         String::from(self.chroot_command("cratesfyi --version")
                          .expect("Failed to get cratesfyi version")
                          .trim()))
    }
}

/// Simple function to capture command output
fn command_result(output: Output) -> CommandResult {
    let mut command_out = String::from_utf8_lossy(&output.stdout).into_owned();
    command_out.push_str(&String::from_utf8_lossy(&output.stderr).into_owned()[..]);
    match output.status.success() {
        true => Ok(command_out),
        false => Err(command_out),
    }
}

/// Returns canonical name of a package.
///
/// It's just package-version. All directory structure used in cratesfyi is
/// following this naming scheme.
fn canonical_name(package: &Package) -> String {
    format!("{}-{}",
            package.manifest().name(),
            package.manifest().version())
}

/// Parses rustc commit hash from rustc version string
fn parse_rustc_version<S: AsRef<str>>(version: S) -> String {
    let version_regex = Regex::new(r" ([\w-.]+) \((\w+) (\d+)-(\d+)-(\d+)\)").unwrap();
    let captures = version_regex.captures(version.as_ref()).expect("Failed to parse rustc version");

    format!("{}{}{}-{}-{}",
            captures.at(3).unwrap(),
            captures.at(4).unwrap(),
            captures.at(5).unwrap(),
            captures.at(1).unwrap(),
            captures.at(2).unwrap())
}

/// Runs `func` with the all crates from crates-io.index repository path.
///
/// First argument of func is the name of crate and
/// second argument is the version of crate. Func will be run for every crate.
fn crates<F>(path: PathBuf, mut func: F) -> Result<(), DocBuilderError>
    where F: FnMut(&str, &str) -> ()
{
    crates_from_path(&path, &mut func)
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::parse_rustc_version;
    use std::path::PathBuf;
    use {DocBuilder, DocBuilderOptions};

    #[test]
    #[ignore]
    fn test_build_world() {
        let _ = env_logger::init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let docbuilder = DocBuilder::new(options);
        // This test is building WHOLE WORLD and may take forever
        assert!(docbuilder.build_world().is_ok());
    }

    #[test]
    #[ignore]
    fn test_build_package() {
        let _ = env_logger::init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let docbuilder = DocBuilder::new(options);
        let res = docbuilder.build_package("rand", "0.3.14");
        assert!(res.is_ok());
    }

    #[test]
    fn test_parse_rustc_version() {
        assert_eq!(parse_rustc_version("rustc 1.10.0-nightly (57ef01513 2016-05-23)"),
                   "20160523-1.10.0-nightly-57ef01513");
        assert_eq!(parse_rustc_version("cratesfyi 0.2.0 (ba9ae23 2016-05-26)"),
                   "20160526-0.2.0-ba9ae23");
    }
}
<commit_msg>Increase count on package build<commit_after>
use super::DocBuilder;
use super::crates::crates_from_path;
use {DocBuilderError, get_package, source_path, copy_dir, copy_doc_dir, update_sources};
use db::{connect_db, add_package_into_database, add_build_into_database};
use cargo::core::Package;
use std::process::{Command, Output};
use std::path::PathBuf;

use regex::Regex;

type CommandResult = Result<String, String>;

#[derive(Debug)]
pub struct ChrootBuilderResult {
    pub output: String,
    pub build_success: bool,
    pub have_doc: bool,
    pub rustc_version: String,
    pub cratesfyi_version: String,
}

impl DocBuilder {
    /// Builds every package documentation in chroot environment
    pub fn build_world(&mut self) -> Result<(), DocBuilderError> {
        try!(update_sources());

        let mut count = 0;

        crates(self.options.crates_io_index_path.clone(), |name, version| {
            match self.build_package(name, version) {
                Ok(status) => {
                    count += 1;
                    if status && count % 10 == 0 {
                        let _ = self.save_cache();
                    }
                },
                Err(err) => info!("Failed to build package {}-{}: {}", name, version, err)
            }
            self.cache.insert(format!("{}-{}", name, version));
            count += 1;
        })
    }

    /// Builds package documentation in chroot environment and adds into cratesfyi database
    pub fn build_package(&mut self, name: &str, version: &str) -> Result<bool, DocBuilderError> {
        info!("Building package {}-{}", name, version);

        // Database connection
        let conn = try!(connect_db());

        // Skip crates according to options
        if (self.options.skip_if_log_exists &&
            self.cache.contains(&format!("{}-{}", name, version)[..])) ||
           (self.options.skip_if_exists &&
            self.db_cache.contains(&format!("{}-{}", name, version)[..])) {
            info!("Skipping");
            return Ok(false);
        }

        // get_package (and cargo) is using semver, add '=' in front of version.
        let pkg = try!(get_package(name, Some(&format!("={}", version)[..])));
        let res = self.build_package_in_chroot(&pkg);

        // copy sources and documentation
        try!(self.copy_sources(&pkg));
        if res.have_doc {
            try!(self.copy_documentation(&pkg, &res.rustc_version));
        }

        let release_id = try!(add_package_into_database(&conn, &pkg, &res));
        try!(add_build_into_database(&conn, &release_id, &res));

        // remove source and build directory after we are done
        try!(self.remove_build_dir(&pkg));

        // add package into build cache
        self.cache.insert(format!("{}-{}", name, version));

        Ok(res.build_success)
    }

    /// Builds documentation of a package with cratesfyi in chroot environment
    fn build_package_in_chroot(&self, package: &Package) -> ChrootBuilderResult {
        debug!("Building package in chroot");
        let (rustc_version, cratesfyi_version) = self.get_versions();
        let cmd = format!("cratesfyi doc {} ={}",
                          package.manifest().name(),
                          package.manifest().version());
        match self.chroot_command(cmd) {
            Ok(o) => {
                ChrootBuilderResult {
                    output: o,
                    build_success: true,
                    have_doc: self.have_documentation(&package),
                    rustc_version: rustc_version,
                    cratesfyi_version: cratesfyi_version,
                }
            }
            Err(e) => {
                ChrootBuilderResult {
                    output: e,
                    build_success: false,
                    have_doc: false,
                    rustc_version: rustc_version,
                    cratesfyi_version: cratesfyi_version,
                }
            }
        }
    }

    /// Copies source files of a package into source_path
    fn copy_sources(&self, package: &Package) -> Result<(), DocBuilderError> {
        debug!("Copying sources");
        let destination = PathBuf::from(&self.options.sources_path).join(format!("{}/{}",
                          package.manifest().name(),
                          package.manifest().version()));
        // unwrap is safe here, this function will be always called after get_package
        match copy_dir(source_path(&package).unwrap(), &destination) {
            Ok(_) => Ok(()),
            Err(e) => Err(DocBuilderError::Io(e)),
        }
    }

    /// Copies documentation to destination directory
    fn copy_documentation(&self,
                          package: &Package,
                          rustc_version: &str)
                          -> Result<(), DocBuilderError> {
        debug!("Copying codumentation");
        let crate_doc_path = PathBuf::from(&self.options.chroot_path)
                                 .join("home")
                                 .join(&self.options.chroot_user)
                                 .join(canonical_name(&package));
        let destination = PathBuf::from(&self.options.destination).join(format!("{}/{}",
                          package.manifest().name(),
                          package.manifest().version()));
        copy_doc_dir(crate_doc_path,
                     destination,
                     parse_rustc_version(rustc_version).trim())
            .map_err(DocBuilderError::Io)
    }

    /// Removes build directory of a package
    fn remove_build_dir(&self, package: &Package) -> Result<(), DocBuilderError> {
        debug!("Removing build directory");
        let _ = self.chroot_command(format!("rm -rf {}", canonical_name(&package)));
        Ok(())
    }

    /// Runs a command in a chroot environment
    fn chroot_command<T: AsRef<str>>(&self, cmd: T) -> CommandResult {
        command_result(Command::new("sudo")
                           .arg("lxc-attach")
                           .arg("-n")
                           .arg(&self.options.container_name)
                           .arg("--")
                           .arg("su")
                           .arg("-")
                           .arg(&self.options.chroot_user)
                           .arg("-c")
                           .arg(cmd.as_ref())
                           .output()
                           .unwrap())
    }

    /// Checks a package build directory to determine if package have docs
    ///
    /// This function is checking first target in targets to see if documentation exists for a
    /// crate. Package must be successfully built in chroot environment first.
    fn have_documentation(&self, package: &Package) -> bool {
        let crate_doc_path = PathBuf::from(&self.options.chroot_path)
                                 .join("home")
                                 .join(&self.options.chroot_user)
                                 .join(canonical_name(&package))
                                 .join("doc")
                                 .join(package.targets()[0].name().to_string());
        crate_doc_path.exists()
    }

    /// Gets rustc and cratesfyi version from chroot environment
    fn get_versions(&self) -> (String, String) {
        // It is safe to use expect here
        // chroot environment must always have rustc and cratesfyi installed
        (String::from(self.chroot_command("rustc --version")
                          .expect("Failed to get rustc version")
                          .trim()),
         String::from(self.chroot_command("cratesfyi --version")
                          .expect("Failed to get cratesfyi version")
                          .trim()))
    }
}

/// Simple function to capture command output
fn command_result(output: Output) -> CommandResult {
    let mut command_out = String::from_utf8_lossy(&output.stdout).into_owned();
    command_out.push_str(&String::from_utf8_lossy(&output.stderr).into_owned()[..]);
    match output.status.success() {
        true => Ok(command_out),
        false => Err(command_out),
    }
}

/// Returns canonical name of a package.
///
/// It's just package-version. All directory structure used in cratesfyi is
/// following this naming scheme.
fn canonical_name(package: &Package) -> String {
    format!("{}-{}",
            package.manifest().name(),
            package.manifest().version())
}

/// Parses rustc commit hash from rustc version string
fn parse_rustc_version<S: AsRef<str>>(version: S) -> String {
    let version_regex = Regex::new(r" ([\w-.]+) \((\w+) (\d+)-(\d+)-(\d+)\)").unwrap();
    let captures = version_regex.captures(version.as_ref()).expect("Failed to parse rustc version");

    format!("{}{}{}-{}-{}",
            captures.at(3).unwrap(),
            captures.at(4).unwrap(),
            captures.at(5).unwrap(),
            captures.at(1).unwrap(),
            captures.at(2).unwrap())
}

/// Runs `func` with the all crates from crates-io.index repository path.
///
/// First argument of func is the name of crate and
/// second argument is the version of crate. Func will be run for every crate.
fn crates<F>(path: PathBuf, mut func: F) -> Result<(), DocBuilderError>
    where F: FnMut(&str, &str) -> ()
{
    crates_from_path(&path, &mut func)
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::parse_rustc_version;
    use std::path::PathBuf;
    use {DocBuilder, DocBuilderOptions};

    #[test]
    #[ignore]
    fn test_build_world() {
        let _ = env_logger::init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let docbuilder = DocBuilder::new(options);
        // This test is building WHOLE WORLD and may take forever
        assert!(docbuilder.build_world().is_ok());
    }

    #[test]
    #[ignore]
    fn test_build_package() {
        let _ = env_logger::init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let docbuilder = DocBuilder::new(options);
        let res = docbuilder.build_package("rand", "0.3.14");
        assert!(res.is_ok());
    }

    #[test]
    fn test_parse_rustc_version() {
        assert_eq!(parse_rustc_version("rustc 1.10.0-nightly (57ef01513 2016-05-23)"),
                   "20160523-1.10.0-nightly-57ef01513");
        assert_eq!(parse_rustc_version("cratesfyi 0.2.0 (ba9ae23 2016-05-26)"),
                   "20160526-0.2.0-ba9ae23");
    }
}
<|endoftext|>


<commit_before>use std::fmt;

/// A representation of byte oriented equivalence classes.
///
/// This is used in a DFA to reduce the size of the transition table. This can
/// have a particularly large impact not only on the total size of a dense DFA,
/// but also on compile times.
#[derive(Clone, Copy)]
pub struct ByteClasses([u8; 256]);

impl ByteClasses {
    /// Creates a new set of equivalence classes where all bytes are mapped to
    /// the same class.
    pub fn empty() -> ByteClasses {
        ByteClasses([0; 256])
    }

    /// Creates a new set of equivalence classes where each byte belongs to
    /// its own equivalence class.
    pub fn singletons() -> ByteClasses {
        let mut classes = ByteClasses::empty();
        for i in 0..256 {
            classes.set(i as u8, i as u8);
        }
        classes
    }

    /// Copies the byte classes given. The given slice must have length 0 or
    /// length 256. Slices of length 0 are treated as singletons (every byte
    /// is its own class).
    pub fn from_slice(slice: &[u8]) -> ByteClasses {
        assert!(slice.is_empty() || slice.len() == 256);

        if slice.is_empty() {
            ByteClasses::singletons()
        } else {
            let mut classes = ByteClasses::empty();
            for (b, &class) in slice.iter().enumerate() {
                classes.set(b as u8, class);
            }
            classes
        }
    }

    /// Set the equivalence class for the given byte.
    pub fn set(&mut self, byte: u8, class: u8) {
        self.0[byte as usize] = class;
    }

    /// Get the equivalence class for the given byte.
    pub fn get(&self, byte: u8) -> u8 {
        self.0[byte as usize]
    }

    /// Get the equivalence class for the given byte while forcefully
    /// eliding bounds checks.
    pub unsafe fn get_unchecked(&self, byte: u8) -> u8 {
        *self.0.get_unchecked(byte as usize)
    }

    /// Return the total number of elements in the alphabet represented by
    /// these equivalence classes. Equivalently, this returns the total number
    /// of equivalence classes.
    pub fn alphabet_len(&self) -> usize {
        self.0[255] as usize + 1
    }

    /// Returns true if and only if every byte in this class maps to its own
    /// equivalence class. Equivalently, there are 256 equivalence classes
    /// and each class contains exactly one byte.
    pub fn is_singleton(&self) -> bool {
        self.alphabet_len() == 256
    }

    /// Returns an iterator over a sequence of representative bytes from each
    /// equivalence class. Namely, this yields exactly N items, where N is
    /// equivalent to the number of equivalence classes. Each item is an
    /// arbitrary byte drawn from each equivalence class.
    ///
    /// This is useful when one is determinizing an NFA and the NFA's alphabet
    /// hasn't been converted to equivalence classes yet. Picking an arbitrary
    /// byte from each equivalence class then permits a full exploration of
    /// the NFA instead of using every possible byte value.
    pub fn representatives(&self) -> ByteClassRepresentatives {
        ByteClassRepresentatives { classes: self, byte: 0, last_class: None }
    }

    /// Returns all of the bytes in the given equivalence class.
    ///
    /// The second element in the tuple indicates the number of elements in
    /// the array.
    fn elements(&self, equiv: u8) -> ([u8; 256], usize) {
        let (mut array, mut len) = ([0; 256], 0);
        for b in 0..256 {
            if self.get(b as u8) == equiv {
                array[len] = b as u8;
                len += 1;
            }
        }
        (array, len)
    }
}

impl fmt::Debug for ByteClasses {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_singleton() {
            write!(f, "ByteClasses({{singletons}})")
        } else {
            write!(f, "ByteClasses(")?;
            for equiv in 0..self.alphabet_len() {
                let (members, len) = self.elements(equiv as u8);
                write!(f, "{} => {:?}", equiv, &members[..len])?;
            }
            write!(f, ")")
        }
    }
}

/// An iterator over representative bytes from each equivalence class.
#[derive(Debug)]
pub struct ByteClassRepresentatives<'a> {
    classes: &'a ByteClasses,
    byte: usize,
    last_class: Option<u8>,
}

impl<'a> Iterator for ByteClassRepresentatives<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        while self.byte < 256 {
            let byte = self.byte as u8;
            let class = self.classes.get(byte);
            self.byte += 1;

            if self.last_class != Some(class) {
                self.last_class = Some(class);
                return Some(byte);
            }
        }
        None
    }
}
<commit_msg>byte class methods should be inlined<commit_after>use std::fmt;

/// A representation of byte oriented equivalence classes.
///
/// This is used in a DFA to reduce the size of the transition table. This can
/// have a particularly large impact not only on the total size of a dense DFA,
/// but also on compile times.
#[derive(Clone, Copy)]
pub struct ByteClasses([u8; 256]);

impl ByteClasses {
    /// Creates a new set of equivalence classes where all bytes are mapped to
    /// the same class.
    pub fn empty() -> ByteClasses {
        ByteClasses([0; 256])
    }

    /// Creates a new set of equivalence classes where each byte belongs to
    /// its own equivalence class.
    pub fn singletons() -> ByteClasses {
        let mut classes = ByteClasses::empty();
        for i in 0..256 {
            classes.set(i as u8, i as u8);
        }
        classes
    }

    /// Copies the byte classes given. The given slice must have length 0 or
    /// length 256. Slices of length 0 are treated as singletons (every byte
    /// is its own class).
    pub fn from_slice(slice: &[u8]) -> ByteClasses {
        assert!(slice.is_empty() || slice.len() == 256);

        if slice.is_empty() {
            ByteClasses::singletons()
        } else {
            let mut classes = ByteClasses::empty();
            for (b, &class) in slice.iter().enumerate() {
                classes.set(b as u8, class);
            }
            classes
        }
    }

    /// Set the equivalence class for the given byte.
    #[inline]
    pub fn set(&mut self, byte: u8, class: u8) {
        self.0[byte as usize] = class;
    }

    /// Get the equivalence class for the given byte.
    #[inline]
    pub fn get(&self, byte: u8) -> u8 {
        self.0[byte as usize]
    }

    /// Get the equivalence class for the given byte while forcefully
    /// eliding bounds checks.
    #[inline]
    pub unsafe fn get_unchecked(&self, byte: u8) -> u8 {
        *self.0.get_unchecked(byte as usize)
    }

    /// Return the total number of elements in the alphabet represented by
    /// these equivalence classes. Equivalently, this returns the total number
    /// of equivalence classes.
    #[inline]
    pub fn alphabet_len(&self) -> usize {
        self.0[255] as usize + 1
    }

    /// Returns true if and only if every byte in this class maps to its own
    /// equivalence class. Equivalently, there are 256 equivalence classes
    /// and each class contains exactly one byte.
    #[inline]
    pub fn is_singleton(&self) -> bool {
        self.alphabet_len() == 256
    }

    /// Returns an iterator over a sequence of representative bytes from each
    /// equivalence class. Namely, this yields exactly N items, where N is
    /// equivalent to the number of equivalence classes. Each item is an
    /// arbitrary byte drawn from each equivalence class.
    ///
    /// This is useful when one is determinizing an NFA and the NFA's alphabet
    /// hasn't been converted to equivalence classes yet. Picking an arbitrary
    /// byte from each equivalence class then permits a full exploration of
    /// the NFA instead of using every possible byte value.
    pub fn representatives(&self) -> ByteClassRepresentatives {
        ByteClassRepresentatives { classes: self, byte: 0, last_class: None }
    }

    /// Returns all of the bytes in the given equivalence class.
    ///
    /// The second element in the tuple indicates the number of elements in
    /// the array.
    fn elements(&self, equiv: u8) -> ([u8; 256], usize) {
        let (mut array, mut len) = ([0; 256], 0);
        for b in 0..256 {
            if self.get(b as u8) == equiv {
                array[len] = b as u8;
                len += 1;
            }
        }
        (array, len)
    }
}

impl fmt::Debug for ByteClasses {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_singleton() {
            write!(f, "ByteClasses({{singletons}})")
        } else {
            write!(f, "ByteClasses(")?;
            for equiv in 0..self.alphabet_len() {
                let (members, len) = self.elements(equiv as u8);
                write!(f, "{} => {:?}", equiv, &members[..len])?;
            }
            write!(f, ")")
        }
    }
}

/// An iterator over representative bytes from each equivalence class.
#[derive(Debug)]
pub struct ByteClassRepresentatives<'a> {
    classes: &'a ByteClasses,
    byte: usize,
    last_class: Option<u8>,
}

impl<'a> Iterator for ByteClassRepresentatives<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        while self.byte < 256 {
            let byte = self.byte as u8;
            let class = self.classes.get(byte);
            self.byte += 1;

            if self.last_class != Some(class) {
                self.last_class = Some(class);
                return Some(byte);
            }
        }
        None
    }
}
<|endoftext|>


<commit_before>use std::fs::{create_dir_all, File};
use std::io::Write;

use clap::ArgMatches;
use rusoto::ChainProvider;

use aws::credentials_provider;
use encryption::Encryptor;
use error::KawsResult;
use pki::CertificateAuthority;

pub struct Cluster<'a> {
    name: &'a str,
    region: &'a str,
}

pub struct ExistingCluster<'a> {
    aws_credentials_provider: ChainProvider,
    domain: &'a str,
    cluster: Cluster<'a>,
    kms_master_key_id: &'a str,
}

pub struct NewCluster<'a> {
    availability_zone: &'a str,
    aws_account_id: &'a str,
    cluster: Cluster<'a>,
    coreos_ami: &'a str,
    domain: &'a str,
    iam_users: Vec<&'a str>,
    instance_size: &'a str,
    kubernetes_version: &'a str,
    masters_max_size: &'a str,
    masters_min_size: &'a str,
    nodes_max_size: &'a str,
    nodes_min_size: &'a str,
    rbac_super_user: &'a str,
    ssh_keys: Vec<&'a str>,
    zone_id: &'a str,
}

impl<'a> Cluster<'a> {
    pub fn new(name: &'a str, region: &'a str) -> Self {
        Cluster {
            name: name,
            region: region,
        }
    }

    fn ca_cert_path(&self) -> String {
        format!("clusters/{}/ca.pem", self.name)
    }

    fn encrypted_master_key_path(&self) -> String {
        format!("clusters/{}/master-key-encrypted.base64", self.name)
    }

    fn encrypted_node_key_path(&self) -> String {
        format!("clusters/{}/node-key-encrypted.base64", self.name)
    }

    fn gitignore_path(&self) -> String {
        format!("clusters/{}/.gitignore", self.name)
    }

    fn master_cert_path(&self) -> String {
        format!("clusters/{}/master.pem", self.name)
    }

    fn name(&self) -> &str {
        self.name
    }

    fn node_cert_path(&self) -> String {
        format!("clusters/{}/node.pem", self.name)
    }

    fn openssl_config_path(&self) -> String {
        format!("clusters/{}/openssl.cnf", self.name)
    }

    fn region(&self) -> &str {
        self.region
    }

    fn tfvars_path(&self) -> String {
        format!("clusters/{}/terraform.tfvars", self.name)
    }
}

impl<'a> ExistingCluster<'a> {
    pub fn new(matches: &'a ArgMatches) -> Self {
        ExistingCluster {
            aws_credentials_provider: credentials_provider(
                matches.value_of("aws-credentials-path"),
                matches.value_of("aws-credentials-profile"),
            ),
            cluster: Cluster::new(
                matches.value_of("cluster").expect("missing cluster name"),
                matches.value_of("region").expect("missing region"),
            ),
            domain: matches.value_of("domain").expect("missing domain"),
            kms_master_key_id: matches.value_of("kms-key").expect("missing kms-key"),
        }
    }

    pub fn generate_pki(&mut self) -> KawsResult {
        self.generate_etcd_pki(self.kms_master_key_id)?;
        self.generate_etcd_peer_pki(self.kms_master_key_id)?;
        self.generate_k8s_pki(self.kms_master_key_id)?;

        Ok(None)
    }

    fn generate_etcd_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(&format!("kaws-etcd-ca-{}", self.cluster.name))?;

        let (server_cert, server_key) = ca.generate_cert(
            &format!("kaws-etcd-server-{}", self.cluster.name),
            Some(&[
                "10.0.1.4",
                "10.0.1.5",
                "10.0.1.6",
            ]),
        )?;

        let (client_cert, client_key) = ca.generate_cert(
            &format!("kaws-etcd-client-{}", self.cluster.name),
            None,
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/etcd-ca.pem", self.cluster.name),
            &format!("clusters/{}/etcd-ca-key-encrypted.base64", self.cluster.name),
        )?;

        server_cert.write_to_file(&format!("clusters/{}/etcd-server.pem", self.cluster.name))?;
        server_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-server-key-encrypted.base64", self.cluster.name),
        )?;

        client_cert.write_to_file(&format!("clusters/{}/etcd-client.pem", self.cluster.name))?;
        client_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-client-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }

    fn generate_etcd_peer_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(
            &format!("kaws-etcd-peer-ca-{}", self.cluster.name)
        )?;

        let (peer_cert, peer_key) = ca.generate_cert(
            &format!("kaws-etcd-peer-{}", self.cluster.name),
            Some(&[
                "10.0.1.4",
                "10.0.1.5",
                "10.0.1.6",
            ]),
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/etcd-peer-ca.pem", self.cluster.name),
            &format!("clusters/{}/etcd-peer-ca-key-encrypted.base64", self.cluster.name),
        )?;

        peer_cert.write_to_file(&format!("clusters/{}/etcd-peer.pem", self.cluster.name))?;
        peer_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-peer-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }

    fn generate_k8s_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(&format!("kaws-k8s-ca-{}", self.cluster.name))?;

        let (master_cert, master_key) = ca.generate_cert(
            &format!("kaws-k8s-master-{}", self.cluster.name),
            Some(&[
                "kubernetes",
                "kubernetes.default",
                "kubernetes.default.svc",
                "kubernetes.default.svc.cluster.local",
                &format!("kubernetes.{}", self.domain),
                "10.3.0.1",
            ]),
        )?;

        let (node_cert, node_key) = ca.generate_cert(
            &format!("kaws-k8s-node-{}", self.cluster.name),
            None,
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/k8s-ca.pem", self.cluster.name),
            &format!("clusters/{}/k8s-ca-key-encrypted.base64", self.cluster.name),
        )?;

        master_cert.write_to_file(&format!("clusters/{}/k8s-master.pem", self.cluster.name))?;
        master_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/k8s-master-key-encrypted.base64", self.cluster.name),
        )?;

        node_cert.write_to_file(&format!("clusters/{}/k8s-node.pem", self.cluster.name))?;
        node_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/k8s-node-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }
}

impl<'a> NewCluster<'a> {
    pub fn new(matches: &'a ArgMatches) -> Self {
        NewCluster {
            availability_zone: matches
                .value_of("availability-zone")
                .expect("missing availability-zone"),
            aws_account_id: matches.value_of("aws-account-id").expect("missing aws-account-id"),
            cluster: Cluster::new(
                matches.value_of("cluster").expect("missing cluster name"),
                matches.value_of("region").expect("missing region"),
            ),
            coreos_ami: matches.value_of("ami").expect("missing ami"),
            domain: matches.value_of("domain").expect("missing domain"),
            iam_users: matches
                .values_of("iam-user")
                .expect("missing iam-users")
                .collect(),
            instance_size: matches.value_of("size").expect("missing instance size"),
            kubernetes_version: matches.value_of("k8s-version").expect("missing k8s-version"),
            masters_max_size: matches
                .value_of("masters-max-size")
                .expect("missing masters-max-size"),
            masters_min_size: matches
                .value_of("masters-min-size")
                .expect("missing masters-min-size"),
            nodes_max_size: matches
                .value_of("nodes-max-size")
                .expect("missing nodes-max-size"),
            nodes_min_size: matches
                .value_of("nodes-min-size")
                .expect("missing nodes-min-size"),
            rbac_super_user: matches.value_of("rbac-super-user").expect("missing rbac-super-user"),
            ssh_keys: matches.values_of("ssh-key").expect("missing ssh-keys").collect(),
            zone_id: matches.value_of("zone-id").expect("missing zone-id"),
        }
    }

    pub fn init(&mut self) -> KawsResult {
        try!(self.create_directories());
        try!(self.create_gitignore());
        try!(self.create_tfvars());
        try!(self.create_openssl_config());
        try!(self.create_pki_stubs());

        Ok(Some(format!(
            "Cluster \"{name}\" initialized! Commit clusters/{name} to Git.",
            name = self.cluster.name,
        )))
    }

    fn create_directories(&self) -> KawsResult {
        log_wrap!("Creating directories for the new cluster", {
            try!(create_dir_all(format!("clusters/{}", self.cluster.name)));
        });

        Ok(None)
    }

    fn create_gitignore(&self) -> KawsResult {
        log_wrap!("Creating .gitignore file", {
            let mut file = try!(File::create(&self.cluster.gitignore_path()));

            try!(write!(file, "*-key.pem"));
        });

        Ok(None)
    }

    fn create_tfvars(&self) -> KawsResult {
        log_wrap!("Creating tfvars file", {
            let mut file = try!(File::create(&self.cluster.tfvars_path()));

            try!(write!(
                file,
                "\
kaws_account_id = \"{}\"
kaws_availability_zone = \"{}\"
kaws_cluster = \"{}\"
kaws_coreos_ami = \"{}\"
kaws_domain = \"{}\"
kaws_etcd_01_initial_cluster_state = \"new\"
kaws_etcd_02_initial_cluster_state = \"new\"
kaws_etcd_03_initial_cluster_state = \"new\"
kaws_iam_users = [{}]
kaws_instance_size = \"{}\"
kaws_masters_max_size = \"{}\"
kaws_masters_min_size = \"{}\"
kaws_nodes_max_size = \"{}\"
kaws_nodes_min_size = \"{}\"
kaws_propagating_vgws = []
kaws_rbac_super_user = \"{}\"
kaws_region = \"{}\"
kaws_ssh_keys = [{}]
kaws_version = \"{}\"
kaws_zone_id = \"{}\"
",
                self.aws_account_id,
                self.availability_zone,
                self.cluster.name(),
                self.coreos_ami,
                self.domain,
                self.iam_users.iter().map(|iam_user| {
                    format!("\"{}\"", iam_user)
                }).collect::<Vec<String>>().join(", "),
                self.instance_size,
                self.masters_max_size,
                self.masters_min_size,
                self.nodes_max_size,
                self.nodes_min_size,
                self.rbac_super_user,
                self.cluster.region(),
                self.ssh_keys.iter().map(|ssh_key| {
                    format!("\"{}\"", ssh_key)
                }).collect::<Vec<String>>().join(", "),
                self.kubernetes_version,
                self.zone_id,
            ));
        });

        Ok(None)
    }

    fn create_openssl_config(&self) -> KawsResult {
        log_wrap!("Creating OpenSSL config file", {
            let mut file = try!(File::create(&self.cluster.openssl_config_path()));

            try!(write!(
                file,
                "\
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.{}
IP.1 = 10.3.0.1
",
                self.domain,
            ));
        });

        Ok(None)
    }

    fn create_pki_stubs(&self) -> KawsResult {
        let paths = [
            &self.cluster.ca_cert_path(),
            &self.cluster.master_cert_path(),
            &self.cluster.encrypted_master_key_path(),
            &self.cluster.node_cert_path(),
            &self.cluster.encrypted_node_key_path(),
        ];

        for path in paths.iter() {
            try!(File::create(path));
        }

        Ok(None)
    }
}
<commit_msg>Remove openssl config file.<commit_after>use std::fs::{create_dir_all, File};
use std::io::Write;

use clap::ArgMatches;
use rusoto::ChainProvider;

use aws::credentials_provider;
use encryption::Encryptor;
use error::KawsResult;
use pki::CertificateAuthority;

pub struct Cluster<'a> {
    name: &'a str,
    region: &'a str,
}

pub struct ExistingCluster<'a> {
    aws_credentials_provider: ChainProvider,
    domain: &'a str,
    cluster: Cluster<'a>,
    kms_master_key_id: &'a str,
}

pub struct NewCluster<'a> {
    availability_zone: &'a str,
    aws_account_id: &'a str,
    cluster: Cluster<'a>,
    coreos_ami: &'a str,
    domain: &'a str,
    iam_users: Vec<&'a str>,
    instance_size: &'a str,
    kubernetes_version: &'a str,
    masters_max_size: &'a str,
    masters_min_size: &'a str,
    nodes_max_size: &'a str,
    nodes_min_size: &'a str,
    rbac_super_user: &'a str,
    ssh_keys: Vec<&'a str>,
    zone_id: &'a str,
}

impl<'a> Cluster<'a> {
    pub fn new(name: &'a str, region: &'a str) -> Self {
        Cluster {
            name: name,
            region: region,
        }
    }

    fn ca_cert_path(&self) -> String {
        format!("clusters/{}/ca.pem", self.name)
    }

    fn encrypted_master_key_path(&self) -> String {
        format!("clusters/{}/master-key-encrypted.base64", self.name)
    }

    fn encrypted_node_key_path(&self) -> String {
        format!("clusters/{}/node-key-encrypted.base64", self.name)
    }

    fn gitignore_path(&self) -> String {
        format!("clusters/{}/.gitignore", self.name)
    }

    fn master_cert_path(&self) -> String {
        format!("clusters/{}/master.pem", self.name)
    }

    fn name(&self) -> &str {
        self.name
    }

    fn node_cert_path(&self) -> String {
        format!("clusters/{}/node.pem", self.name)
    }

    fn region(&self) -> &str {
        self.region
    }

    fn tfvars_path(&self) -> String {
        format!("clusters/{}/terraform.tfvars", self.name)
    }
}

impl<'a> ExistingCluster<'a> {
    pub fn new(matches: &'a ArgMatches) -> Self {
        ExistingCluster {
            aws_credentials_provider: credentials_provider(
                matches.value_of("aws-credentials-path"),
                matches.value_of("aws-credentials-profile"),
            ),
            cluster: Cluster::new(
                matches.value_of("cluster").expect("missing cluster name"),
                matches.value_of("region").expect("missing region"),
            ),
            domain: matches.value_of("domain").expect("missing domain"),
            kms_master_key_id: matches.value_of("kms-key").expect("missing kms-key"),
        }
    }

    pub fn generate_pki(&mut self) -> KawsResult {
        self.generate_etcd_pki(self.kms_master_key_id)?;
        self.generate_etcd_peer_pki(self.kms_master_key_id)?;
        self.generate_k8s_pki(self.kms_master_key_id)?;

        Ok(None)
    }

    fn generate_etcd_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(&format!("kaws-etcd-ca-{}", self.cluster.name))?;

        let (server_cert, server_key) = ca.generate_cert(
            &format!("kaws-etcd-server-{}", self.cluster.name),
            Some(&[
                "10.0.1.4",
                "10.0.1.5",
                "10.0.1.6",
            ]),
        )?;

        let (client_cert, client_key) = ca.generate_cert(
            &format!("kaws-etcd-client-{}", self.cluster.name),
            None,
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/etcd-ca.pem", self.cluster.name),
            &format!("clusters/{}/etcd-ca-key-encrypted.base64", self.cluster.name),
        )?;

        server_cert.write_to_file(&format!("clusters/{}/etcd-server.pem", self.cluster.name))?;
        server_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-server-key-encrypted.base64", self.cluster.name),
        )?;

        client_cert.write_to_file(&format!("clusters/{}/etcd-client.pem", self.cluster.name))?;
        client_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-client-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }

    fn generate_etcd_peer_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(
            &format!("kaws-etcd-peer-ca-{}", self.cluster.name)
        )?;

        let (peer_cert, peer_key) = ca.generate_cert(
            &format!("kaws-etcd-peer-{}", self.cluster.name),
            Some(&[
                "10.0.1.4",
                "10.0.1.5",
                "10.0.1.6",
            ]),
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/etcd-peer-ca.pem", self.cluster.name),
            &format!("clusters/{}/etcd-peer-ca-key-encrypted.base64", self.cluster.name),
        )?;

        peer_cert.write_to_file(&format!("clusters/{}/etcd-peer.pem", self.cluster.name))?;
        peer_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/etcd-peer-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }

    fn generate_k8s_pki(&self, kms_key_id: &str) -> KawsResult {
        let ca = CertificateAuthority::generate(&format!("kaws-k8s-ca-{}", self.cluster.name))?;

        let (master_cert, master_key) = ca.generate_cert(
            &format!("kaws-k8s-master-{}", self.cluster.name),
            Some(&[
                "kubernetes",
                "kubernetes.default",
                "kubernetes.default.svc",
                "kubernetes.default.svc.cluster.local",
                &format!("kubernetes.{}", self.domain),
                "10.3.0.1",
            ]),
        )?;

        let (node_cert, node_key) = ca.generate_cert(
            &format!("kaws-k8s-node-{}", self.cluster.name),
            None,
        )?;

        let mut encryptor = Encryptor::new(
            self.aws_credentials_provider.clone(),
            self.cluster.region().parse()?,
            Some(kms_key_id),
        );

        ca.write_to_files(
            &mut encryptor,
            &format!("clusters/{}/k8s-ca.pem", self.cluster.name),
            &format!("clusters/{}/k8s-ca-key-encrypted.base64", self.cluster.name),
        )?;

        master_cert.write_to_file(&format!("clusters/{}/k8s-master.pem", self.cluster.name))?;
        master_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/k8s-master-key-encrypted.base64", self.cluster.name),
        )?;

        node_cert.write_to_file(&format!("clusters/{}/k8s-node.pem", self.cluster.name))?;
        node_key.write_to_file(
            &mut encryptor,
            &format!("clusters/{}/k8s-node-key-encrypted.base64", self.cluster.name),
        )?;

        Ok(None)
    }
}

impl<'a> NewCluster<'a> {
    pub fn new(matches: &'a ArgMatches) -> Self {
        NewCluster {
            availability_zone: matches
                .value_of("availability-zone")
                .expect("missing availability-zone"),
            aws_account_id: matches.value_of("aws-account-id").expect("missing aws-account-id"),
            cluster: Cluster::new(
                matches.value_of("cluster").expect("missing cluster name"),
                matches.value_of("region").expect("missing region"),
            ),
            coreos_ami: matches.value_of("ami").expect("missing ami"),
            domain: matches.value_of("domain").expect("missing domain"),
            iam_users: matches
                .values_of("iam-user")
                .expect("missing iam-users")
                .collect(),
            instance_size: matches.value_of("size").expect("missing instance size"),
            kubernetes_version: matches.value_of("k8s-version").expect("missing k8s-version"),
            masters_max_size: matches
                .value_of("masters-max-size")
                .expect("missing masters-max-size"),
            masters_min_size: matches
                .value_of("masters-min-size")
                .expect("missing masters-min-size"),
            nodes_max_size: matches
                .value_of("nodes-max-size")
                .expect("missing nodes-max-size"),
            nodes_min_size: matches
                .value_of("nodes-min-size")
                .expect("missing nodes-min-size"),
            rbac_super_user: matches.value_of("rbac-super-user").expect("missing rbac-super-user"),
            ssh_keys: matches.values_of("ssh-key").expect("missing ssh-keys").collect(),
            zone_id: matches.value_of("zone-id").expect("missing zone-id"),
        }
    }

    pub fn init(&mut self) -> KawsResult {
        try!(self.create_directories());
        try!(self.create_gitignore());
        try!(self.create_tfvars());
        try!(self.create_pki_stubs());

        Ok(Some(format!(
            "Cluster \"{name}\" initialized! Commit clusters/{name} to Git.",
            name = self.cluster.name,
        )))
    }

    fn create_directories(&self) -> KawsResult {
        log_wrap!("Creating directories for the new cluster", {
            try!(create_dir_all(format!("clusters/{}", self.cluster.name)));
        });

        Ok(None)
    }

    fn create_gitignore(&self) -> KawsResult {
        log_wrap!("Creating .gitignore file", {
            let mut file = try!(File::create(&self.cluster.gitignore_path()));

            try!(write!(file, "*-key.pem"));
        });

        Ok(None)
    }

    fn create_tfvars(&self) -> KawsResult {
        log_wrap!("Creating tfvars file", {
            let mut file = try!(File::create(&self.cluster.tfvars_path()));

            try!(write!(
                file,
                "\
kaws_account_id = \"{}\"
kaws_availability_zone = \"{}\"
kaws_cluster = \"{}\"
kaws_coreos_ami = \"{}\"
kaws_domain = \"{}\"
kaws_etcd_01_initial_cluster_state = \"new\"
kaws_etcd_02_initial_cluster_state = \"new\"
kaws_etcd_03_initial_cluster_state = \"new\"
kaws_iam_users = [{}]
kaws_instance_size = \"{}\"
kaws_masters_max_size = \"{}\"
kaws_masters_min_size = \"{}\"
kaws_nodes_max_size = \"{}\"
kaws_nodes_min_size = \"{}\"
kaws_propagating_vgws = []
kaws_rbac_super_user = \"{}\"
kaws_region = \"{}\"
kaws_ssh_keys = [{}]
kaws_version = \"{}\"
kaws_zone_id = \"{}\"
",
                self.aws_account_id,
                self.availability_zone,
                self.cluster.name(),
                self.coreos_ami,
                self.domain,
                self.iam_users.iter().map(|iam_user| {
                    format!("\"{}\"", iam_user)
                }).collect::<Vec<String>>().join(", "),
                self.instance_size,
                self.masters_max_size,
                self.masters_min_size,
                self.nodes_max_size,
                self.nodes_min_size,
                self.rbac_super_user,
                self.cluster.region(),
                self.ssh_keys.iter().map(|ssh_key| {
                    format!("\"{}\"", ssh_key)
                }).collect::<Vec<String>>().join(", "),
                self.kubernetes_version,
                self.zone_id,
            ));
        });

        Ok(None)
    }

    fn create_pki_stubs(&self) -> KawsResult {
        let paths = [
            &self.cluster.ca_cert_path(),
            &self.cluster.master_cert_path(),
            &self.cluster.encrypted_master_key_path(),
            &self.cluster.node_cert_path(),
            &self.cluster.encrypted_node_key_path(),
        ];

        for path in paths.iter() {
            try!(File::create(path));
        }

        Ok(None)
    }
}
<|endoftext|>


<commit_before>//! # command
//!
//! Runs task commands/scripts.
//!

use log::Logger;
use rand::{Rng, thread_rng};
use std::env;
use std::env::current_dir;
use std::fs::{File, create_dir_all, remove_file};
use std::io::Error;
use std::io::prelude::*;
use std::process::{Command, ExitStatus, Stdio, exit};
use types::Step;

/// Validates the exit code code and if not 0 or unable to validate it, panic.
pub fn validate_exit_code(exit_status: Result<ExitStatus, Error>) {
    match exit_status {
        Ok(code) => {
            if !code.success() {
                match code.code() {
                    Some(value) => {
                        if value != 0 {
                            exit(value);
                        }
                    }
                    None => exit(1),
                }
            }
        }
        Err(error) => panic!("Error while executing command, error: {}", error),
    }
}

/// Runs the requested script text and panics in case of any script error.
pub fn run_script(
    logger: &Logger,
    script_lines: &Vec<String>,
) {
    let name = env!("CARGO_PKG_NAME");
    let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

    let mut file_path = env::temp_dir();
    file_path.push(name);

    // create parent directory
    match create_dir_all(&file_path) {
        Ok(_) => logger.verbose::<()>("Created temporary directory.", &[], None),
        Err(error) => logger.verbose("Unable to create temporary directory: ", &[&file_path.to_str().unwrap_or("???")], Some(error)),
    };

    file_path.push(file_name);
    if cfg!(windows) {
        file_path.set_extension("bat");
    } else {
        file_path.set_extension("sh");
    };

    let file_path_str = &file_path.to_str().unwrap_or("???");

    logger.verbose::<()>("Creating temporary script file: ", &[&file_path_str], None);

    let mut file = match File::create(&file_path) {
        Err(error) => {
            logger.error("Unable to create script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to create script file, error: {}", error);
        }
        Ok(file) => file,
    };

    let mut text = script_lines.join("\n");

    let cwd_holder = match current_dir() {
        Ok(value) => value,
        Err(error) => {
            logger.error("Unable to get current working directory.", &[], Some(&error));
            panic!("Unable to get current working directory, error: {}", error);
        }
    };

    let cwd = match cwd_holder.to_str() {
        Some(cwd_str) => cwd_str.clone(),
        None => {
            logger.error::<()>("Unable to get current working directory.", &[], None);
            panic!("Unable to get current working directory");
        }
    };

    text.insert_str(0, "\n");
    text.insert_str(0, cwd);
    text.insert_str(0, "cd ");

    match file.write_all(text.as_bytes()) {
        Err(error) => {
            logger.error("Unable to write to script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to write to script file, error: {}", error);
        }
        Ok(_) => logger.verbose::<()>("Written script file text:\n", &[&text], None),
    }

    let command = if cfg!(windows) {
        "cmd"
    } else {
        "sh"
    };

    let args_vector = if cfg!(windows) {
        vec!["-c".to_string(), file_path_str.to_string()]
    } else {
        vec![file_path_str.to_string()]
    };

    let args = Some(args_vector);

    run_command(&logger, &command, &args);

    match remove_file(&file_path_str) {
        Ok(_) => logger.verbose::<()>("Temporary file deleted: ", &[&file_path_str], None),
        Err(error) => logger.verbose("Unable to delete temporary file: ", &[&file_path_str], Some(error)),
    };
}

/// Runs the requested command and panics in case of any error.
pub fn run_command(
    logger: &Logger,
    command_string: &str,
    args: &Option<Vec<String>>,
) {
    logger.verbose::<()>("Execute Command: ", &[&command_string], None);
    let mut command = Command::new(&command_string);

    match *args {
        Some(ref args_vec) => {
            for arg in args_vec.iter() {
                command.arg(arg);
            }
        }
        None => logger.verbose::<()>("No command args defined.", &[], None),
    };

    command.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());
    logger.info("Execute Command: ", &[], Some(&command));

    let exit_status = command.status();
    validate_exit_code(exit_status);
}

/// Runs the given task command and if not defined, the task script.
pub fn run(
    logger: &Logger,
    step: &Step,
) {
    match step.config.command {
        Some(ref command_string) => {
            run_command(&logger, &command_string, &step.config.args);
        }
        None => {
            match step.config.script {
                Some(ref script) => run_script(&logger, script),
                None => logger.verbose::<()>("No script defined.", &[], None),
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use log;
    use std::io::ErrorKind;
    use types::Task;

    #[test]
    #[should_panic]
    fn validate_exit_code_error() {
        validate_exit_code(Err(Error::new(ErrorKind::Other, "test")));
    }

    #[test]
    fn run_no_command() {
        let logger = log::create("error");
        let task = Task {
            install_crate: None,
            command: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            script: None,
            dependencies: None
        };
        let step = Step { name: "test".to_string(), config: task };

        run(&logger, &step);
    }
}
<commit_msg>more tests<commit_after>//! # command
//!
//! Runs task commands/scripts.
//!

use log::Logger;
use rand::{Rng, thread_rng};
use std::env;
use std::env::current_dir;
use std::fs::{File, create_dir_all, remove_file};
use std::io::Error;
use std::io::prelude::*;
use std::process::{Command, ExitStatus, Stdio, exit};
use types::Step;

/// Validates the exit code code and if not 0 or unable to validate it, panic.
pub fn validate_exit_code(exit_status: Result<ExitStatus, Error>) {
    match exit_status {
        Ok(code) => {
            if !code.success() {
                match code.code() {
                    Some(value) => {
                        if value != 0 {
                            exit(value);
                        }
                    }
                    None => exit(1),
                }
            }
        }
        Err(error) => panic!("Error while executing command, error: {}", error),
    }
}

/// Runs the requested script text and panics in case of any script error.
pub fn run_script(
    logger: &Logger,
    script_lines: &Vec<String>,
) {
    let name = env!("CARGO_PKG_NAME");
    let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

    let mut file_path = env::temp_dir();
    file_path.push(name);

    // create parent directory
    match create_dir_all(&file_path) {
        Ok(_) => logger.verbose::<()>("Created temporary directory.", &[], None),
        Err(error) => logger.verbose("Unable to create temporary directory: ", &[&file_path.to_str().unwrap_or("???")], Some(error)),
    };

    file_path.push(file_name);
    if cfg!(windows) {
        file_path.set_extension("bat");
    } else {
        file_path.set_extension("sh");
    };

    let file_path_str = &file_path.to_str().unwrap_or("???");

    logger.verbose::<()>("Creating temporary script file: ", &[&file_path_str], None);

    let mut file = match File::create(&file_path) {
        Err(error) => {
            logger.error("Unable to create script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to create script file, error: {}", error);
        }
        Ok(file) => file,
    };

    let mut text = script_lines.join("\n");

    let cwd_holder = match current_dir() {
        Ok(value) => value,
        Err(error) => {
            logger.error("Unable to get current working directory.", &[], Some(&error));
            panic!("Unable to get current working directory, error: {}", error);
        }
    };

    let cwd = match cwd_holder.to_str() {
        Some(cwd_str) => cwd_str.clone(),
        None => {
            logger.error::<()>("Unable to get current working directory.", &[], None);
            panic!("Unable to get current working directory");
        }
    };

    text.insert_str(0, "\n");
    text.insert_str(0, cwd);
    text.insert_str(0, "cd ");

    match file.write_all(text.as_bytes()) {
        Err(error) => {
            logger.error("Unable to write to script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to write to script file, error: {}", error);
        }
        Ok(_) => logger.verbose::<()>("Written script file text:\n", &[&text], None),
    }

    let command = if cfg!(windows) {
        "cmd"
    } else {
        "sh"
    };

    let args_vector = if cfg!(windows) {
        vec!["-c".to_string(), file_path_str.to_string()]
    } else {
        vec![file_path_str.to_string()]
    };

    let args = Some(args_vector);

    run_command(&logger, &command, &args);

    match remove_file(&file_path_str) {
        Ok(_) => logger.verbose::<()>("Temporary file deleted: ", &[&file_path_str], None),
        Err(error) => logger.verbose("Unable to delete temporary file: ", &[&file_path_str], Some(error)),
    };
}

/// Runs the requested command and panics in case of any error.
pub fn run_command(
    logger: &Logger,
    command_string: &str,
    args: &Option<Vec<String>>,
) {
    logger.verbose::<()>("Execute Command: ", &[&command_string], None);
    let mut command = Command::new(&command_string);

    match *args {
        Some(ref args_vec) => {
            for arg in args_vec.iter() {
                command.arg(arg);
            }
        }
        None => logger.verbose::<()>("No command args defined.", &[], None),
    };

    command.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());
    logger.info("Execute Command: ", &[], Some(&command));

    let exit_status = command.status();
    validate_exit_code(exit_status);
}

/// Runs the given task command and if not defined, the task script.
pub fn run(
    logger: &Logger,
    step: &Step,
) {
    match step.config.command {
        Some(ref command_string) => {
            run_command(&logger, &command_string, &step.config.args);
        }
        None => {
            match step.config.script {
                Some(ref script) => run_script(&logger, script),
                None => logger.verbose::<()>("No script defined.", &[], None),
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use log;
    use std::io::ErrorKind;
    use types::Task;

    #[test]
    #[should_panic]
    fn validate_exit_code_error() {
        validate_exit_code(Err(Error::new(ErrorKind::Other, "test")));
    }

    #[test]
    fn run_no_command() {
        let logger = log::create("error");
        let task = Task {
            install_crate: None,
            command: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            script: None,
            dependencies: None
        };
        let step = Step { name: "test".to_string(), config: task };

        run(&logger, &step);
    }

    #[test]
    fn run_command() {
        let logger = log::create("error");
        let task = Task {
            command: Some("echo 1".to_string()),
            install_crate: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            script: None,
            dependencies: None
        };
        let step = Step { name: "test".to_string(), config: task };

        run(&logger, &step);
    }

    #[test]
    fn run_script() {
        let logger = log::create("error");
        let task = Task {
            script: Some(vec!["echo 1".to_string()]),
            command: None,
            install_crate: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            dependencies: None
        };
        let step = Step { name: "test".to_string(), config: task };

        run(&logger, &step);
    }
}
<|endoftext|>


<commit_before>use gl;
use gl::types::*;
use types::{Color, Viewport, BufferBits, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}
<commit_msg>Add texture binders to `Context`<commit_after>use gl;
use gl::types::*;
use types::{Color, Viewport, BufferBits, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};
use texture::{Texture2dBinder, TextureCubeMapBinder};

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder,
    pub texture_2d: Texture2dBinder,
    pub texture_cube_map: TextureCubeMapBinder
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder,
            texture_2d: Texture2dBinder,
            texture_cube_map: TextureCubeMapBinder
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}
<|endoftext|>


<commit_before>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::BufferBinder;
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<commit_msg>Add `ContextSubRef` and `ContextSubMut` type aliases<commit_after>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::{BufferBinder, BufferBinderRef, BufferBinderMut};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub type ContextSubRef<'a> = ContextOf<BufferBinderRef<'a>,
                                       &'a FramebufferBinder,
                                       &'a ProgramBinder,
                                       &'a RenderbufferBinder,
                                       &'a TextureUnits>;

pub type ContextSubMut<'a> = ContextOf<BufferBinderMut<'a>,
                                       &'a mut FramebufferBinder,
                                       &'a mut ProgramBinder,
                                       &'a mut RenderbufferBinder,
                                       &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<|endoftext|>