<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat, ExtractOption, ExtractOptions};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        let mut extract_options = ExtractOptions::new();
        extract_options.add(ExtractOption::Time);
        extract_options.add(ExtractOption::Owner);
        extract_options.add(ExtractOption::Permissions);
        try!(writer.set_options(&extract_options));
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body = [{}]", &body);
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id = [{}]", &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                buf.push_str(content.trim());
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        metadata.insert(matched_type.unwrap(), buf);
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn root() -> PathBuf {
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

}
<commit_msg>Move buffer trimming to the final buffer<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat, ExtractOption, ExtractOptions};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        let mut extract_options = ExtractOptions::new();
        extract_options.add(ExtractOption::Time);
        extract_options.add(ExtractOption::Owner);
        extract_options.add(ExtractOption::Permissions);
        try!(writer.set_options(&extract_options));
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        let filename = format!("{}", &file);
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body {} = [{}]", &filename, &body);
                let ids: Vec<String> = body.lines().map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id {} = [{}]", &filename, &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                // You used to trim. Now you don't, because you were trimming
                                // in the wrong place. Sometimes a buffer ends (or starts!) with
                                // a newline.
                                buf.push_str(content);
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        // Hey, before you go - we are trimming whitespace for you. This
                        // is handy, because later on, you just want the string you want.
                        metadata.insert(matched_type.unwrap(), String::from(buf.trim()));
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn root() -> PathBuf {
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Table", inherited=False) %>

${helpers.single_keyword(
    "table-layout",
    "auto fixed",
    engines="gecko servo-2013",
    gecko_ffi_name="mLayoutStrategy",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "-x-span",
    "Integer",
    "1",
    engines="gecko",
    spec="Internal-only (for `<col span>` pres attr)",
    animation_value_type="none",
    enabled_in="",
)}
<commit_msg>style: Replacing table-layout defines with an enum.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Table", inherited=False) %>

${helpers.single_keyword(
    "table-layout",
    "auto fixed",
    engines="gecko servo-2013",
    gecko_ffi_name="mLayoutStrategy",
    animation_value_type="discrete",
    gecko_enum_prefix="StyleTableLayout",
    spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "-x-span",
    "Integer",
    "1",
    engines="gecko",
    spec="Internal-only (for `<col span>` pres attr)",
    animation_value_type="none",
    enabled_in="",
)}
<|endoftext|>


<commit_before>//! The main searching function.
use time::uci_timer::*;

use std::cmp::{min,max};
use std::sync::atomic::{Ordering,AtomicBool};

use pleco::{MoveList,Board,BitMove};
use pleco::core::*;
use pleco::tools::tt::*;
use pleco::core::score::*;
use pleco::tools::eval::Eval;
use pleco::tools::pleco_arc::Arc;

use MAX_PLY;
use TT_TABLE;

use time::time_management::TimeManager;
use super::threads::TIMER;
use root_moves::root_moves_list::RootMoveList;
use consts::*;

const THREAD_DIST: usize = 20;
//                                      1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
static SKIP_SIZE: [u16; THREAD_DIST] = [1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4];
static START_PLY: [u16; THREAD_DIST] = [0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7];

pub struct ThreadSearcher {
    pub limit: Limits,
    pub board: Board,
    pub time_man: &'static TimeManager,
    pub tt: &'static TranspositionTable,
    pub id: usize,
    pub root_moves: RootMoveList,
    pub use_stdout: Arc<AtomicBool>,
}

impl ThreadSearcher {
    pub fn search_root(&mut self) {
        assert!(self.board.depth() == 0);
        self.root_moves.set_finished(false);
        if self.stop() {
            self.root_moves.set_finished(true);
            return;
        }

        if self.use_stdout() {
            println!("info id {} start", self.id);
        }

//        if self.main_thread() {
//            println!("info max_time: {}, ideal time: {}", self.time_man.maximum_time(), self.time_man.ideal_time());
//        }

        let max_depth = if let LimitsType::Depth(d) = self.limit.limits_type {
            d
        } else {
            MAX_PLY
        };

        let start_ply: u16 = START_PLY[self.id % THREAD_DIST];
        let skip_size: u16 = SKIP_SIZE[self.id % THREAD_DIST];
        let mut depth: u16 = start_ply;

        let mut delta: i32 = NEG_INFINITE as i32;
        #[allow(unused_assignments)]
        let mut best_value: i32 = NEG_INFINITE as i32;
        let mut alpha: i32 = NEG_INFINITE as i32;
        let mut beta: i32 = INFINITE as i32;

        let mut time_reduction: f64 = 1.0;
        let mut last_best_move: BitMove = BitMove::null();
        let mut best_move_stability: u32 = 0;

        self.root_moves.shuffle(self.id, &self.board);

        'iterative_deepening: while !self.stop() && depth < max_depth {
            self.root_moves.rollback();

            if depth >= 5 {
                delta = 18;
                alpha = max(self.root_moves.prev_best_score() - delta, NEG_INFINITE as i32);
                beta = min(self.root_moves.prev_best_score() + delta, INFINITE as i32);
            }

            'aspiration_window: loop {

                best_value = self.search::<PV>(alpha, beta, depth) as i32;
                self.root_moves.sort();

                if self.stop() {
                    break 'aspiration_window;
                }

                if best_value <= alpha {
                    alpha = max(best_value - delta, NEG_INFINITE as i32);
                } else if best_value >= beta {
                    beta = min(best_value + delta, INFINITE as i32);
                } else {
                    break 'aspiration_window;
                }
                delta += (delta / 4) + 5;

                assert!(alpha >= NEG_INFINITE as i32);
                assert!(beta <= INFINITE as i32);
            }

            self.root_moves.sort();
            if self.use_stdout() && self.main_thread() {
                println!("info depth {}", depth);
            }
            if !self.stop() {
                self.root_moves.set_depth_completed(depth);
            }
            depth += skip_size;

            if !self.main_thread() {
                continue;
            }

            let best_move = self.root_moves.first().bit_move;
            if best_move != last_best_move {
                time_reduction = 1.0;
                best_move_stability = 0;
            } else {
                time_reduction *= 0.91;
                best_move_stability += 1;
            }

            last_best_move = best_move;

            // check for time
            if let Some(_) = self.limit.use_time_management() {
                if !self.stop() {
//                    let prev_best = self.thread.root_moves.first().prev_score;
                    let ideal = TIMER.ideal_time();
                    let elapsed = TIMER.elapsed();
                    let stability: f64 = f64::powi(0.92, best_move_stability as i32);
                    let new_ideal = (ideal as f64 * stability * time_reduction) as i64;
                    println!("ideal: {}, new_ideal: {}, elapsed: {}", ideal, new_ideal, elapsed);
                    if self.root_moves.len() == 1 || TIMER.elapsed() >= new_ideal {
                        break 'iterative_deepening;
                    }
                }
            }

        }
        self.root_moves.set_finished(true);
    }

    fn search<N: PVNode>(&mut self, mut alpha: i32, beta: i32, max_depth: u16) -> i32 {
        let is_pv: bool = N::is_pv();
        let at_root: bool = self.board.depth() == 0;
        let zob = self.board.zobrist();
        let (tt_hit, tt_entry): (bool, &mut Entry) = TT_TABLE.probe(zob);
        let tt_value = if tt_hit {tt_entry.score as i32} else {0};
        let in_check: bool = self.board.in_check();
        let ply = self.board.depth();

        let mut best_move = BitMove::null();

        let mut value = NEG_INFINITE as i32;
        let mut best_value = NEG_INFINITE as i32;
        let mut moves_played = 0;

        let mut pos_eval: i32 = 0;

        if self.main_thread() {
            self.check_time();
        }

        if ply >= max_depth || self.stop() {
            return Eval::eval_low(&self.board) as i32;
        }

        let plys_to_zero = max_depth - ply;

        if !at_root {
            if alpha >= beta {
                return alpha
            }
        }

        if !is_pv
            && tt_hit
            && tt_entry.depth as u16 >= plys_to_zero
            && tt_value != 0
            && correct_bound_eq(tt_value, beta, tt_entry.node_type()) {
            return tt_value;
        }

        if in_check {
            pos_eval = 0;
        } else {
            if tt_hit {
                if tt_entry.eval == 0 {
                    pos_eval = Eval::eval_low(&self.board) as i32;
                }
                if tt_value != 0 && correct_bound(tt_value, pos_eval, tt_entry.node_type()) {
                    pos_eval = tt_value;
                }
            } else {
                pos_eval = Eval::eval_low(&self.board) as i32;
                tt_entry.place(zob, BitMove::null(), 0, pos_eval as i16, 0, NodeBound::NoBound);
            }
        }

        if !in_check {

            if ply > 3
                && ply < 7
                && pos_eval - futility_margin(ply) >= beta && pos_eval < 10000 {
                return pos_eval;
            }
        }

        #[allow(unused_mut)]
        let mut moves: MoveList = if at_root {
            self.root_moves.to_list()
        } else {
            self.board.generate_pseudolegal_moves()
        };

        if moves.is_empty() {
            if self.board.in_check() {
                return MATE as i32 + (ply as i32);
            } else {
                return DRAW as i32;
            }
        }

        if !at_root {
            mvv_lva_sort(&mut moves, &self.board);
        }

        for (i, mov) in moves.iter().enumerate() {
            if at_root || self.board.legal_move(*mov) {
                moves_played += 1;
                let gives_check: bool = self.board.gives_check(*mov);
                self.board.apply_unknown_move(*mov, gives_check);
                self.tt.prefetch(self.board.zobrist());
                let do_full_depth: bool = if max_depth >= 3 && moves_played > 1 && ply >= 2 {
                    if in_check || gives_check {
                        value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth - 1);
                    } else {
                        value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth - 2);
                    }
                    value > alpha
                } else {
                    !is_pv || moves_played > 1
                };
                if do_full_depth {
                    value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth);
                }
                if is_pv && (moves_played == 1 || (value > alpha && (at_root || value < beta))) {
                    value = -self.search::<PV>(-beta, -alpha, max_depth);
                }
                self.board.undo_move();
                assert!(value > NEG_INFINITE as i32);
                assert!(value < INFINITE as i32);
                if self.stop() {
                    return 0;
                }
                if at_root {
                    if moves_played == 1 || value as i32 > alpha {
                        self.root_moves.insert_score_depth(i,value, max_depth);
                    } else {
                        self.root_moves.insert_score(i, NEG_INFINITE as i32);
                    }
                }

                if value > best_value {
                    best_value = value;

                    if value > alpha {
                        best_move = *mov;
                        if is_pv && value < beta {
                            alpha = value;
                        } else {
//                            assert!(value >= beta);
                           break;
                        }
                    }
                }
            }
        }

        if moves_played == 0 {
            if self.board.in_check() {
                return MATE as i32 - (ply as i32);
            } else {
                return DRAW as i32;
            }
        }

        let node_bound = if best_value as i32 >= beta {NodeBound::LowerBound}
            else if is_pv && !best_move.is_null() {NodeBound::Exact}
                else {NodeBound::UpperBound};

        tt_entry.place(zob, best_move, best_value as i16, pos_eval as i16, plys_to_zero as u8, node_bound);

        best_value
    }

    // TODO: Qscience search

    fn main_thread(&self) -> bool {
        self.id == 0
    }

    fn stop(&self) -> bool {
        self.root_moves.load_stop()
    }

    fn check_time(&mut self) {
        if self.limit.use_time_management().is_some()
            && TIMER.elapsed() >= TIMER.maximum_time() {
            self.root_moves.set_stop(true);
        } else if let Some(time) = self.limit.use_movetime() {
            if self.limit.elapsed() >= time as i64 {
                self.root_moves.set_stop(true);
            }
        }
    }

    pub fn print_startup(&self) {
        if self.use_stdout() {
            println!("info id {} start", self.id);
        }
    }

    pub fn use_stdout(&self) -> bool {
        self.use_stdout.load(Ordering::Relaxed)
    }

}

