<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, PackageTarget, 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.insert(MetaFile::Target, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Target)).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),
        }
    }

    pub fn target(&mut self) -> Result<PackageTarget> {
        match self.read_metadata(MetaFile::Target) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Target)),
            Ok(Some(data)) => PackageTarget::from_str(&data),
            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 os::system::{Architecture, Platform};
    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());
    }

    #[test]
    fn reading_artifact_target() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let target = hart.target().unwrap();
        assert_eq!(target.platform, Platform::Linux);
        assert_eq!(target.architecture, Architecture::X86_64);
    }
}
<commit_msg>[core] Support leading `/` in artifact metadata lookup regexes.<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, PackageTarget, 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.insert(MetaFile::Target, Regex::new(&format!(r"^/?hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Target)).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),
        }
    }

    pub fn target(&mut self) -> Result<PackageTarget> {
        match self.read_metadata(MetaFile::Target) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Target)),
            Ok(Some(data)) => PackageTarget::from_str(&data),
            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 os::system::{Architecture, Platform};
    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());
    }

    #[test]
    fn reading_artifact_target() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let target = hart.target().unwrap();
        assert_eq!(target.platform, Platform::Linux);
        assert_eq!(target.architecture, Architecture::X86_64);
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use extra::json;
use std::hashmap::HashMap;
use serialize::Decodable;
use glfw;
use gl;
use cgmath::vector::{
    Vec3,
    Vec2,
};
use glh = gl_helpers;
use camera::Camera;
use map::MapPosIter;
use geom::Geom;
use tile_picker::TilePicker;
use obj;
use mesh::Mesh;
use misc::read_file;
use core::{
    Core,
    Unit,
    CommandEndTurn,
    CommandMove,
    CommandCreateUnit,
    Event,
    EventMove,
    EventEndTurn,
    EventCreateUnit,
};
use core_types::{
    Size2,
    Int,
    Bool,
    UnitId,
    PlayerId,
    MapPos,
};
use gl_types::{
    Scene,
    VertexCoord,
    TextureCoord,
    Float,
    Point2,
    MatId,
    ShaderId,
};
use event_visualizer::{
    EventVisualizer,
    EventMoveVisualizer,
    EventEndTurnVisualizer,
    EventCreateUnitVisualizer,
};

fn build_hex_mesh(&geom: &Geom, map_size: Size2<Int>) -> ~[VertexCoord] {
    let mut vertex_data = ~[];
    for tile_pos in MapPosIter::new(map_size) {
        let pos = geom.map_pos_to_world_pos(tile_pos);
        for num in range(0 as Int, 6) {
            let vertex = geom.index_to_hex_vertex(num);
            let next_vertex = geom.index_to_hex_vertex(num + 1);
            vertex_data.push(pos + vertex);
            vertex_data.push(pos + next_vertex);
            vertex_data.push(pos + Vec3::zero());
        }
    }
    vertex_data
}

fn build_hex_tex_coord(map_size: Size2<Int>) -> ~[TextureCoord] {
    let mut vertex_data = ~[];
    for _ in MapPosIter::new(map_size) {
        for _ in range(0, 6) {
            vertex_data.push(Vec2{x: 0.0, y: 0.0});
            vertex_data.push(Vec2{x: 1.0, y: 0.0});
            vertex_data.push(Vec2{x: 0.5, y: 0.5});
        }
    }
    vertex_data
}

fn read_win_size(config_path: &str) -> Size2<Int> {
    let path = Path::new(config_path);
    let json = json::from_str(read_file(&path)).unwrap();
    let mut decoder = json::Decoder::new(json);
    let size: Size2<Int> = Decodable::decode(&mut decoder);
    size
}

fn init_win(win_size: Size2<Int>) -> glfw::Window {
    glfw::set_error_callback(~glfw::LogErrorHandler);
    let init_status = glfw::init();
    if !init_status.is_ok() {
        fail!("Failed to initialize GLFW");
    }
    let win = glfw::Window::create(
        win_size.x as u32,
        win_size.y as u32,
        "Marauder",
        glfw::Windowed,
    ).unwrap();
    win.make_context_current();
    win.set_all_polling(true);
    win
}

pub struct Visualizer<'a> {
    program: ShaderId,
    map_mesh: Mesh,
    unit_mesh: Mesh,
    mat_id: MatId,
    win: glfw::Window,
    mouse_pos: Point2<Float>,
    camera: Camera,
    picker: TilePicker,
    selected_tile_pos: Option<MapPos>,
    selected_unit_id: Option<UnitId>,
    geom: Geom,
    scenes: HashMap<PlayerId, Scene>,
    core: Core<'a>,
    event_visualizer: Option<~EventVisualizer>,
    units: HashMap<PlayerId, ~[Unit]>,
}

impl<'a> Visualizer<'a> {
    pub fn new() -> ~Visualizer {
        let win_size = read_win_size("config.json");
        let win = init_win(win_size);
        let geom = Geom::new();
        let mut vis = ~Visualizer {
            program: 0,
            map_mesh: Mesh::new(),
            unit_mesh: Mesh::new(),
            mat_id: 0,
            win: win,
            mouse_pos: Vec2::zero(),
            camera: Camera::new(),
            picker: TilePicker::new(win_size),
            selected_tile_pos: None,
            selected_unit_id: None,
            geom: geom,
            // TODO: use for cycle
            scenes: {
                let mut m = HashMap::new();
                m.insert(0 as PlayerId, HashMap::new());
                m.insert(1 as PlayerId, HashMap::new());
                m
            },
            units: {
                let mut m = HashMap::new();
                m.insert(0 as PlayerId, ~[]);
                m.insert(1 as PlayerId, ~[]);
                m
            },
            core: Core::new(),
            event_visualizer: None,
        };
        vis.init_opengl();
        vis.picker.init(&geom, vis.core.map_size);
        vis.init_models();
        vis
    }

    fn win<'a>(&'a self) -> &'a glfw::Window {
        &self.win
    }

    fn init_models(&mut self) {
        self.program = glh::compile_program(
            read_file(&Path::new("normal.vs.glsl")),
            read_file(&Path::new("normal.fs.glsl")),
        );
        gl::UseProgram(self.program);
        self.mat_id = glh::get_uniform(self.program, "mvp_mat");
        let vertex_coordinates_attr = glh::get_attr(
            self.program, "in_vertex_coordinates");
        gl::EnableVertexAttribArray(vertex_coordinates_attr);
        glh::vertex_attrib_pointer(vertex_coordinates_attr, 3);
        let texture_coords_attr = glh::get_attr(
            self.program, "in_texture_coordinates");
        gl::EnableVertexAttribArray(texture_coords_attr);
        glh::vertex_attrib_pointer(texture_coords_attr, 3);
        let map_vertex_data = build_hex_mesh(&self.geom, self.core.map_size);
        self.map_mesh.set_vertex_coords(map_vertex_data);
        self.map_mesh.set_texture_coords(
            build_hex_tex_coord(self.core.map_size));
        self.map_mesh.set_texture(glh::load_texture(~"data/floor.png"));
        let unit_obj = obj::Model::new("data/tank.obj");
        self.unit_mesh.set_vertex_coords(unit_obj.build());
        self.unit_mesh.set_texture_coords(unit_obj.build_tex_coord());
        self.unit_mesh.set_texture(glh::load_texture(~"data/tank.png"));
    }

    fn scene<'a>(&'a self) -> &'a Scene {
        self.scenes.get(&self.core.current_player_id)
    }

    fn unit_at_opt(&'a self, pos: MapPos) -> Option<&'a Unit> {
        let id = self.core.current_player_id;
        self.units.get(&id).iter().find(|u| u.pos == pos)
    }

    fn init_opengl(&mut self) {
        gl::load_with(glfw::get_proc_address);
        gl::Enable(gl::DEPTH_TEST);
    }

    fn cleanup_opengl(&self) {
        gl::DeleteProgram(self.program);
    }

    fn draw_units(&self) {
        gl::UseProgram(self.program);
        for (_, unit) in self.scene().iter() {
            let m = glh::tr(self.camera.mat(), unit.pos);
            glh::uniform_mat4f(self.mat_id, &m);
            self.unit_mesh.draw(self.program);
        }
    }

    fn draw_map(&self) {
        glh::uniform_mat4f(self.mat_id, &self.camera.mat());
        self.map_mesh.draw(self.program);
    }

    fn draw(&mut self) {
        gl::ClearColor(0.3, 0.3, 0.3, 1.0);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        gl::UseProgram(self.program);
        self.draw_units();
        self.draw_map();
        if !self.event_visualizer.is_none() {
            let scene = self.scenes.get_mut(&self.core.current_player_id);
            self.event_visualizer.get_mut_ref().draw(&self.geom, scene);
        }
        self.win().swap_buffers();
    }

    pub fn is_running(&self) -> Bool {
        return !self.win().should_close()
    }

    fn handle_key_event(&mut self, key: glfw::Key) {
        match key {
            glfw::KeyEscape | glfw::KeyQ => {
                self.win().set_should_close(true);
            },
            glfw::KeyT => {
                self.core.do_command(CommandEndTurn);
            },
            glfw::KeyU => {
                let pos_opt = self.selected_tile_pos;
                if pos_opt.is_some() {
                    let pos = pos_opt.unwrap();
                    if self.unit_at_opt(pos).is_none() {
                        let cmd = CommandCreateUnit(pos);
                        self.core.do_command(cmd);
                    }
                }
            },
            glfw::KeySpace => println!("space"),
            glfw::KeyUp => self.camera.move(270.0),
            glfw::KeyDown => self.camera.move(90.0),
            glfw::KeyRight => self.camera.move(0.0),
            glfw::KeyLeft => self.camera.move(180.0),
            glfw::KeyMinus => self.camera.zoom += 1.0,
            glfw::KeyEqual => self.camera.zoom -= 1.0,
            _ => {},
        }
    }

    fn handle_cursor_pos_event(&mut self, pos: Point2<Float>) {
        let button = self.win().get_mouse_button(glfw::MouseButtonRight);
        if button == glfw::Press {
            self.camera.z_angle += (self.mouse_pos.x - pos.x) / 2.0;
            self.camera.x_angle += (self.mouse_pos.y - pos.y) / 2.0;
        }
        self.mouse_pos = pos;
    }

    fn handle_mouse_button_event(&mut self) {
        if self.selected_tile_pos.is_some() {
            let pos = self.selected_tile_pos.unwrap();
            if self.unit_at_opt(pos).is_some() {
                let unit_id = self.unit_at_opt(pos).unwrap().id;
                self.selected_unit_id = Some(unit_id);
            } else if self.selected_unit_id.is_some() {
                let unit_id = self.selected_unit_id.unwrap();
                self.core.do_command(CommandMove(unit_id, pos));
            }
        }
    }

    fn get_events(&mut self) -> ~[glfw::WindowEvent] {
        glfw::poll_events();
        let mut events = ~[];
        for (_, event) in self.win().flush_events() {
            events.push(event);
        }
        events
    }

    fn handle_event(&mut self, event: glfw::WindowEvent) {
        match event {
            glfw::KeyEvent(key, _, glfw::Press, _) => {
                self.handle_key_event(key);
            },
            glfw::CursorPosEvent(x, y) => {
                let p = Vec2{x: x as Float, y: y as Float};
                self.handle_cursor_pos_event(p);
            },
            glfw::MouseButtonEvent(glfw::MouseButtonLeft, glfw::Press, _) => {
                self.handle_mouse_button_event();
            },
            glfw::SizeEvent(w, h) => {
                gl::Viewport(0, 0, w, h);
                self.picker.set_win_size(Size2{x: w, y: h});
            },
            _ => {},
        }
    }

    fn handle_events(&mut self) {
        for event in self.get_events().iter() {
            self.handle_event(*event);
        }
    }

    fn pick_tile(&mut self) {
        let mouse_pos = Vec2 {
            x: self.mouse_pos.x as Int,
            y: self.mouse_pos.y as Int,
        };
        self.selected_tile_pos = self.picker.pick_tile(&self.camera, mouse_pos);
    }

    pub fn make_event_visualizer(&mut self, event: Event) -> ~EventVisualizer {
        match event {
            EventMove(unit_id, path) => {
                EventMoveVisualizer::new(unit_id, path)
            },
            EventEndTurn(_, _) => {
                EventEndTurnVisualizer::new()
            },
            EventCreateUnit(id, pos) => {
                EventCreateUnitVisualizer::new(id, pos)
            },
        }
    }

    pub fn logic(&mut self) {
        if self.event_visualizer.is_none() {
            let event_opt = self.core.get_event();
            if event_opt.is_some() {
                let event = event_opt.unwrap();
                let vis = self.make_event_visualizer(event);
                self.event_visualizer = Some(vis);
            }
        } else if self.event_visualizer.get_ref().is_finished() {
            let scene = self.scenes.get_mut(&self.core.current_player_id);
            self.event_visualizer.get_mut_ref().end(
                &self.geom,
                scene,
                self.units.get_mut(&self.core.current_player_id)
            );
            self.event_visualizer = None;
        }
    }

    pub fn tick(&mut self) {
        self.handle_events();
        self.logic();
        self.pick_tile();
        self.draw();
    }
}