fn mvv_lva_sort(moves: &mut MoveList, board: &Board) {
    moves.sort_by_key(|a| {
        let piece = board.piece_at_sq((*a).get_src()).unwrap();

        if a.is_capture() {
            piece.value() - board.captured_piece(*a).unwrap().value()
        } else if a.is_castle() {
            1
        } else if piece == Piece::P {
            if a.is_double_push().0 {
                2
            } else {
                3
            }
        } else {
            4
        }
    })
}

fn correct_bound_eq(tt_value: i32, beta: i32, bound: NodeBound) -> bool {
    if tt_value as i32 >= beta {
        bound as u8 & NodeBound::LowerBound as u8 != 0
    } else {
        bound as u8 & NodeBound::UpperBound as u8 != 0
    }
}

fn correct_bound(tt_value: i32, val: i32, bound: NodeBound) -> bool {
    if tt_value as i32 > val {
        bound as u8 & NodeBound::LowerBound as u8 != 0
    } else {
        bound as u8 & NodeBound::UpperBound as u8 != 0
    }
}

fn futility_margin(depth: u16) -> i32 {
    depth as i32 * 150
}<commit_msg>Hopefully fixing travis hanging bug.<commit_after>//! The main searching function.
use time::uci_timer::*;

use std::cmp::{min,max};
use std::sync::atomic::{Ordering,AtomicBool};

use pleco::{MoveList,Board,BitMove};
use pleco::core::*;
use pleco::tools::tt::*;
use pleco::core::score::*;
use pleco::tools::eval::Eval;
use pleco::tools::pleco_arc::Arc;

use MAX_PLY;
use TT_TABLE;

use time::time_management::TimeManager;
use super::threads::TIMER;
use root_moves::root_moves_list::RootMoveList;
use consts::*;

const THREAD_DIST: usize = 20;
//                                      1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
static SKIP_SIZE: [u16; THREAD_DIST] = [1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4];
static START_PLY: [u16; THREAD_DIST] = [0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7];

pub struct ThreadSearcher {
    pub limit: Limits,
    pub board: Board,
    pub time_man: &'static TimeManager,
    pub tt: &'static TranspositionTable,
    pub id: usize,
    pub root_moves: RootMoveList,
    pub use_stdout: Arc<AtomicBool>,
}

impl ThreadSearcher {
    pub fn search_root(&mut self) {
        assert!(self.board.depth() == 0);
        self.root_moves.set_finished(false);
        if self.stop() {
            self.root_moves.set_finished(true);
            return;
        }

        if self.use_stdout() {
            println!("info id {} start", self.id);
        }

//        if self.main_thread() {
//            println!("info max_time: {}, ideal time: {}", self.time_man.maximum_time(), self.time_man.ideal_time());
//        }

        let max_depth = if let LimitsType::Depth(d) = self.limit.limits_type {
            d
        } else {
            MAX_PLY
        };

        let start_ply: u16 = START_PLY[self.id % THREAD_DIST];
        let skip_size: u16 = SKIP_SIZE[self.id % THREAD_DIST];
        let mut depth: u16 = start_ply;

        let mut delta: i32 = NEG_INFINITE as i32;
        #[allow(unused_assignments)]
        let mut best_value: i32 = NEG_INFINITE as i32;
        let mut alpha: i32 = NEG_INFINITE as i32;
        let mut beta: i32 = INFINITE as i32;

        let mut time_reduction: f64 = 1.0;
        let mut last_best_move: BitMove = BitMove::null();
        let mut best_move_stability: u32 = 0;

        self.root_moves.shuffle(self.id, &self.board);

        'iterative_deepening: while !self.stop() && depth < max_depth {
            self.root_moves.rollback();

            if depth >= 5 {
                delta = 18;
                alpha = max(self.root_moves.prev_best_score() - delta, NEG_INFINITE as i32);
                beta = min(self.root_moves.prev_best_score() + delta, INFINITE as i32);
            }

            'aspiration_window: loop {

                best_value = self.search::<PV>(alpha, beta, depth) as i32;
                self.root_moves.sort();

                if self.stop() {
                    break 'aspiration_window;
                }

                if best_value <= alpha {
                    alpha = max(best_value - delta, NEG_INFINITE as i32);
                } else if best_value >= beta {
                    beta = min(best_value + delta, INFINITE as i32);
                } else {
                    break 'aspiration_window;
                }
                delta += (delta / 4) + 5;

                assert!(alpha >= NEG_INFINITE as i32);
                assert!(beta <= INFINITE as i32);
            }