impl<'a> Drop for Visualizer<'a> {
    fn drop(&mut self) {
        self.cleanup_opengl();
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>visualizer.rs: Style fix<commit_after>// See LICENSE file for copyright and license details.

use extra::json;
use std::hashmap::HashMap;
use serialize::Decodable;
use glfw;
use gl;
use cgmath::vector::{
    Vec3,
    Vec2,
};
use glh = gl_helpers;
use camera::Camera;
use map::MapPosIter;
use geom::Geom;
use tile_picker::TilePicker;
use obj;
use mesh::Mesh;
use misc::read_file;
use core::{
    Core,
    Unit,
    CommandEndTurn,
    CommandMove,
    CommandCreateUnit,
    Event,
    EventMove,
    EventEndTurn,
    EventCreateUnit,
};
use core_types::{
    Size2,
    Int,
    Bool,
    UnitId,
    PlayerId,
    MapPos,
};
use gl_types::{
    Scene,
    VertexCoord,
    TextureCoord,
    Float,
    Point2,
    MatId,
    ShaderId,
};
use event_visualizer::{
    EventVisualizer,
    EventMoveVisualizer,
    EventEndTurnVisualizer,
    EventCreateUnitVisualizer,
};

fn build_hex_mesh(&geom: &Geom, map_size: Size2<Int>) -> ~[VertexCoord] {
    let mut vertex_data = ~[];
    for tile_pos in MapPosIter::new(map_size) {
        let pos = geom.map_pos_to_world_pos(tile_pos);
        for num in range(0 as Int, 6) {
            let vertex = geom.index_to_hex_vertex(num);
            let next_vertex = geom.index_to_hex_vertex(num + 1);
            vertex_data.push(pos + vertex);
            vertex_data.push(pos + next_vertex);
            vertex_data.push(pos + Vec3::zero());
        }
    }
    vertex_data
}

fn build_hex_tex_coord(map_size: Size2<Int>) -> ~[TextureCoord] {
    let mut vertex_data = ~[];
    for _ in MapPosIter::new(map_size) {
        for _ in range(0, 6) {
            vertex_data.push(Vec2{x: 0.0, y: 0.0});
            vertex_data.push(Vec2{x: 1.0, y: 0.0});
            vertex_data.push(Vec2{x: 0.5, y: 0.5});
        }
    }
    vertex_data
}

fn read_win_size(config_path: &str) -> Size2<Int> {
    let path = Path::new(config_path);
    let json = json::from_str(read_file(&path)).unwrap();
    let mut decoder = json::Decoder::new(json);
    let size: Size2<Int> = Decodable::decode(&mut decoder);
    size
}

fn init_win(win_size: Size2<Int>) -> glfw::Window {
    glfw::set_error_callback(~glfw::LogErrorHandler);
    let init_status = glfw::init();
    if !init_status.is_ok() {
        fail!("Failed to initialize GLFW");
    }
    let win = glfw::Window::create(
        win_size.x as u32,
        win_size.y as u32,
        "Marauder",
        glfw::Windowed,
    ).unwrap();
    win.make_context_current();
    win.set_all_polling(true);
    win
}

pub struct Visualizer<'a> {
    program: ShaderId,
    map_mesh: Mesh,
    unit_mesh: Mesh,
    mat_id: MatId,
    win: glfw::Window,
    mouse_pos: Point2<Float>,
    camera: Camera,
    picker: TilePicker,
    selected_tile_pos: Option<MapPos>,
    selected_unit_id: Option<UnitId>,
    geom: Geom,
    scenes: HashMap<PlayerId, Scene>,
    core: Core<'a>,
    event_visualizer: Option<~EventVisualizer>,
    units: HashMap<PlayerId, ~[Unit]>,
}

impl<'a> Visualizer<'a> {
    pub fn new() -> ~Visualizer {
        let win_size = read_win_size("config.json");
        let win = init_win(win_size);
        let geom = Geom::new();
        let mut vis = ~Visualizer {
            program: 0,
            map_mesh: Mesh::new(),
            unit_mesh: Mesh::new(),
            mat_id: 0,
            win: win,
            mouse_pos: Vec2::zero(),
            camera: Camera::new(),
            picker: TilePicker::new(win_size),
            selected_tile_pos: None,
            selected_unit_id: None,
            geom: geom,
            // TODO: use for cycle
            scenes: {
                let mut m = HashMap::new();
                m.insert(0 as PlayerId, HashMap::new());
                m.insert(1 as PlayerId, HashMap::new());
                m
            },
            units: {
                let mut m = HashMap::new();
                m.insert(0 as PlayerId, ~[]);
                m.insert(1 as PlayerId, ~[]);
                m
            },
            core: Core::new(),
            event_visualizer: None,
        };
        vis.init_opengl();
        vis.picker.init(&geom, vis.core.map_size);
        vis.init_models();
        vis
    }