            self.root_moves.sort();
            if self.use_stdout() && self.main_thread() {
                println!("info depth {}", depth);
            }
            if !self.stop() {
                self.root_moves.set_depth_completed(depth);
            }
            depth += skip_size;

            if !self.main_thread() {
                continue;
            }

            let best_move = self.root_moves.first().bit_move;
            if best_move != last_best_move {
                time_reduction = 1.0;
                best_move_stability = 0;
            } else {
                time_reduction *= 0.91;
                best_move_stability += 1;
            }

            last_best_move = best_move;

            // check for time
            if let Some(_) = self.limit.use_time_management() {
                if !self.stop() {
//                    let prev_best = self.thread.root_moves.first().prev_score;
                    let ideal = TIMER.ideal_time();
                    let elapsed = TIMER.elapsed();
                    let stability: f64 = f64::powi(0.92, best_move_stability as i32);
                    let new_ideal = (ideal as f64 * stability * time_reduction) as i64;
                    println!("ideal: {}, new_ideal: {}, elapsed: {}", ideal, new_ideal, elapsed);
                    if self.root_moves.len() == 1 || TIMER.elapsed() >= new_ideal {
                        break 'iterative_deepening;
                    }
                }
            }

        }
        self.root_moves.set_finished(true);
    }

    fn search<N: PVNode>(&mut self, mut alpha: i32, beta: i32, max_depth: u16) -> i32 {
        let is_pv: bool = N::is_pv();
        let at_root: bool = self.board.depth() == 0;
        let zob = self.board.zobrist();
        let (tt_hit, tt_entry): (bool, &mut Entry) = TT_TABLE.probe(zob);
        let tt_value = if tt_hit {tt_entry.score as i32} else {0};
        let in_check: bool = self.board.in_check();
        let ply = self.board.depth();

        let mut best_move = BitMove::null();

        let mut value = NEG_INFINITE as i32;
        let mut best_value = NEG_INFINITE as i32;
        let mut moves_played = 0;

        let mut pos_eval: i32 = 0;

        if self.main_thread() {
            self.check_time();
        }

        if ply >= max_depth || self.stop() {
            return Eval::eval_low(&self.board) as i32;
        }

        let plys_to_zero = max_depth - ply;

        if !at_root {
            if alpha >= beta {
                return alpha
            }
        }

        if !is_pv
            && tt_hit
            && tt_entry.depth as u16 >= plys_to_zero
            && tt_value != 0
            && correct_bound_eq(tt_value, beta, tt_entry.node_type()) {
            return tt_value;
        }

        if in_check {
            pos_eval = 0;
        } else {
            if tt_hit {
                if tt_entry.eval == 0 {
                    pos_eval = Eval::eval_low(&self.board) as i32;
                }
                if tt_value != 0 && correct_bound(tt_value, pos_eval, tt_entry.node_type()) {
                    pos_eval = tt_value;
                }
            } else {
                pos_eval = Eval::eval_low(&self.board) as i32;
                tt_entry.place(zob, BitMove::null(), 0, pos_eval as i16, 0, NodeBound::NoBound);
            }
        }

        if !in_check {

            if ply > 3
                && ply < 7
                && pos_eval - futility_margin(ply) >= beta && pos_eval < 10000 {
                return pos_eval;
            }
        }

        #[allow(unused_mut)]
        let mut moves: MoveList = if at_root {
            self.root_moves.to_list()
        } else {
            self.board.generate_pseudolegal_moves()
        };

        if moves.is_empty() {
            if self.board.in_check() {
                return MATE as i32 - (ply as i32);
            } else {
                return DRAW as i32;
            }
        }

        if !at_root {
            mvv_lva_sort(&mut moves, &self.board);
        }

        for (i, mov) in moves.iter().enumerate() {
            if at_root || self.board.legal_move(*mov) {
                moves_played += 1;
                let gives_check: bool = self.board.gives_check(*mov);
                self.board.apply_unknown_move(*mov, gives_check);
                self.tt.prefetch(self.board.zobrist());
                let do_full_depth: bool = if max_depth >= 3 && moves_played > 1 && ply >= 2 {
                    if in_check || gives_check {
                        value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth - 1);
                    } else {
                        value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth - 2);
                    }
                    value > alpha
                } else {
                    !is_pv || moves_played > 1
                };
                if do_full_depth {
                    value = -self.search::<NonPV>(-(alpha+1), -alpha, max_depth);
                }
                if is_pv && (moves_played == 1 || (value > alpha && (at_root || value < beta))) {
                    value = -self.search::<PV>(-beta, -alpha, max_depth);
                }
                self.board.undo_move();
                assert!(value > NEG_INFINITE as i32);
                assert!(value < INFINITE as i32);
                if self.stop() {
                    return 0;
                }
                if at_root {
                    if moves_played == 1 || value as i32 > alpha {
                        self.root_moves.insert_score_depth(i,value, max_depth);
                    } else {
                        self.root_moves.insert_score(i, NEG_INFINITE as i32);
                    }
                }

                if value > best_value {
                    best_value = value;

                    if value > alpha {
                        best_move = *mov;
                        if is_pv && value < beta {
                            alpha = value;
                        } else {
//                            assert!(value >= beta);
                           break;
                        }
                    }
                }
            }
        }

        if moves_played == 0 {
            if self.board.in_check() {
                return MATE as i32 - (ply as i32);
            } else {
                return DRAW as i32;
            }
        }

        let node_bound = if best_value as i32 >= beta {NodeBound::LowerBound}
            else if is_pv && !best_move.is_null() {NodeBound::Exact}
                else {NodeBound::UpperBound};

        tt_entry.place(zob, best_move, best_value as i16, pos_eval as i16, plys_to_zero as u8, node_bound);

        best_value
    }

    // TODO: Qscience search

    fn main_thread(&self) -> bool {
        self.id == 0
    }

    fn stop(&self) -> bool {
        self.root_moves.load_stop()
    }

    fn check_time(&mut self) {
        if self.limit.use_time_management().is_some()
            && TIMER.elapsed() >= TIMER.maximum_time() {
            self.root_moves.set_stop(true);
        } else if let Some(time) = self.limit.use_movetime() {
            if self.limit.elapsed() >= time as i64 {
                self.root_moves.set_stop(true);
            }
        }
    }

    pub fn print_startup(&self) {
        if self.use_stdout() {
            println!("info id {} start", self.id);
        }
    }

    pub fn use_stdout(&self) -> bool {
        self.use_stdout.load(Ordering::Relaxed)
    }

}

fn mvv_lva_sort(moves: &mut MoveList, board: &Board) {
    moves.sort_by_key(|a| {
        let piece = board.piece_at_sq((*a).get_src()).unwrap();

        if a.is_capture() {
            piece.value() - board.captured_piece(*a).unwrap().value()
        } else if a.is_castle() {
            1
        } else if piece == Piece::P {
            if a.is_double_push().0 {
                2
            } else {
                3
            }
        } else {
            4
        }
    })
}

fn correct_bound_eq(tt_value: i32, beta: i32, bound: NodeBound) -> bool {
    if tt_value as i32 >= beta {
        bound as u8 & NodeBound::LowerBound as u8 != 0
    } else {
        bound as u8 & NodeBound::UpperBound as u8 != 0
    }
}

fn correct_bound(tt_value: i32, val: i32, bound: NodeBound) -> bool {
    if tt_value as i32 > val {
        bound as u8 & NodeBound::LowerBound as u8 != 0
    } else {
        bound as u8 & NodeBound::UpperBound as u8 != 0
    }
}