    fn win<'a>(&'a self) -> &'a glfw::Window {
        &self.win
    }

    fn init_models(&mut self) {
        self.program = glh::compile_program(
            read_file(&Path::new("normal.vs.glsl")),
            read_file(&Path::new("normal.fs.glsl")),
        );
        gl::UseProgram(self.program);
        self.mat_id = glh::get_uniform(self.program, "mvp_mat");
        let vertex_coordinates_attr = glh::get_attr(
            self.program, "in_vertex_coordinates");
        gl::EnableVertexAttribArray(vertex_coordinates_attr);
        glh::vertex_attrib_pointer(vertex_coordinates_attr, 3);
        let texture_coords_attr = glh::get_attr(
            self.program, "in_texture_coordinates");
        gl::EnableVertexAttribArray(texture_coords_attr);
        glh::vertex_attrib_pointer(texture_coords_attr, 3);
        let map_vertex_data = build_hex_mesh(&self.geom, self.core.map_size);
        self.map_mesh.set_vertex_coords(map_vertex_data);
        self.map_mesh.set_texture_coords(
            build_hex_tex_coord(self.core.map_size));
        self.map_mesh.set_texture(glh::load_texture(~"data/floor.png"));
        let unit_obj = obj::Model::new("data/tank.obj");
        self.unit_mesh.set_vertex_coords(unit_obj.build());
        self.unit_mesh.set_texture_coords(unit_obj.build_tex_coord());
        self.unit_mesh.set_texture(glh::load_texture(~"data/tank.png"));
    }

    fn scene<'a>(&'a self) -> &'a Scene {
        self.scenes.get(&self.core.current_player_id)
    }

    fn unit_at_opt(&'a self, pos: MapPos) -> Option<&'a Unit> {
        let id = self.core.current_player_id;
        self.units.get(&id).iter().find(|u| u.pos == pos)
    }

    fn init_opengl(&mut self) {
        gl::load_with(glfw::get_proc_address);
        gl::Enable(gl::DEPTH_TEST);
    }

    fn cleanup_opengl(&self) {
        gl::DeleteProgram(self.program);
    }

    fn draw_units(&self) {
        gl::UseProgram(self.program);
        for (_, unit) in self.scene().iter() {
            let m = glh::tr(self.camera.mat(), unit.pos);
            glh::uniform_mat4f(self.mat_id, &m);
            self.unit_mesh.draw(self.program);
        }
    }

    fn draw_map(&self) {
        glh::uniform_mat4f(self.mat_id, &self.camera.mat());
        self.map_mesh.draw(self.program);
    }

    fn draw(&mut self) {
        gl::ClearColor(0.3, 0.3, 0.3, 1.0);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        gl::UseProgram(self.program);
        self.draw_units();
        self.draw_map();
        if !self.event_visualizer.is_none() {
            let scene = self.scenes.get_mut(&self.core.current_player_id);
            self.event_visualizer.get_mut_ref().draw(&self.geom, scene);
        }
        self.win().swap_buffers();
    }

    pub fn is_running(&self) -> Bool {
        return !self.win().should_close()
    }

    fn handle_key_event(&mut self, key: glfw::Key) {
        match key {
            glfw::KeyEscape | glfw::KeyQ => {
                self.win().set_should_close(true);
            },
            glfw::KeyT => {
                self.core.do_command(CommandEndTurn);
            },
            glfw::KeyU => {
                let pos_opt = self.selected_tile_pos;
                if pos_opt.is_some() {
                    let pos = pos_opt.unwrap();
                    if self.unit_at_opt(pos).is_none() {
                        let cmd = CommandCreateUnit(pos);
                        self.core.do_command(cmd);
                    }
                }
            },
            glfw::KeySpace => println!("space"),
            glfw::KeyUp => self.camera.move(270.0),
            glfw::KeyDown => self.camera.move(90.0),
            glfw::KeyRight => self.camera.move(0.0),
            glfw::KeyLeft => self.camera.move(180.0),
            glfw::KeyMinus => self.camera.zoom += 1.0,
            glfw::KeyEqual => self.camera.zoom -= 1.0,
            _ => {},
        }
    }

    fn handle_cursor_pos_event(&mut self, pos: Point2<Float>) {
        let button = self.win().get_mouse_button(glfw::MouseButtonRight);
        if button == glfw::Press {
            self.camera.z_angle += (self.mouse_pos.x - pos.x) / 2.0;
            self.camera.x_angle += (self.mouse_pos.y - pos.y) / 2.0;
        }
        self.mouse_pos = pos;
    }

    fn handle_mouse_button_event(&mut self) {
        if self.selected_tile_pos.is_some() {
            let pos = self.selected_tile_pos.unwrap();
            if self.unit_at_opt(pos).is_some() {
                let unit_id = self.unit_at_opt(pos).unwrap().id;
                self.selected_unit_id = Some(unit_id);
            } else if self.selected_unit_id.is_some() {
                let unit_id = self.selected_unit_id.unwrap();
                self.core.do_command(CommandMove(unit_id, pos));
            }
        }
    }

    fn get_events(&mut self) -> ~[glfw::WindowEvent] {
        glfw::poll_events();
        let mut events = ~[];
        for (_, event) in self.win().flush_events() {
            events.push(event);
        }
        events
    }

    fn handle_event(&mut self, event: glfw::WindowEvent) {
        match event {
            glfw::KeyEvent(key, _, glfw::Press, _) => {
                self.handle_key_event(key);
            },
            glfw::CursorPosEvent(x, y) => {
                let p = Vec2{x: x as Float, y: y as Float};
                self.handle_cursor_pos_event(p);
            },
            glfw::MouseButtonEvent(glfw::MouseButtonLeft, glfw::Press, _) => {
                self.handle_mouse_button_event();
            },
            glfw::SizeEvent(w, h) => {
                gl::Viewport(0, 0, w, h);
                self.picker.set_win_size(Size2{x: w, y: h});
            },
            _ => {},
        }
    }

    fn handle_events(&mut self) {
        for event in self.get_events().iter() {
            self.handle_event(*event);
        }
    }

    fn pick_tile(&mut self) {
        let mouse_pos = Vec2 {
            x: self.mouse_pos.x as Int,
            y: self.mouse_pos.y as Int,
        };
        self.selected_tile_pos = self.picker.pick_tile(&self.camera, mouse_pos);
    }

    pub fn make_event_visualizer(&mut self, event: Event) -> ~EventVisualizer {
        match event {
            EventMove(unit_id, path) => {
                EventMoveVisualizer::new(unit_id, path)
            },
            EventEndTurn(_, _) => {
                EventEndTurnVisualizer::new()
            },
            EventCreateUnit(id, pos) => {
                EventCreateUnitVisualizer::new(id, pos)
            },
        }
    }

    pub fn logic(&mut self) {
        if self.event_visualizer.is_none() {
            let event_opt = self.core.get_event();
            if event_opt.is_some() {
                let event = event_opt.unwrap();
                let vis = self.make_event_visualizer(event);
                self.event_visualizer = Some(vis);
            }
        } else if self.event_visualizer.get_ref().is_finished() {
            let scene = self.scenes.get_mut(&self.core.current_player_id);
            let units = self.units.get_mut(&self.core.current_player_id);
            self.event_visualizer.get_mut_ref().end(
                &self.geom, scene, units);
            self.event_visualizer = None;
        }
    }

    pub fn tick(&mut self) {
        self.handle_events();
        self.logic();
        self.pick_tile();
        self.draw();
    }
}