fn futility_margin(depth: u16) -> i32 {
    depth as i32 * 150
}<|endoftext|>


<commit_before>/// Incremental Graph Representations

use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use adapton::engine::{cell, ns, name_of_str, name_unit, Name};
use adapton::collections::{list_of_tree, tree_fold_seq, tree_of_list, Dir2, List, ListIntro,
                           MapIntro, MapElim, SetIntro, Tree, TreeIntro};
use adapton::collections::trie::{trie_fold_seq, trie_fold_seq_nm, Set, Trie,
                                 TrieIntro};

/// Representation of a graph as a list of edges, where edges are
/// a pair of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Graph<Node> {
    edge_tree: Tree<(Node, Node)>,
}

/// Representation of a graph as finite map from node ids to
/// an outgoing adjacency list of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct AdjacencyGraph<Node>
    where Node: Debug + Clone + Hash + PartialEq + Eq + 'static
{
    adjacency_map: Trie<(Node, Tree<Node>)>,
}

/// Produce a graph.
pub trait GraphIntro<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Yields an empty graph, i.e. no vertices or edges.
    fn empty() -> Self;
    /// Adds the edge `(src, dst)` named `nm` to the graph `graph`.
    fn add_edge(graph: Self, nm: Name, src: Node, dst: Node) -> Self;
}

/// Reduce a graph to a value.
pub trait GraphElim<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Returns a set of the edges in the graph `graph`.
    fn edges(graph: &Self) -> Set<(Node, Node)>;
    /// Returns a set of the vertices of the graph `graph`.
    fn vertices(graph: &Self) -> Set<Node>;
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphIntro<Node> for Graph<Node> {
    fn empty() -> Graph<Node> {
        Graph::<Node> { edge_tree: Tree::nil() }
    }

    fn add_edge(graph: Graph<Node>, nm: Name, src: Node, dst: Node) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(graph.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let el = List::cons((src, dst), el);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphElim<Node> for Graph<Node> {
    fn edges(graph: &Graph<Node>) -> Set<(Node, Node)> {
        tree_fold_seq(graph.edge_tree.clone(),
                      Dir2::Left,
                      SetIntro::empty(),
                      Rc::new(|e, set| SetIntro::add(set, e)),
                      Rc::new(|_, set| set),
                      Rc::new(|nm: Name, _, set| {
                          TrieIntro::name(nm.clone(), TrieIntro::art(cell(nm, set)))
                      }))
    }

    fn vertices(graph: &Graph<Node>) -> Set<Node> {
        let edge_trie = ns(name_of_str("edge_trie_vertices"), || Self::edges(graph));
        trie_fold_seq(edge_trie,
                      SetIntro::empty(),
                      Rc::new(|((src,dst), ()), set| {
                          let add_src = SetIntro::add(set, src);
                          SetIntro::add(add_src, dst)
                      }),
                      Rc::new(move |set| set),
                      Rc::new(move |n: Name, set| {
                          Set::name(n.clone(), Set::art(cell(n, set)))
                      }))
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static>
    GraphIntro<Node> for AdjacencyGraph<Node> {
        fn empty() -> AdjacencyGraph<Node> {
            AdjacencyGraph::<Node> {
                adjacency_map: MapIntro::empty(),
            }
        }

        fn add_edge(graph: AdjacencyGraph<Node>,
                    nm: Name, src: Node, dst: Node) -> AdjacencyGraph<Node> {
            let src_adj_list = MapElim::find(&graph.adjacency_map, &src);
            match src_adj_list {
                None => {
                    // let adj = List::name_art(Some(nm.clone()), List::nil());
                    let adj = List::cons(dst, List::nil());
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
                Some(adj_nodes) => {
                    let adj = list_of_tree(adj_nodes, Dir2::Left);
                    // let adj = List::name_art(Some(nm.clone()), adj);
                    let adj = List::cons(dst, adj);
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
            }
        }
    }

impl<Node: Debug + Copy + Clone + Hash + PartialEq + Eq + 'static>
    GraphElim<Node> for AdjacencyGraph<Node> {
        fn edges(graph: &AdjacencyGraph<Node>) -> Set<(Node, Node)> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set|
                                  tree_fold_seq(dsts, Dir2::Left, set,
                                                Rc::new(move |dst, set|
                                                        SetIntro::add(set, (src, dst))),
                                                Rc::new(|_, set| set),
                                                Rc::new(|nm: Name, _, set| {
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm,
                                                                                set)))
                                                }))),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set|
                                  TrieIntro::name(nm.clone(),
                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn vertices(graph: &AdjacencyGraph<Node>) -> Set<Node> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set| {
                              let src_set = SetIntro::add(set, src);
                              tree_fold_seq(dsts, Dir2::Left, src_set,
                                            Rc::new(|dst, set| SetIntro::add(set, dst)),
                                            Rc::new(|_, set| set),
                                            Rc::new(|nm: Name, _, set|
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm, set)))))
                          }),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set| TrieIntro::name(nm.clone(),
                                                                  TrieIntro::art(cell(nm, set)))))
        }
    }

pub fn adjacency_of_edge_list<X: Hash + Clone + Debug + PartialEq + Eq>(el_graph: &Graph<X>)
                                                                        -> AdjacencyGraph<X> {
    let adj_graph = AdjacencyGraph::empty();
    trie_fold_seq_nm(Graph::edges(el_graph), adj_graph, None,
                     Rc::new(|nm, ((src,dst),()), g| {
                         if let Some(nm) = nm {
                             AdjacencyGraph::add_edge(g, nm, src, dst)
                         } else {
                             AdjacencyGraph::add_edge(g, name_unit(), src, dst)
                         }
                     }),
                     Rc::new(|g| g),
                     Rc::new(|nm: Name, g: AdjacencyGraph<_>| {
                         let adj = TrieIntro::name(nm.clone(),
                                                   TrieIntro::art(cell(nm, g.adjacency_map)));
                         AdjacencyGraph {
                             adjacency_map: adj,
                         }
                     }))
}
<commit_msg>Formatting<commit_after>/// Incremental Graph Representations

use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use adapton::engine::{cell, ns, name_of_str, name_unit, Name};
use adapton::collections::{list_of_tree, tree_fold_seq, tree_of_list, Dir2, List, ListIntro,
                           MapIntro, MapElim, SetIntro, Tree, TreeIntro};
use adapton::collections::trie::{trie_fold_seq, trie_fold_seq_nm, Set, Trie, TrieIntro};

/// Representation of a graph as a list of edges, where edges are
/// a pair of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Graph<Node> {
    edge_tree: Tree<(Node, Node)>,
}

/// Representation of a graph as finite map from node ids to
/// an outgoing adjacency list of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct AdjacencyGraph<Node>
    where Node: Debug + Clone + Hash + PartialEq + Eq + 'static
{
    adjacency_map: Trie<(Node, Tree<Node>)>,
}

/// Produce a graph.
pub trait GraphIntro<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Yields an empty graph, i.e. no vertices or edges.
    fn empty() -> Self;
    /// Adds the edge `(src, dst)` named `nm` to the graph `graph`.
    fn add_edge(graph: Self, nm: Name, src: Node, dst: Node) -> Self;
}

/// Reduce a graph to a value.
pub trait GraphElim<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Returns a set of the edges in the graph `graph`.
    fn edges(graph: &Self) -> Set<(Node, Node)>;
    /// Returns a set of the vertices of the graph `graph`.
    fn vertices(graph: &Self) -> Set<Node>;
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphIntro<Node> for Graph<Node> {
    fn empty() -> Graph<Node> {
        Graph::<Node> { edge_tree: Tree::nil() }
    }

    fn add_edge(graph: Graph<Node>, nm: Name, src: Node, dst: Node) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(graph.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let el = List::cons((src, dst), el);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphElim<Node> for Graph<Node> {
    fn edges(graph: &Graph<Node>) -> Set<(Node, Node)> {
        tree_fold_seq(graph.edge_tree.clone(),
                      Dir2::Left,
                      SetIntro::empty(),
                      Rc::new(|e, set| SetIntro::add(set, e)),
                      Rc::new(|_, set| set),
                      Rc::new(|nm: Name, _, set| {
                          TrieIntro::name(nm.clone(), TrieIntro::art(cell(nm, set)))
                      }))
    }

    fn vertices(graph: &Graph<Node>) -> Set<Node> {
        let edge_trie = ns(name_of_str("edge_trie_vertices"), || Self::edges(graph));
        trie_fold_seq(edge_trie,
                      SetIntro::empty(),
                      Rc::new(|((src,dst), ()), set| {
                          let add_src = SetIntro::add(set, src);
                          SetIntro::add(add_src, dst)
                      }),
                      Rc::new(move |set| set),
                      Rc::new(move |n: Name, set| {
                          Set::name(n.clone(), Set::art(cell(n, set)))
                      }))
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static>
    GraphIntro<Node> for AdjacencyGraph<Node> {
        fn empty() -> AdjacencyGraph<Node> {
            AdjacencyGraph::<Node> {
                adjacency_map: MapIntro::empty(),
            }
        }

        fn add_edge(graph: AdjacencyGraph<Node>,
                    nm: Name, src: Node, dst: Node) -> AdjacencyGraph<Node> {
            let src_adj_list = MapElim::find(&graph.adjacency_map, &src);
            match src_adj_list {
                None => {
                    // let adj = List::name_art(Some(nm.clone()), List::nil());
                    let adj = List::cons(dst, List::nil());
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
                Some(adj_nodes) => {
                    let adj = list_of_tree(adj_nodes, Dir2::Left);
                    // let adj = List::name_art(Some(nm.clone()), adj);
                    let adj = List::cons(dst, adj);
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
            }
        }
    }

impl<Node: Debug + Copy + Clone + Hash + PartialEq + Eq + 'static>
    GraphElim<Node> for AdjacencyGraph<Node> {
        fn edges(graph: &AdjacencyGraph<Node>) -> Set<(Node, Node)> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set|
                                  tree_fold_seq(dsts, Dir2::Left, set,
                                                Rc::new(move |dst, set|
                                                        SetIntro::add(set, (src, dst))),
                                                Rc::new(|_, set| set),
                                                Rc::new(|nm: Name, _, set| {
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm,
                                                                                set)))
                                                }))),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set|
                                  TrieIntro::name(nm.clone(),
                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn vertices(graph: &AdjacencyGraph<Node>) -> Set<Node> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set| {
                              let src_set = SetIntro::add(set, src);
                              tree_fold_seq(dsts, Dir2::Left, src_set,
                                            Rc::new(|dst, set| SetIntro::add(set, dst)),
                                            Rc::new(|_, set| set),
                                            Rc::new(|nm: Name, _, set|
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm, set)))))
                          }),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set| TrieIntro::name(nm.clone(),
                                                                  TrieIntro::art(cell(nm, set)))))
        }
    }

pub fn adjacency_of_edge_list<X: Hash + Clone + Debug + PartialEq + Eq>(el_graph: &Graph<X>)
                                                                        -> AdjacencyGraph<X> {
    let adj_graph = AdjacencyGraph::empty();
    trie_fold_seq_nm(Graph::edges(el_graph), adj_graph, None,
                     Rc::new(|nm, ((src,dst),()), g| {
                         if let Some(nm) = nm {
                             AdjacencyGraph::add_edge(g, nm, src, dst)
                         } else {
                             AdjacencyGraph::add_edge(g, name_unit(), src, dst)
                         }
                     }),
                     Rc::new(|g| g),
                     Rc::new(|nm: Name, g: AdjacencyGraph<_>| {
                         let adj = TrieIntro::name(nm.clone(),
                                                   TrieIntro::art(cell(nm, g.adjacency_map)));
                         AdjacencyGraph {
                             adjacency_map: adj,
                         }
                     }))
}
<|endoftext|>


<commit_before>extern crate rustbox;
extern crate yoga;
extern crate yoga_rustbox;
extern crate yoga_wrapper;

use yoga::{Backend, Builds, Renders, Renderable};

use std::error::Error;
use std::default::Default;

use rustbox::RustBox;
use rustbox::Key;

fn main() {
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let builder = yoga_rustbox::Builder::new();

    let mut text = builder.text("Yo!");
    text.set_height(3.0);
    text.set_align_self(yoga_wrapper::Align::Center);
    text.set_flex_grow(1.0);
    text.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::Cyan)));

    let mut image = builder.view();
    image.set_width(8.0);
    image.set_margin(yoga_wrapper::Edge::End, 2.0);
    image.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::Cyan)));

    let mut root = builder.view();
    root.set_width(50.0);
    root.set_height(12.0);
    root.set_flex_direction(yoga_wrapper::FlexDirection::Row);
    root.set_padding(yoga_wrapper::Edge::All, 2.0);
    root.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::White)));

    root.insert_child(&image, 0);
    root.insert_child(&text, 1);

    root.calculate_layout();

    yoga_rustbox::Backend::new(&rustbox).render(&root);

    loop {
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }
                    _ => {}
                }
            }
            Err(e) => panic!("{}", e.description()),
            _ => {}
        }
    }
}
<commit_msg>fix(example): c example should use view<commit_after>extern crate rustbox;
extern crate yoga;
extern crate yoga_rustbox;
extern crate yoga_wrapper;

use yoga::{Backend, Builds, Renders, Renderable};

use std::error::Error;
use std::default::Default;

use rustbox::RustBox;
use rustbox::Key;

fn main() {
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let builder = yoga_rustbox::Builder::new();

    let mut text = builder.view();
    text.set_height(3.0);
    text.set_align_self(yoga_wrapper::Align::Center);
    text.set_flex_grow(1.0);
    text.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::Cyan)));

    let mut image = builder.view();
    image.set_width(8.0);
    image.set_margin(yoga_wrapper::Edge::End, 2.0);
    image.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::Cyan)));

    let mut root = builder.view();
    root.set_width(50.0);
    root.set_height(12.0);
    root.set_flex_direction(yoga_wrapper::FlexDirection::Row);
    root.set_padding(yoga_wrapper::Edge::All, 2.0);
    root.set_background_color(Some(yoga::style::BackgroundColor::Color(rustbox::Color::White)));

    root.insert_child(&image, 0);
    root.insert_child(&text, 1);

    root.calculate_layout();

    yoga_rustbox::Backend::new(&rustbox).render(&root);

    loop {
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }
                    _ => {}
                }
            }
            Err(e) => panic!("{}", e.description()),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;
use rand::Rng;
use std::cmp::{Ord, Ordering};
use std::fmt;

// comands in config-file start with
pub const CONF_LINE_IDENTIFIER__CONTROL: char = ':';

// the default can be changed by one of the following switches
const DEFAULT_REWARDING_SCHEME: RewardingScheme = RewardingScheme::UnhideWhenGuessedChar;

// Keyword to switch rewarding scheme
// :traditional-rewarding
const UNHIDE_WHEN_LOST_LIVE_IDENTIFIER: &str = "traditional-rewarding";

// Keyword to switch rewarding scheme
// :success-rewarding
const UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER: &str = "success-rewarding";

// images in config file start with
pub const CONF_LINE_IDENTIFIER__IMAGE: char = '|';

const BIG_IMAGE: usize = 100; // sort algorithm <-> random algorithm