impl<'a> Drop for Visualizer<'a> {
    fn drop(&mut self) {
        self.cleanup_opengl();
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

use libc::*;

pub type xmpp_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_conn_handler = Option<extern "C" fn(*const xmpp_conn_t, c_int, c_int, *const xmpp_stream_error_t, *const c_void)>;
pub type xmpp_timed_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_log_handler = Option<extern "C" fn(*const c_void, c_int, *const c_char, *const c_char, *const c_char)>;
pub type xmpp_open_handler = Option<extern "C" fn(*const xmpp_conn_t)>;

pub type hash_free_func = Option<extern "C" fn(*const xmpp_ctx_t, *mut c_void)>;

#[repr(C)]
pub struct xmpp_conn_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,

  state: c_int,
  timeout_stamp: uint64_t,
  error: c_int,
  stream_error: *mut xmpp_stream_error_t,
  sock: c_int,
  tls: *mut c_void,

  tls_support: c_int,
  tls_disabled: c_int,
  tls_failed: c_int,
  sasl_support: c_int,
  secured: c_int,

  bind_required: c_int,
  session_required: c_int,

  lang: *mut c_char,
  domain: *mut c_char,
  connectdomain: *mut c_char,
  connectport: *mut c_char,
  jid: *mut c_char,
  pass: *mut c_char,
  bound_jid: *mut c_char,
  stream_id: *mut c_char,

  blocking_seed: c_int,
  send_queue_max: c_int,
  send_queue_len: c_int,
  send_queue_head: *mut c_void,
  send_queue_tail: *mut c_void,

  reset_parser: c_int,
  parser: *mut c_void,
  connect_timeout: c_uint,
  open_handler: xmpp_open_handler,
  authenticated: c_int,

  conn_handler: xmpp_conn_handler,
  userdata: *mut c_void,

  timed_handlers: *mut c_void,
  id_handlers: *mut hash_t,
  handlers: *mut c_void
}

#[repr(C)]
pub struct xmpp_stanza_t {
  _ref: c_int,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,
  prev: *mut xmpp_stanza_t,
  next: *mut xmpp_stanza_t,
  children: *mut xmpp_stanza_t,
  parent: *mut xmpp_stanza_t,
  data: *mut c_char,
  attributes: *mut hash_t
}

#[repr(C)]
pub struct xmpp_ctx_t {
  mem: *const xmpp_mem_t,
  log: *const xmpp_log_t,
  loop_status: c_int,
  connlist: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_connlist_t {
  conn: *mut xmpp_conn_t,
  next: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_mem_t {
  alloc: Option<extern "C" fn(size_t, *mut u8) -> *mut u8>,
  free: Option<extern "C" fn(*mut u8, *const u8)>,
  realloc: Option<extern "C" fn(*mut u8, size_t, *const u8) -> *mut u8>,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_log_t {
  handler: xmpp_log_handler,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_stream_error_t {
  _type: c_int, //xmpp_error_type_t
  text: *mut c_char,
  stanza: *mut xmpp_stanza_t
}

#[repr(C)]
pub struct hashentry_t {
  next: *mut hashentry_t,
  key: *mut c_char,
  values: *mut c_void
}

#[repr(C)]
pub struct hash_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  free: hash_free_func,
  length: c_int,
  num_keys: c_int,
  entries: *mut *mut hashentry_t
}

#[link(name="strophe")]
extern {
  // connection management
  pub fn xmpp_conn_new(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_close(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_release(conn: *const xmpp_conn_t) -> c_int;
  pub fn xmpp_conn_get_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_get_bound_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_jid(conn: *const xmpp_conn_t, jid: *const c_char);
  pub fn xmpp_conn_get_pass(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_pass(conn: *const xmpp_conn_t, pass: *const c_char);
  pub fn xmpp_conn_get_context(conn: *const xmpp_conn_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_connect_client(conn: *const xmpp_conn_t, altdomain: *const c_char, altport: c_ushort, callback: xmpp_conn_handler, userdata: *const u8) -> c_int;
  pub fn xmpp_disconnect(conn: *const xmpp_conn_t);
  pub fn xmpp_send(conn: *const xmpp_conn_t, stanza: *const xmpp_stanza_t);

  // context objects
  pub fn xmpp_get_default_logger(level: c_int) -> *mut xmpp_log_t;
  pub fn xmpp_ctx_new(mem: *const xmpp_mem_t, log: *const xmpp_log_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_ctx_free(ctx: *const xmpp_ctx_t);

  // initialization, shutdown and versioning
  pub fn xmpp_initialize();
  pub fn xmpp_shutdown();
  pub fn xmpp_version_check(major: c_int, minor: c_int) -> c_int;

  // event loop
  pub fn xmpp_run_once(ctx: *mut xmpp_ctx_t, timeout: c_ulong);
  pub fn xmpp_run(ctx: *mut xmpp_ctx_t);
  pub fn xmpp_stop(ctx: *mut xmpp_ctx_t);

  // stanza and timed event handlers
  pub fn xmpp_timed_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_timed_handler);
  pub fn xmpp_id_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char);
  pub fn xmpp_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler);
  pub fn xmpp_timed_handler_add(conn: *const xmpp_conn_t, handler: xmpp_timed_handler, period: c_ulong, userdata: *const c_void);
  pub fn xmpp_id_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char, userdata: *const c_void);
  pub fn xmpp_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, ns: *const c_char, name: *const c_char, _type: *const c_char, userdata: *const c_void);

  // stanza creation and manipulation
  pub fn xmpp_stanza_new(ctx: *mut xmpp_ctx_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_clone(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_copy(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_release(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_text(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_tag(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_to_text(stanza: *mut xmpp_stanza_t, buf: *const *const c_char, sizelen: *const size_t) -> c_int;
  pub fn xmpp_stanza_set_name(stanza: *mut xmpp_stanza_t, name: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_name(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_attribute_count(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_get_attributes(stanza: *const xmpp_stanza_t, attr: *const *const c_char, attrlen: c_int) -> c_int;
  pub fn xmpp_stanza_set_attribute(stanza: *const xmpp_stanza_t, key: *const c_char, value: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> c_int;
  pub fn xmpp_stanza_add_child(stanza: *mut xmpp_stanza_t, child: *mut xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_set_text(stanza: *mut xmpp_stanza_t, text: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_text_with_size(stanza: *mut xmpp_stanza_t, text: *const c_char, size: size_t) -> c_int;
  pub fn xmpp_stanza_get_id(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_ns(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_type(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_child_by_name(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_child_by_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_children(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_next(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_text(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_text_ptr(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_set_id(stanza: *const xmpp_stanza_t, id: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_type(stanza: *const xmpp_stanza_t, _type: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_attribute(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut c_char;
}
<commit_msg>add missing enums<commit_after>#![allow(non_camel_case_types)]

use libc::*;

pub type xmpp_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_conn_handler = Option<extern "C" fn(*const xmpp_conn_t, c_int, c_int, *const xmpp_stream_error_t, *const c_void)>;
pub type xmpp_timed_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_log_handler = Option<extern "C" fn(*const c_void, c_int, *const c_char, *const c_char, *const c_char)>;
pub type xmpp_open_handler = Option<extern "C" fn(*const xmpp_conn_t)>;

pub type hash_free_func = Option<extern "C" fn(*const xmpp_ctx_t, *mut c_void)>;

#[repr(C)]
pub struct xmpp_conn_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,

  state: c_int,
  timeout_stamp: uint64_t,
  error: c_int,
  stream_error: *mut xmpp_stream_error_t,
  sock: c_int,
  tls: *mut c_void,

  tls_support: c_int,
  tls_disabled: c_int,
  tls_failed: c_int,
  sasl_support: c_int,
  secured: c_int,

  bind_required: c_int,
  session_required: c_int,

  lang: *mut c_char,
  domain: *mut c_char,
  connectdomain: *mut c_char,
  connectport: *mut c_char,
  jid: *mut c_char,
  pass: *mut c_char,
  bound_jid: *mut c_char,
  stream_id: *mut c_char,

  blocking_seed: c_int,
  send_queue_max: c_int,
  send_queue_len: c_int,
  send_queue_head: *mut c_void,
  send_queue_tail: *mut c_void,

  reset_parser: c_int,
  parser: *mut c_void,
  connect_timeout: c_uint,
  open_handler: xmpp_open_handler,
  authenticated: c_int,

  conn_handler: xmpp_conn_handler,
  userdata: *mut c_void,

  timed_handlers: *mut c_void,
  id_handlers: *mut hash_t,
  handlers: *mut c_void
}

#[repr(C)]
pub struct xmpp_stanza_t {
  _ref: c_int,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,
  prev: *mut xmpp_stanza_t,
  next: *mut xmpp_stanza_t,
  children: *mut xmpp_stanza_t,
  parent: *mut xmpp_stanza_t,
  data: *mut c_char,
  attributes: *mut hash_t
}

#[repr(C)]
pub struct xmpp_ctx_t {
  mem: *const xmpp_mem_t,
  log: *const xmpp_log_t,
  loop_status: c_int,
  connlist: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_connlist_t {
  conn: *mut xmpp_conn_t,
  next: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_mem_t {
  alloc: Option<extern "C" fn(size_t, *mut u8) -> *mut u8>,
  free: Option<extern "C" fn(*mut u8, *const u8)>,
  realloc: Option<extern "C" fn(*mut u8, size_t, *const u8) -> *mut u8>,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_log_t {
  handler: xmpp_log_handler,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_stream_error_t {
  _type: c_int, //xmpp_error_type_t
  text: *mut c_char,
  stanza: *mut xmpp_stanza_t
}

#[repr(C)]
pub struct hashentry_t {
  next: *mut hashentry_t,
  key: *mut c_char,
  values: *mut c_void
}

#[repr(C)]
pub struct hash_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  free: hash_free_func,
  length: c_int,
  num_keys: c_int,
  entries: *mut *mut hashentry_t
}

#[repr(C)]
pub enum xmpp_log_level_t {
  XMPP_LEVEL_DEBUG,
  XMPP_LEVEL_INFO,
  XMPP_LEVEL_WARN,
  XMPP_LEVEL_ERROR
}

#[repr(C)]
pub enum xmpp_conn_type_t {
  XMPP_UNKNOWN,
  XMPP_CLIENT,
  XMPP_COMPONENT
}

#[repr(C)]
pub enum xmpp_conn_event_t {
  XMPP_CONN_CONNECT,
  XMPP_CONN_DISCONNECT,
  XMPP_CONN_FAIL
}

#[repr(C)]
pub enum xmpp_error_type_t {
  XMPP_SE_BAD_FORMAT,
  XMPP_SE_BAD_NS_PREFIX,
  XMPP_SE_CONFLICT,
  XMPP_SE_CONN_TIMEOUT,
  XMPP_SE_HOST_GONE,
  XMPP_SE_HOST_UNKNOWN,
  XMPP_SE_IMPROPER_ADDR,
  XMPP_SE_INTERNAL_SERVER_ERROR,
  XMPP_SE_INVALID_FROM,
  XMPP_SE_INVALID_ID,
  XMPP_SE_INVALID_NS,
  XMPP_SE_INVALID_XML,
  XMPP_SE_NOT_AUTHORIZED,
  XMPP_SE_POLICY_VIOLATION,
  XMPP_SE_REMOTE_CONN_FAILED,
  XMPP_SE_RESOURCE_CONSTRAINT,
  XMPP_SE_RESTRICTED_XML,
  XMPP_SE_SEE_OTHER_HOST,
  XMPP_SE_SYSTEM_SHUTDOWN,
  XMPP_SE_UNDEFINED_CONDITION,
  XMPP_SE_UNSUPPORTED_ENCODING,
  XMPP_SE_UNSUPPORTED_STANZA_TYPE,
  XMPP_SE_UNSUPPORTED_VERSION,
  XMPP_SE_XML_NOT_WELL_FORMED
}

#[link(name="strophe")]
extern {
  // connection management
  pub fn xmpp_conn_new(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_close(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_release(conn: *const xmpp_conn_t) -> c_int;
  pub fn xmpp_conn_get_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_get_bound_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_jid(conn: *const xmpp_conn_t, jid: *const c_char);
  pub fn xmpp_conn_get_pass(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_pass(conn: *const xmpp_conn_t, pass: *const c_char);
  pub fn xmpp_conn_get_context(conn: *const xmpp_conn_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_connect_client(conn: *const xmpp_conn_t, altdomain: *const c_char, altport: c_ushort, callback: xmpp_conn_handler, userdata: *const u8) -> c_int;
  pub fn xmpp_disconnect(conn: *const xmpp_conn_t);
  pub fn xmpp_send(conn: *const xmpp_conn_t, stanza: *const xmpp_stanza_t);

  // context objects
  pub fn xmpp_get_default_logger(level: c_int) -> *mut xmpp_log_t;
  pub fn xmpp_ctx_new(mem: *const xmpp_mem_t, log: *const xmpp_log_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_ctx_free(ctx: *const xmpp_ctx_t);

  // initialization, shutdown and versioning
  pub fn xmpp_initialize();
  pub fn xmpp_shutdown();
  pub fn xmpp_version_check(major: c_int, minor: c_int) -> c_int;

  // event loop
  pub fn xmpp_run_once(ctx: *mut xmpp_ctx_t, timeout: c_ulong);
  pub fn xmpp_run(ctx: *mut xmpp_ctx_t);
  pub fn xmpp_stop(ctx: *mut xmpp_ctx_t);

  // stanza and timed event handlers
  pub fn xmpp_timed_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_timed_handler);
  pub fn xmpp_id_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char);
  pub fn xmpp_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler);
  pub fn xmpp_timed_handler_add(conn: *const xmpp_conn_t, handler: xmpp_timed_handler, period: c_ulong, userdata: *const c_void);
  pub fn xmpp_id_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char, userdata: *const c_void);
  pub fn xmpp_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, ns: *const c_char, name: *const c_char, _type: *const c_char, userdata: *const c_void);

  // stanza creation and manipulation
  pub fn xmpp_stanza_new(ctx: *mut xmpp_ctx_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_clone(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_copy(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_release(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_text(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_tag(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_to_text(stanza: *mut xmpp_stanza_t, buf: *const *const c_char, sizelen: *const size_t) -> c_int;
  pub fn xmpp_stanza_set_name(stanza: *mut xmpp_stanza_t, name: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_name(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_attribute_count(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_get_attributes(stanza: *const xmpp_stanza_t, attr: *const *const c_char, attrlen: c_int) -> c_int;
  pub fn xmpp_stanza_set_attribute(stanza: *const xmpp_stanza_t, key: *const c_char, value: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> c_int;
  pub fn xmpp_stanza_add_child(stanza: *mut xmpp_stanza_t, child: *mut xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_set_text(stanza: *mut xmpp_stanza_t, text: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_text_with_size(stanza: *mut xmpp_stanza_t, text: *const c_char, size: size_t) -> c_int;
  pub fn xmpp_stanza_get_id(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_ns(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_type(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_child_by_name(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_child_by_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_children(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_next(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_text(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_text_ptr(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_set_id(stanza: *const xmpp_stanza_t, id: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_type(stanza: *const xmpp_stanza_t, _type: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_attribute(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut c_char;
}

<|endoftext|>


<commit_before>use std::borrow::Cow;
use error::{
    Result as MyResult,
};
use error::Error::DriverError;
use error::DriverError::MixedParams;

enum ParserState {
    TopLevel,
    // (string_delimiter, last_char)
    InStringLiteral(char, char),
    MaybeInNamedParam,
    InNamedParam,
}

use self::ParserState::*;

pub fn parse_named_params<'a>(query: &'a str) -> MyResult<(Option<Vec<String>>, Cow<'a, str>)> {
    let mut state = TopLevel;
    let mut have_positional = false;
    let mut cur_param = 0;
    // Vec<(start_offset, end_offset, name)>
    let mut params = Vec::new();
    for (i, c) in query.chars().enumerate() {
        let mut rematch = false;
        match state {
            TopLevel => {
                match c {
                    ':' => state = MaybeInNamedParam,
                    '\'' => state = InStringLiteral('\'', '\''),
                    '"' => state = InStringLiteral('"', '"'),
                    '?' => have_positional = true,
                    _ => (),
                }
            },
            InStringLiteral(separator, prev_char) => {
                match c {
                    x if x == separator && prev_char != '\\' => state = TopLevel,
                    x => state = InStringLiteral(separator, x),
                }
            },
            MaybeInNamedParam => {
                match c {
                    'a' ... 'z' | '_' => {
                        params.push((i - 1, 0, String::with_capacity(16)));
                        params[cur_param].2.push(c);
                        state = InNamedParam;
                    },
                    _ => rematch = true,
                }
            },
            InNamedParam => {
                match c {
                    'a' ... 'z' | '_' => params[cur_param].2.push(c),
                    _ => {
                        params[cur_param].1 = i;
                        cur_param += 1;
                        rematch = true;
                    }
                }
            },
        }
        if rematch {
            match c {
                ':' => state = MaybeInNamedParam,
                '\'' => state = InStringLiteral('\'', '\''),
                '"' => state = InStringLiteral('"', '"'),
                _ => state = TopLevel,
            }
        }
    }
    match state {
        InNamedParam => params[cur_param].1 = query.len(),
        _ => (),
    }
    if params.len() > 0 {
        if have_positional {
            return Err(DriverError(MixedParams));
        }
        let mut real_query = String::with_capacity(query.len());
        let mut last = 0;
        let mut out_params = Vec::with_capacity(params.len());
        for (start, end, name) in params.into_iter() {
            real_query.push_str(&query[last..start]);
            real_query.push('?');
            last = end;
            out_params.push(name);
        }
        real_query.push_str(&query[last..]);
        Ok((Some(out_params), real_query.into()))
    } else {
        Ok((None, query.into()))
    }
}

#[cfg(test)]
mod test {
    use error::Error::DriverError;
    use error::DriverError::MixedParams;
    use named_params::parse_named_params;

    #[test]
    fn should_parse_named_params() {
        let result = parse_named_params(":a :b").unwrap();
        assert_eq!((Some(vec!["a".to_string(), "b".into()]), "? ?".into()), result);

        let result = parse_named_params("SELECT (:a-10)").unwrap();
        assert_eq!((Some(vec!["a".to_string()]), "SELECT (?-10)".into()), result);

        let result = parse_named_params(r#"SELECT '"\':a' "'\"':c" :b"#).unwrap();
        assert_eq!((Some(vec!["b".to_string()]), r#"SELECT '"\':a' "'\"':c" ?"#.into()), result);

        let result = parse_named_params(r":a_Aa:b").unwrap();
        assert_eq!((Some(vec!["a_".to_string(), "b".into()]), r"?Aa?".into()), result);

        let result = parse_named_params(r"::b").unwrap();
        assert_eq!((Some(vec!["b".to_string()]), r":?".into()), result);

        let err = parse_named_params(r":a ?").unwrap_err();
        match err {
            DriverError(MixedParams) => (),
            _ => unreachable!(),
        }
    }

    #[cfg(feature = "nightly")]
    mod bench {
        use test;
        use named_params::parse_named_params;

        #[bench]
        fn parse_ten_named_params(bencher: &mut test::Bencher) {
            bencher.iter(|| {
                let result = parse_named_params(r#"
                SELECT :one, :two, :three, :four, :five, :six, :seven, :eight, :nine, :ten
                "#).unwrap();
                test::black_box(result);
            });
        }

        #[bench]
        fn parse_zero_named_params(bencher: &mut test::Bencher) {
            bencher.iter(|| {
                let result = parse_named_params(r"
                SELECT one, two, three, four, five, six, seven, eight, nine, ten
                ").unwrap();
                test::black_box(result);
            });
        }
    }
}
<commit_msg>Allow numbers in named param name. Fix #58<commit_after>use std::borrow::Cow;
use error::{
    Result as MyResult,
};
use error::Error::DriverError;
use error::DriverError::MixedParams;

enum ParserState {
    TopLevel,
    // (string_delimiter, last_char)
    InStringLiteral(char, char),
    MaybeInNamedParam,
    InNamedParam,
}

use self::ParserState::*;

pub fn parse_named_params<'a>(query: &'a str) -> MyResult<(Option<Vec<String>>, Cow<'a, str>)> {
    let mut state = TopLevel;
    let mut have_positional = false;
    let mut cur_param = 0;
    // Vec<(start_offset, end_offset, name)>
    let mut params = Vec::new();
    for (i, c) in query.chars().enumerate() {
        let mut rematch = false;
        match state {
            TopLevel => {
                match c {
                    ':' => state = MaybeInNamedParam,
                    '\'' => state = InStringLiteral('\'', '\''),
                    '"' => state = InStringLiteral('"', '"'),
                    '?' => have_positional = true,
                    _ => (),
                }
            },
            InStringLiteral(separator, prev_char) => {
                match c {
                    x if x == separator && prev_char != '\\' => state = TopLevel,
                    x => state = InStringLiteral(separator, x),
                }
            },
            MaybeInNamedParam => {
                match c {
                    'a' ... 'z' | '_' => {
                        params.push((i - 1, 0, String::with_capacity(16)));
                        params[cur_param].2.push(c);
                        state = InNamedParam;
                    },
                    _ => rematch = true,
                }
            },
            InNamedParam => {
                match c {
                    'a' ... 'z' | '0' ... '9' | '_' => params[cur_param].2.push(c),
                    _ => {
                        params[cur_param].1 = i;
                        cur_param += 1;
                        rematch = true;
                    }
                }
            },
        }
        if rematch {
            match c {
                ':' => state = MaybeInNamedParam,
                '\'' => state = InStringLiteral('\'', '\''),
                '"' => state = InStringLiteral('"', '"'),
                _ => state = TopLevel,
            }
        }
    }
    match state {
        InNamedParam => params[cur_param].1 = query.len(),
        _ => (),
    }
    if params.len() > 0 {
        if have_positional {
            return Err(DriverError(MixedParams));
        }
        let mut real_query = String::with_capacity(query.len());
        let mut last = 0;
        let mut out_params = Vec::with_capacity(params.len());
        for (start, end, name) in params.into_iter() {
            real_query.push_str(&query[last..start]);
            real_query.push('?');
            last = end;
            out_params.push(name);
        }
        real_query.push_str(&query[last..]);
        Ok((Some(out_params), real_query.into()))
    } else {
        Ok((None, query.into()))
    }
}

#[cfg(test)]
mod test {
    use error::Error::DriverError;
    use error::DriverError::MixedParams;
    use named_params::parse_named_params;

    #[test]
    fn should_parse_named_params() {
        let result = parse_named_params(":a :b").unwrap();
        assert_eq!((Some(vec!["a".to_string(), "b".into()]), "? ?".into()), result);

        let result = parse_named_params("SELECT (:a-10)").unwrap();
        assert_eq!((Some(vec!["a".to_string()]), "SELECT (?-10)".into()), result);

        let result = parse_named_params(r#"SELECT '"\':a' "'\"':c" :b"#).unwrap();
        assert_eq!((Some(vec!["b".to_string()]), r#"SELECT '"\':a' "'\"':c" ?"#.into()), result);

        let result = parse_named_params(r":a_Aa:b").unwrap();
        assert_eq!((Some(vec!["a_".to_string(), "b".into()]), r"?Aa?".into()), result);

        let result = parse_named_params(r"::b").unwrap();
        assert_eq!((Some(vec!["b".to_string()]), r":?".into()), result);

        let err = parse_named_params(r":a ?").unwrap_err();
        match err {
            DriverError(MixedParams) => (),
            _ => unreachable!(),
        }
    }

    #[test]
    fn should_allow_numbers_in_param_name() {
        let result = parse_named_params(":a1 :a2").unwrap();
        assert_eq!((Some(vec!["a1".to_string(), "a2".to_string()]), "? ?".into()), result);

        let result = parse_named_params(":1a :2a").unwrap();
        assert_eq!((None, ":1a :2a".into()), result);
    }

    #[cfg(feature = "nightly")]
    mod bench {
        use test;
        use named_params::parse_named_params;

        #[bench]
        fn parse_ten_named_params(bencher: &mut test::Bencher) {
            bencher.iter(|| {
                let result = parse_named_params(r#"
                SELECT :one, :two, :three, :four, :five, :six, :seven, :eight, :nine, :ten
                "#).unwrap();
                test::black_box(result);
            });
        }

        #[bench]
        fn parse_zero_named_params(bencher: &mut test::Bencher) {
            bencher.iter(|| {
                let result = parse_named_params(r"
                SELECT one, two, three, four, five, six, seven, eight, nine, ten
                ").unwrap();
                test::black_box(result);
            });
        }
    }
}
<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

// TODO: Includes cleanup is required.
use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

/// Encodes and attempts to write the given non-owning ValueRef into the given Write.
///
/// # Errors
///
/// This function returns Error with an underlying I/O error if unable to properly write entire
/// value. Interruption errors are handled internally by silent operation restarting.
///
/// # Examples
/// ```
/// use rmp::ValueRef;
/// use rmp::encode::value_ref::write_value_ref;
///
/// let mut buf = Vec::new();
/// let val = ValueRef::String("le message");
///
/// write_value_ref(&mut buf, &val).unwrap();
/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
/// ```
pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<commit_msg>Misc: wording.<commit_after>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

// TODO: Includes cleanup is required.
use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

/// Encodes and attempts to write the given non-owning ValueRef into the Write.
///
/// # Errors
///
/// This function returns Error with an underlying I/O error if unable to properly write entire
/// value. Interruption errors are handled internally by silent operation restarting.
///
/// # Examples
/// ```
/// use rmp::ValueRef;
/// use rmp::encode::value_ref::write_value_ref;
///
/// let mut buf = Vec::new();
/// let val = ValueRef::String("le message");
///
/// write_value_ref(&mut buf, &val).unwrap();
/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
/// ```
pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use ffi;
use python::{Python, ToPythonPointer, PythonObject};
use conversion::ToPyObject;
use objects::{PyObject, PyList};
use err::{self, PyResult, PyErr};

/// Represents a Python `dict`.
pub struct PyDict<'p>(PyObject<'p>);

pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);

impl <'p> PyDict<'p> {
    /// Creates a new empty dictionary.
    ///
    /// May panic when running out of memory.
    pub fn new(py: Python<'p>) -> PyDict<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
        }
    }

    /// Return a new dictionary that contains the same key-value pairs as self.
    /// Corresponds to `dict(self)` in Python.
    pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
        let py = self.python();
        unsafe {
            err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.as_ptr()))
        }
    }

    /// Empty an existing dictionary of all key-value pairs.
    #[inline]
    pub fn clear(&self) {
        unsafe { ffi::PyDict_Clear(self.as_ptr()) }
    }

    /// Return the number of items in the dictionary.
    /// This is equivalent to len(p) on a dictionary.
    #[inline]
    pub fn len(&self) -> usize {
        unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
    }

    /// Determine if the dictionary contains the specified key.
    /// This is equivalent to the Python expression `key in self`.
    pub fn contains<K>(&self, key: K) -> PyResult<'p, bool> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            match ffi::PyDict_Contains(self.as_ptr(), key) {
                1 => Ok(true),
                0 => Ok(false),
                _ => Err(PyErr::fetch(py))
            }
        })
    }

    /// Gets an item from the dictionary.
    /// Returns None if the item is not present, or if an error occurs.
    pub fn get_item<K>(&self, key: K) -> Option<PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            PyObject::from_borrowed_ptr_opt(py,
                ffi::PyDict_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression `self[key] = value`.
    pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                err::error_on_minusone(py,
                    ffi::PyDict_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression `del self[key]`.
    pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            err::error_on_minusone(py,
                ffi::PyDict_DelItem(self.as_ptr(), key))
        })
    }

    // List of dict items.
    // This is equivalent to the `dict.items()` method.
    pub fn items(&self) -> PyList {
        let py = self.python();
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_Items(self.as_ptr()))
        }
    }
}
<commit_msg>Implement ToPyObject for HashMap->dict conversion.<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use ffi;
use python::{Python, ToPythonPointer, PythonObject};
use conversion::ToPyObject;
use objects::{PyObject, PyList};
use err::{self, PyResult, PyErr};
use std::collections::HashMap;
use std::hash::Hash;
use std::cmp::Eq;

/// Represents a Python `dict`.
pub struct PyDict<'p>(PyObject<'p>);

pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);

impl <'p> PyDict<'p> {
    /// Creates a new empty dictionary.
    ///
    /// May panic when running out of memory.
    pub fn new(py: Python<'p>) -> PyDict<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
        }
    }

    /// Return a new dictionary that contains the same key-value pairs as self.
    /// Corresponds to `dict(self)` in Python.
    pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
        let py = self.python();
        unsafe {
            err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.as_ptr()))
        }
    }

    /// Empty an existing dictionary of all key-value pairs.
    #[inline]
    pub fn clear(&self) {
        unsafe { ffi::PyDict_Clear(self.as_ptr()) }
    }

    /// Return the number of items in the dictionary.
    /// This is equivalent to len(p) on a dictionary.
    #[inline]
    pub fn len(&self) -> usize {
        unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
    }

    /// Determine if the dictionary contains the specified key.
    /// This is equivalent to the Python expression `key in self`.
    pub fn contains<K>(&self, key: K) -> PyResult<'p, bool> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            match ffi::PyDict_Contains(self.as_ptr(), key) {
                1 => Ok(true),
                0 => Ok(false),
                _ => Err(PyErr::fetch(py))
            }
        })
    }

    /// Gets an item from the dictionary.
    /// Returns None if the item is not present, or if an error occurs.
    pub fn get_item<K>(&self, key: K) -> Option<PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            PyObject::from_borrowed_ptr_opt(py,
                ffi::PyDict_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression `self[key] = value`.
    pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                err::error_on_minusone(py,
                    ffi::PyDict_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression `del self[key]`.
    pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            err::error_on_minusone(py,
                ffi::PyDict_DelItem(self.as_ptr(), key))
        })
    }

    // List of dict items.
    // This is equivalent to the `dict.items()` method.
    pub fn items(&self) -> PyList {
        let py = self.python();
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_Items(self.as_ptr()))
        }
    }
}

// TODO: use macros to make implementations for different maps
impl <'p, K, V> ToPyObject<'p> for HashMap<K, V> where K: Hash+Eq+ToPyObject<'p>, V: ToPyObject<'p> {
    type ObjectType = PyDict<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyDict<'p> {
        let ptr = unsafe { ffi::PyDict_New() };
        let t = unsafe { err::cast_from_owned_ptr_or_panic(py, ptr) };
        for (key, value) in self.iter() {
            key.with_borrowed_ptr(py, move |key|
                value.with_borrowed_ptr(py, |value| unsafe {
                    ffi::PyDict_SetItem(ptr, key, value);
                }))
        };
        t
    }
}
<|endoftext|>