// first char of image lines must be '|'
const DEFAULT_IMAGES: & [&str] = &[
    "
|    ,,,,,
|   (o   o)
|    /. .\\
|   (_____)
|     : :
|    ##O##'
|  ,,,: :,,,
| _)\\ : : /(____
|{  \\     /  ___}
| \\/)     ((/
|  (_______)
|    :   :
|    :   :
|   / \\ / \\
|   \"\"\" \"\"\"
",
    "
|    |\\_|X|_/|
|   /         \\
| =(  O     O  )=
|  -\\    o    /-
|   / .-----. \\
| /_ | o   o |_ \\
|(U  |       |  U)
|   _|_     _|_
|  (   )---(   )
",
    "
|        _.---._    /\\\\
|     ./'       \"--`\\//
|   ./              o \\
|  /./\\  )______   \\__ \\
| ./  / /\\ \\   | \\ \\  \\ \\
|    / /  \\ \\  | |\\ \\  \\7
|     \"     \"    \"  \"        VK
",
    "
|       ,.
|      (_|,.
|     ,' /, )_______   _
|  __j o``-'        `.'-)'
| (\")                 \\'
|  `-j                |
|    `-._(           /
|       |_\\  |--^.  /
|      /_]'|_| /_)_/
|         /_]'  /_]'
# Author: hjw
",
    "
|        _
|       [ ]
|      (   )
|       |>|
|    __/===\\__
|   //| o=o |\\\\
| <]  | o=o |  [>
|     \\=====/
|    / / | \\ \\
|   <_________>
",
    "
|                          (_)(_)
|                          /     \\
|                         /       |
|                        /   \\  * |
|          ________     /    /\\__/
|  _      /        \\   /    /
| / \\    /  ____    \\_/    /
|//\\ \\  /  /    \\         /
|V  \\ \\/  /      \\       /
|    \\___/        \\_____/
",
    "
|         .-.
|        (. .)__,')
|        / V      )
|  ()    \\  (   \\/
|<)-`\\()  `._`._ \\
|  <).>=====<<==`'====
|   C-'`(>
# Author: hjw
",
    "
| >(. )
|  |  (     /)
|  |   \\___/ )
|  (   ----- )  >@)_//   >@)_//  >@)_//  >@)_//
|   \\_______/    (__)     (__)    (__)    (__)
|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
",
    "
|           __
|           /(`o
|     ,-,  //  \\\\
|    (,,,) ||   V
|   (,,,,)\\//
|   (,,,/w)-'
|   \\,,/w)
|   `V/uu
|     / |
|     | |
|     o o
|     \\ |
|\\,/  ,\\|,.  \\,/
",
    "
|o
| \\_/\\o
|( Oo)                    \\|/
|(_=-)  .===O-  ~~Z~A~P~~ -O-
|/   \\_/U'                /|\\
|||  |_/
|\\\\  |
|{K ||
| | PP
| | ||
| (__\\\\
# Author: ac
",
    "
|      ______
|     /     /\\
|    /     /  \\
|   /_____/----\\_    (
|  \"     \"          ).
| _ ___          o (:') o
|(@))_))        o ~/~~\\~ o
|                o  o  o
",
    "
|                             _______     |\\
|                            |License|    | \\
|  _____                     | ~~*~~ |    |  \\
| |     |  (((        .--.   |_______|    |
| |DrJRO| ~OvO~ __   (////)               |
| |     | ( _ )|==|   \\__/                |
| |o    |  \\_/ |_(|  /    \\   _______     |
| |     | //|\\\\   \\\\//|  |\\\\  |__o__|     |
| |   __|//\\_/\\\\ __\\/ |__|//  |__o__|     |
| |  |==\"\"//=\\\\\"\"====|||||)   |__o__|     |
|_|__||_|_||_||_____||||||____|__o__|_____|
|    ||  (_) (_)    ||||||                \\
|    []             [(_)(_)
",
];

#[derive(PartialOrd, Eq, PartialEq, Debug, Copy, Clone)] //omitting Ord
pub struct ImageChar {
    pub point: (u8, u8),
    pub char_: char,
}

impl fmt::Display for ImageChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.char_)
    }
}

// Ord enables us to v.sort()
impl Ord for ImageChar {
    fn cmp(&self, other: &Self) -> Ordering {
        fn weight(ic: &ImageChar) -> isize {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = ic;
            // points near the lower left corner are light
            x as isize - y as isize
        }
        weight(&self).cmp(&weight(&other))
    }
}

#[derive(Debug, PartialEq)]
pub enum RewardingScheme {
    UnhideWhenLostLife,
    UnhideWhenGuessedChar,
}

#[derive(Debug)]
pub struct Image {
    pub ichars: Vec<ImageChar>,
    pub offset: (usize, usize),
    pub dimension: (u8, u8),
    pub visible_points: usize,
    pub rewarding_scheme: RewardingScheme,
}

impl fmt::Display for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut s = String::new();
        for ic in self.ichars.iter().take(self.visible_points) {
            let &ImageChar {
                point: (x, y),
                char_: c,
            } = ic;
            s = s
                + "\x1b["
                + &(y as usize + 1 + self.offset.1).to_string()
                + ";"
                + &(x as usize + 1 + self.offset.0).to_string()
                + "f"
                + &c.to_string();
        }
        // after printing the image s, bring the cursor below
        write!(
            f,
            "{}\x1b[{};0f",
            s,
            (self.dimension.1 as usize + 1 + self.offset.1).to_string()
        )
    }
}

impl Image {
    pub fn new(string: &str, offset: (usize, usize)) -> Image {
        let mut v: Vec<ImageChar> = Vec::new();

        let mut rewarding_scheme: RewardingScheme = DEFAULT_REWARDING_SCHEME;
        for (y, line) in string
            // split in lines
            .lines()
            // interpret identifier line
            .filter_map(|l| {
                if l.starts_with(CONF_LINE_IDENTIFIER__CONTROL) {
                    if l[1..].trim().contains(UNHIDE_WHEN_LOST_LIVE_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenLostLife;
                    }
                    if l[1..].trim().contains(UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenGuessedChar;
                    }
                    None
                } else {
                    Some(l)
                }
            })
            // consider only lines starting with '|'
            .filter(|&l| l.starts_with(CONF_LINE_IDENTIFIER__IMAGE))
            .enumerate()
        //.inspect(|&(n,l)| println!("line {:?}: {:?} ", n,l))
        {
            let mut ii: Vec<_> = line
                .char_indices()
                // skip first char '|'
                .skip(1)
                // consider only chars != ' '
                .filter(|&(_, c)| c != ' ')
                // save in ImageChar object
                .map(|(x, c)| ImageChar {
                    point: (x as u8, y as u8),
                    char_: c,
                })
                .collect();
            v.append(&mut ii);
        }

        // find dimensions
        let mut x_max = 0;
        let mut y_max = 0;
        for i in v.iter() {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = i;
            if x > x_max {
                x_max = x
            };
            if y > y_max {
                y_max = y
            };
        }

        // order points
        let v_len = v.len();
        if v_len <= BIG_IMAGE {
            v.sort(); // Sort algorithm, see "impl Ord for ImageChar"
        } else {
            rand::thread_rng().shuffle(&mut v); // points appear randomly.
        }

        if v.len() == 0 {
            Image::new(rand::thread_rng().choose(&DEFAULT_IMAGES).unwrap(), offset)
        } else {
            Image {
                ichars: v,
                offset: offset,
                dimension: (x_max,y_max),
                visible_points: v_len,
                rewarding_scheme: rewarding_scheme,
            }
        }
    }

    pub fn disclose(&mut self, lives_frac: (usize, usize), guessed_chars_frac: (usize, usize)) {
        let l = self.ichars.len();

        let as_points = |(n, d)| (3 * l * (d - n) as usize / d as usize + 1 * l) / 4;

        self.visible_points = match self.rewarding_scheme {
            RewardingScheme::UnhideWhenGuessedChar => as_points(guessed_chars_frac),
            RewardingScheme::UnhideWhenLostLife => as_points(lives_frac),
        };
    }
}
<commit_msg>replace unneeded field pattern<commit_after>extern crate rand;
use rand::Rng;
use std::cmp::{Ord, Ordering};
use std::fmt;

// comands in config-file start with
pub const CONF_LINE_IDENTIFIER__CONTROL: char = ':';

// the default can be changed by one of the following switches
const DEFAULT_REWARDING_SCHEME: RewardingScheme = RewardingScheme::UnhideWhenGuessedChar;

// Keyword to switch rewarding scheme
// :traditional-rewarding
const UNHIDE_WHEN_LOST_LIVE_IDENTIFIER: &str = "traditional-rewarding";

// Keyword to switch rewarding scheme
// :success-rewarding
const UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER: &str = "success-rewarding";

// images in config file start with
pub const CONF_LINE_IDENTIFIER__IMAGE: char = '|';

const BIG_IMAGE: usize = 100; // sort algorithm <-> random algorithm

// first char of image lines must be '|'
const DEFAULT_IMAGES: & [&str] = &[
    "
|    ,,,,,
|   (o   o)
|    /. .\\
|   (_____)
|     : :
|    ##O##'
|  ,,,: :,,,
| _)\\ : : /(____
|{  \\     /  ___}
| \\/)     ((/
|  (_______)
|    :   :
|    :   :
|   / \\ / \\
|   \"\"\" \"\"\"
",
    "
|    |\\_|X|_/|
|   /         \\
| =(  O     O  )=
|  -\\    o    /-
|   / .-----. \\
| /_ | o   o |_ \\
|(U  |       |  U)
|   _|_     _|_
|  (   )---(   )
",
    "
|        _.---._    /\\\\
|     ./'       \"--`\\//
|   ./              o \\
|  /./\\  )______   \\__ \\
| ./  / /\\ \\   | \\ \\  \\ \\
|    / /  \\ \\  | |\\ \\  \\7
|     \"     \"    \"  \"        VK
",
    "
|       ,.
|      (_|,.
|     ,' /, )_______   _
|  __j o``-'        `.'-)'
| (\")                 \\'
|  `-j                |
|    `-._(           /
|       |_\\  |--^.  /
|      /_]'|_| /_)_/
|         /_]'  /_]'
# Author: hjw
",
    "
|        _
|       [ ]
|      (   )
|       |>|
|    __/===\\__
|   //| o=o |\\\\
| <]  | o=o |  [>
|     \\=====/
|    / / | \\ \\
|   <_________>
",
    "
|                          (_)(_)
|                          /     \\
|                         /       |
|                        /   \\  * |
|          ________     /    /\\__/
|  _      /        \\   /    /
| / \\    /  ____    \\_/    /
|//\\ \\  /  /    \\         /
|V  \\ \\/  /      \\       /
|    \\___/        \\_____/
",
    "
|         .-.
|        (. .)__,')
|        / V      )
|  ()    \\  (   \\/
|<)-`\\()  `._`._ \\
|  <).>=====<<==`'====
|   C-'`(>
# Author: hjw
",
    "
| >(. )
|  |  (     /)
|  |   \\___/ )
|  (   ----- )  >@)_//   >@)_//  >@)_//  >@)_//
|   \\_______/    (__)     (__)    (__)    (__)
|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
",
    "
|           __
|           /(`o
|     ,-,  //  \\\\
|    (,,,) ||   V
|   (,,,,)\\//
|   (,,,/w)-'
|   \\,,/w)
|   `V/uu
|     / |
|     | |
|     o o
|     \\ |
|\\,/  ,\\|,.  \\,/
",
    "
|o
| \\_/\\o
|( Oo)                    \\|/
|(_=-)  .===O-  ~~Z~A~P~~ -O-
|/   \\_/U'                /|\\
|||  |_/
|\\\\  |
|{K ||
| | PP
| | ||
| (__\\\\
# Author: ac
",
    "
|      ______
|     /     /\\
|    /     /  \\
|   /_____/----\\_    (
|  \"     \"          ).
| _ ___          o (:') o
|(@))_))        o ~/~~\\~ o
|                o  o  o
",
    "
|                             _______     |\\
|                            |License|    | \\
|  _____                     | ~~*~~ |    |  \\
| |     |  (((        .--.   |_______|    |
| |DrJRO| ~OvO~ __   (////)               |
| |     | ( _ )|==|   \\__/                |
| |o    |  \\_/ |_(|  /    \\   _______     |
| |     | //|\\\\   \\\\//|  |\\\\  |__o__|     |
| |   __|//\\_/\\\\ __\\/ |__|//  |__o__|     |
| |  |==\"\"//=\\\\\"\"====|||||)   |__o__|     |
|_|__||_|_||_||_____||||||____|__o__|_____|
|    ||  (_) (_)    ||||||                \\
|    []             [(_)(_)
",
];

#[derive(PartialOrd, Eq, PartialEq, Debug, Copy, Clone)] //omitting Ord
pub struct ImageChar {
    pub point: (u8, u8),
    pub char_: char,
}

impl fmt::Display for ImageChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.char_)
    }
}

// Ord enables us to v.sort()
impl Ord for ImageChar {
    fn cmp(&self, other: &Self) -> Ordering {
        fn weight(ic: &ImageChar) -> isize {
            let &ImageChar {
                point: (x, y),
                ..
            } = ic;
            // points near the lower left corner are light
            x as isize - y as isize
        }
        weight(&self).cmp(&weight(&other))
    }
}

#[derive(Debug, PartialEq)]
pub enum RewardingScheme {
    UnhideWhenLostLife,
    UnhideWhenGuessedChar,
}

#[derive(Debug)]
pub struct Image {
    pub ichars: Vec<ImageChar>,
    pub offset: (usize, usize),
    pub dimension: (u8, u8),
    pub visible_points: usize,
    pub rewarding_scheme: RewardingScheme,
}

impl fmt::Display for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut s = String::new();
        for ic in self.ichars.iter().take(self.visible_points) {
            let &ImageChar {
                point: (x, y),
                ..
            } = ic;
            s = s
                + "\x1b["
                + &(y as usize + 1 + self.offset.1).to_string()
                + ";"
                + &(x as usize + 1 + self.offset.0).to_string()
                + "f"
                + &c.to_string();
        }
        // after printing the image s, bring the cursor below
        write!(
            f,
            "{}\x1b[{};0f",
            s,
            (self.dimension.1 as usize + 1 + self.offset.1).to_string()
        )
    }
}

impl Image {
    pub fn new(string: &str, offset: (usize, usize)) -> Image {
        let mut v: Vec<ImageChar> = Vec::new();

        let mut rewarding_scheme: RewardingScheme = DEFAULT_REWARDING_SCHEME;
        for (y, line) in string
            // split in lines
            .lines()
            // interpret identifier line
            .filter_map(|l| {
                if l.starts_with(CONF_LINE_IDENTIFIER__CONTROL) {
                    if l[1..].trim().contains(UNHIDE_WHEN_LOST_LIVE_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenLostLife;
                    }
                    if l[1..].trim().contains(UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenGuessedChar;
                    }
                    None
                } else {
                    Some(l)
                }
            })
            // consider only lines starting with '|'
            .filter(|&l| l.starts_with(CONF_LINE_IDENTIFIER__IMAGE))
            .enumerate()
        //.inspect(|&(n,l)| println!("line {:?}: {:?} ", n,l))
        {
            let mut ii: Vec<_> = line
                .char_indices()
                // skip first char '|'
                .skip(1)
                // consider only chars != ' '
                .filter(|&(_, c)| c != ' ')
                // save in ImageChar object
                .map(|(x, c)| ImageChar {
                    point: (x as u8, y as u8),
                    char_: c,
                })
                .collect();
            v.append(&mut ii);
        }

        // find dimensions
        let mut x_max = 0;
        let mut y_max = 0;
        for i in v.iter() {
            let &ImageChar {
                point: (x, y),
                ..
            } = i;
            if x > x_max {
                x_max = x
            };
            if y > y_max {
                y_max = y
            };
        }

        // order points
        let v_len = v.len();
        if v_len <= BIG_IMAGE {
            v.sort(); // Sort algorithm, see "impl Ord for ImageChar"
        } else {
            rand::thread_rng().shuffle(&mut v); // points appear randomly.
        }

        if v.len() == 0 {
            Image::new(rand::thread_rng().choose(&DEFAULT_IMAGES).unwrap(), offset)
        } else {
            Image {
                ichars: v,
                offset: offset,
                dimension: (x_max,y_max),
                visible_points: v_len,
                rewarding_scheme: rewarding_scheme,
            }
        }
    }

    pub fn disclose(&mut self, lives_frac: (usize, usize), guessed_chars_frac: (usize, usize)) {
        let l = self.ichars.len();

        let as_points = |(n, d)| (3 * l * (d - n) as usize / d as usize + 1 * l) / 4;

        self.visible_points = match self.rewarding_scheme {
            RewardingScheme::UnhideWhenGuessedChar => as_points(guessed_chars_frac),
            RewardingScheme::UnhideWhenLostLife => as_points(lives_frac),
        };
    }
}
<|endoftext|>


<commit_before>//! Basic http responses
#![allow(non_upper_case_globals)]
use http::StatusCode;

use crate::response::{Response, ResponseBuilder};

macro_rules! STATIC_RESP {
    ($name:ident, $status:expr) => {
        #[allow(non_snake_case, missing_docs)]
        pub fn $name() -> ResponseBuilder {
            ResponseBuilder::new($status)
        }
    };
}

impl Response {
    STATIC_RESP!(Ok, StatusCode::OK);
    STATIC_RESP!(Created, StatusCode::CREATED);
    STATIC_RESP!(Accepted, StatusCode::ACCEPTED);
    STATIC_RESP!(
        NonAuthoritativeInformation,
        StatusCode::NON_AUTHORITATIVE_INFORMATION
    );

    STATIC_RESP!(NoContent, StatusCode::NO_CONTENT);
    STATIC_RESP!(ResetContent, StatusCode::RESET_CONTENT);
    STATIC_RESP!(PartialContent, StatusCode::PARTIAL_CONTENT);
    STATIC_RESP!(MultiStatus, StatusCode::MULTI_STATUS);
    STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED);

    STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES);
    STATIC_RESP!(MovedPermanenty, StatusCode::MOVED_PERMANENTLY);
    STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY);
    STATIC_RESP!(Found, StatusCode::FOUND);
    STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER);
    STATIC_RESP!(NotModified, StatusCode::NOT_MODIFIED);
    STATIC_RESP!(UseProxy, StatusCode::USE_PROXY);
    STATIC_RESP!(TemporaryRedirect, StatusCode::TEMPORARY_REDIRECT);
    STATIC_RESP!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT);

    STATIC_RESP!(BadRequest, StatusCode::BAD_REQUEST);
    STATIC_RESP!(NotFound, StatusCode::NOT_FOUND);
    STATIC_RESP!(Unauthorized, StatusCode::UNAUTHORIZED);
    STATIC_RESP!(PaymentRequired, StatusCode::PAYMENT_REQUIRED);
    STATIC_RESP!(Forbidden, StatusCode::FORBIDDEN);
    STATIC_RESP!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED);
    STATIC_RESP!(NotAcceptable, StatusCode::NOT_ACCEPTABLE);
    STATIC_RESP!(
        ProxyAuthenticationRequired,
        StatusCode::PROXY_AUTHENTICATION_REQUIRED
    );
    STATIC_RESP!(RequestTimeout, StatusCode::REQUEST_TIMEOUT);
    STATIC_RESP!(Conflict, StatusCode::CONFLICT);
    STATIC_RESP!(Gone, StatusCode::GONE);
    STATIC_RESP!(LengthRequired, StatusCode::LENGTH_REQUIRED);
    STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED);
    STATIC_RESP!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED);
    STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE);
    STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG);
    STATIC_RESP!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE);
    STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);
    STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);
    STATIC_RESP!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY);

    STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR);
    STATIC_RESP!(NotImplemented, StatusCode::NOT_IMPLEMENTED);
    STATIC_RESP!(BadGateway, StatusCode::BAD_GATEWAY);
    STATIC_RESP!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE);
    STATIC_RESP!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT);
    STATIC_RESP!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED);
    STATIC_RESP!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES);
    STATIC_RESP!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE);
    STATIC_RESP!(LoopDetected, StatusCode::LOOP_DETECTED);
}

#[cfg(test)]
mod tests {
    use crate::body::Body;
    use crate::response::Response;
    use http::StatusCode;

    #[test]
    fn test_build() {
        let resp = Response::Ok().body(Body::Empty);
        assert_eq!(resp.status(), StatusCode::OK);
    }
}
<commit_msg>Add builder function for HTTP 429 Too Many Requests status (#931)<commit_after>//! Basic http responses
#![allow(non_upper_case_globals)]
use http::StatusCode;

use crate::response::{Response, ResponseBuilder};

macro_rules! STATIC_RESP {
    ($name:ident, $status:expr) => {
        #[allow(non_snake_case, missing_docs)]
        pub fn $name() -> ResponseBuilder {
            ResponseBuilder::new($status)
        }
    };
}

impl Response {
    STATIC_RESP!(Ok, StatusCode::OK);
    STATIC_RESP!(Created, StatusCode::CREATED);
    STATIC_RESP!(Accepted, StatusCode::ACCEPTED);
    STATIC_RESP!(
        NonAuthoritativeInformation,
        StatusCode::NON_AUTHORITATIVE_INFORMATION
    );

    STATIC_RESP!(NoContent, StatusCode::NO_CONTENT);
    STATIC_RESP!(ResetContent, StatusCode::RESET_CONTENT);
    STATIC_RESP!(PartialContent, StatusCode::PARTIAL_CONTENT);
    STATIC_RESP!(MultiStatus, StatusCode::MULTI_STATUS);
    STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED);

    STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES);
    STATIC_RESP!(MovedPermanenty, StatusCode::MOVED_PERMANENTLY);
    STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY);
    STATIC_RESP!(Found, StatusCode::FOUND);
    STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER);
    STATIC_RESP!(NotModified, StatusCode::NOT_MODIFIED);
    STATIC_RESP!(UseProxy, StatusCode::USE_PROXY);
    STATIC_RESP!(TemporaryRedirect, StatusCode::TEMPORARY_REDIRECT);
    STATIC_RESP!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT);

    STATIC_RESP!(BadRequest, StatusCode::BAD_REQUEST);
    STATIC_RESP!(NotFound, StatusCode::NOT_FOUND);
    STATIC_RESP!(Unauthorized, StatusCode::UNAUTHORIZED);
    STATIC_RESP!(PaymentRequired, StatusCode::PAYMENT_REQUIRED);
    STATIC_RESP!(Forbidden, StatusCode::FORBIDDEN);
    STATIC_RESP!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED);
    STATIC_RESP!(NotAcceptable, StatusCode::NOT_ACCEPTABLE);
    STATIC_RESP!(
        ProxyAuthenticationRequired,
        StatusCode::PROXY_AUTHENTICATION_REQUIRED
    );
    STATIC_RESP!(RequestTimeout, StatusCode::REQUEST_TIMEOUT);
    STATIC_RESP!(Conflict, StatusCode::CONFLICT);
    STATIC_RESP!(Gone, StatusCode::GONE);
    STATIC_RESP!(LengthRequired, StatusCode::LENGTH_REQUIRED);
    STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED);
    STATIC_RESP!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED);
    STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE);
    STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG);
    STATIC_RESP!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE);
    STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);
    STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);
    STATIC_RESP!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY);
    STATIC_RESP!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS);

    STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR);
    STATIC_RESP!(NotImplemented, StatusCode::NOT_IMPLEMENTED);
    STATIC_RESP!(BadGateway, StatusCode::BAD_GATEWAY);
    STATIC_RESP!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE);
    STATIC_RESP!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT);
    STATIC_RESP!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED);
    STATIC_RESP!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES);
    STATIC_RESP!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE);
    STATIC_RESP!(LoopDetected, StatusCode::LOOP_DETECTED);
}

#[cfg(test)]
mod tests {
    use crate::body::Body;
    use crate::response::Response;
    use http::StatusCode;

    #[test]
    fn test_build() {
        let resp = Response::Ok().body(Body::Empty);
        assert_eq!(resp.status(), StatusCode::OK);
    }
}
<|endoftext|>