<commit_before>//! Create and manage user-defined networks that containers can be attached to.
//!
//! API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Network>

use std::{
    collections::{BTreeMap, HashMap},
    hash::Hash,
};

use hyper::Body;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use url::form_urlencoded;

use crate::{
    docker::Docker,
    errors::{Error, Result},
};

/// Interface for docker network
pub struct Networks<'docker> {
    docker: &'docker Docker,
}

impl<'docker> Networks<'docker> {
    /// Exports an interface for interacting with docker Networks
    pub fn new(docker: &'docker Docker) -> Self {
        Networks { docker }
    }

    /// List the docker networks on the current docker host
    pub async fn list(
        &self,
        opts: &NetworkListOptions,
    ) -> Result<Vec<NetworkInfo>> {
        let mut path = vec!["/networks".to_owned()];
        if let Some(query) = opts.serialize() {
            path.push(query);
        }
        self.docker.get_json(&path.join("?")).await
    }

    /// Returns a reference to a set of operations available to a specific network instance
    pub fn get<S>(
        &self,
        id: S,
    ) -> Network<'docker>
    where
        S: Into<String>,
    {
        Network::new(self.docker, id)
    }

    /// Create a new Network instance
    pub async fn create(
        &self,
        opts: &NetworkCreateOptions,
    ) -> Result<NetworkCreateInfo> {
        let body: Body = opts.serialize()?.into();
        let path = vec!["/networks/create".to_owned()];

        self.docker
            .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON)))
            .await
    }
}

/// Interface for accessing and manipulating a docker network
pub struct Network<'docker> {
    docker: &'docker Docker,
    id: String,
}

impl<'docker> Network<'docker> {
    /// Exports an interface exposing operations against a network instance
    pub fn new<S>(
        docker: &'docker Docker,
        id: S,
    ) -> Self
    where
        S: Into<String>,
    {
        Network {
            docker,
            id: id.into(),
        }
    }

    /// a getter for the Network id
    pub fn id(&self) -> &str {
        &self.id
    }

    /// Inspects the current docker network instance's details
    pub async fn inspect(&self) -> Result<NetworkInfo> {
        self.docker
            .get_json(&format!("/networks/{}", self.id)[..])
            .await
    }

    /// Delete the network instance
    pub async fn delete(&self) -> Result<()> {
        self.docker
            .delete(&format!("/networks/{}", self.id)[..])
            .await?;
        Ok(())
    }

    /// Connect container to network
    pub async fn connect(
        &self,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        self.do_connection("connect", opts).await
    }

    /// Disconnect container to network
    pub async fn disconnect(
        &self,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        self.do_connection("disconnect", opts).await
    }

    async fn do_connection(
        &self,
        segment: &str,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        let body: Body = opts.serialize()?.into();

        self.docker
            .post(
                &format!("/networks/{}/{}", self.id, segment)[..],
                Some((body, mime::APPLICATION_JSON)),
            )
            .await?;
        Ok(())
    }
}

/// Options for filtering networks list results
#[derive(Default, Debug)]
pub struct NetworkListOptions {
    params: HashMap<&'static str, String>,
}

impl NetworkListOptions {
    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Option<String> {
        if self.params.is_empty() {
            None
        } else {
            Some(
                form_urlencoded::Serializer::new(String::new())
                    .extend_pairs(&self.params)
                    .finish(),
            )
        }
    }
}

/// Interface for creating new docker network
#[derive(Serialize, Debug)]
pub struct NetworkCreateOptions {
    params: HashMap<&'static str, Value>,
}

impl NetworkCreateOptions {
    /// return a new instance of a builder for options
    pub fn builder(name: &str) -> NetworkCreateOptionsBuilder {
        NetworkCreateOptionsBuilder::new(name)
    }

    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Result<String> {
        serde_json::to_string(&self.params).map_err(Error::from)
    }

    pub fn parse_from<'a, K, V>(
        &self,
        params: &'a HashMap<K, V>,
        body: &mut serde_json::Map<String, Value>,
    ) where
        &'a HashMap<K, V>: IntoIterator,
        K: ToString + Eq + Hash,
        V: Serialize,
    {
        for (k, v) in params.iter() {
            let key = k.to_string();
            let value = serde_json::to_value(v).unwrap();

            body.insert(key, value);
        }
    }
}

#[derive(Default)]
pub struct NetworkCreateOptionsBuilder {
    params: HashMap<&'static str, Value>,
}

impl NetworkCreateOptionsBuilder {
    pub(crate) fn new(name: &str) -> Self {
        let mut params = HashMap::new();
        params.insert("Name", json!(name));
        NetworkCreateOptionsBuilder { params }
    }

    pub fn driver(
        &mut self,
        name: &str,
    ) -> &mut Self {
        if !name.is_empty() {
            self.params.insert("Driver", json!(name));
        }
        self
    }

    pub fn label(
        &mut self,
        labels: HashMap<String, String>,
    ) -> &mut Self {
        self.params.insert("Labels", json!(labels));
        self
    }

    pub fn build(&self) -> NetworkCreateOptions {
        NetworkCreateOptions {
            params: self.params.clone(),
        }
    }
}

/// Interface for connect container to network
#[derive(Serialize, Debug)]
pub struct ContainerConnectionOptions {
    params: HashMap<&'static str, Value>,
}

impl ContainerConnectionOptions {
    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Result<String> {
        serde_json::to_string(&self.params).map_err(Error::from)
    }

    pub fn parse_from<'a, K, V>(
        &self,
        params: &'a HashMap<K, V>,
        body: &mut BTreeMap<String, Value>,
    ) where
        &'a HashMap<K, V>: IntoIterator,
        K: ToString + Eq + Hash,
        V: Serialize,
    {
        for (k, v) in params.iter() {
            let key = k.to_string();
            let value = serde_json::to_value(v).unwrap();

            body.insert(key, value);
        }
    }

    /// return a new instance of a builder for options
    pub fn builder(container_id: &str) -> ContainerConnectionOptionsBuilder {
        ContainerConnectionOptionsBuilder::new(container_id)
    }
}

#[derive(Default)]
pub struct ContainerConnectionOptionsBuilder {
    params: HashMap<&'static str, Value>,
}

impl ContainerConnectionOptionsBuilder {
    pub(crate) fn new(container_id: &str) -> Self {
        let mut params = HashMap::new();
        params.insert("Container", json!(container_id));
        ContainerConnectionOptionsBuilder { params }
    }

    pub fn aliases(
        &mut self,
        aliases: Vec<&str>,
    ) -> &mut Self {
        self.params
            .insert("EndpointConfig", json!({ "Aliases": json!(aliases) }));
        self
    }

    pub fn force(&mut self) -> &mut Self {
        self.params.insert("Force", json!(true));
        self
    }

    pub fn build(&self) -> ContainerConnectionOptions {
        ContainerConnectionOptions {
            params: self.params.clone(),
        }
    }
}

type PortDescription = HashMap<String, Option<Vec<HashMap<String, String>>>>;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkSettings {
    pub bridge: String,
    pub gateway: String,
    #[serde(rename = "IPAddress")]
    pub ip_address: String,
    #[serde(rename = "IPPrefixLen")]
    pub ip_prefix_len: u64,
    pub mac_address: String,
    pub ports: Option<PortDescription>,
    pub networks: HashMap<String, NetworkEntry>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkEntry {
    #[serde(rename = "NetworkID")]
    pub network_id: String,
    #[serde(rename = "EndpointID")]
    pub endpoint_id: String,
    pub gateway: String,
    #[serde(rename = "IPAddress")]
    pub ip_address: String,
    #[serde(rename = "IPPrefixLen")]
    pub ip_prefix_len: u64,
    #[serde(rename = "IPv6Gateway")]
    pub ipv6_gateway: String,
    #[serde(rename = "GlobalIPv6Address")]
    pub global_ipv6_address: String,
    #[serde(rename = "GlobalIPv6PrefixLen")]
    pub global_ipv6_prefix_len: u64,
    pub mac_address: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NetworkInfo {
    pub rx_dropped: u64,
    pub rx_bytes: u64,
    pub rx_errors: u64,
    pub tx_packets: u64,
    pub tx_dropped: u64,
    pub rx_packets: u64,
    pub tx_errors: u64,
    pub tx_bytes: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct IPAM {
    pub driver: String,
    pub config: Vec<HashMap<String, String>>,
    pub options: Option<HashMap<String, String>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkDetails {
    pub name: String,
    pub id: String,
    pub scope: String,
    pub driver: String,
    #[serde(rename = "EnableIPv6")]
    pub enable_ipv6: bool,
    #[serde(rename = "IPAM")]
    pub ipam: IPAM,
    pub internal: bool,
    pub attachable: bool,
    pub containers: HashMap<String, NetworkContainerDetails>,
    pub options: Option<HashMap<String, String>>,
    pub labels: Option<HashMap<String, String>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkContainerDetails {
    #[serde(rename = "EndpointID")]
    pub endpoint_id: String,
    pub mac_address: String,
    #[serde(rename = "IPv4Address")]
    pub ipv4_address: String,
    #[serde(rename = "IPv6Address")]
    pub ipv6_address: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkCreateInfo {
    pub id: String,
    pub warning: String,
}
<commit_msg>Add missing fields to NetworkEntry<commit_after>//! Create and manage user-defined networks that containers can be attached to.
//!
//! API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Network>

use std::{
    collections::{BTreeMap, HashMap},
    hash::Hash,
};

use hyper::Body;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use url::form_urlencoded;

use crate::{
    docker::Docker,
    errors::{Error, Result},
};

/// Interface for docker network
pub struct Networks<'docker> {
    docker: &'docker Docker,
}

impl<'docker> Networks<'docker> {
    /// Exports an interface for interacting with docker Networks
    pub fn new(docker: &'docker Docker) -> Self {
        Networks { docker }
    }

    /// List the docker networks on the current docker host
    pub async fn list(
        &self,
        opts: &NetworkListOptions,
    ) -> Result<Vec<NetworkInfo>> {
        let mut path = vec!["/networks".to_owned()];
        if let Some(query) = opts.serialize() {
            path.push(query);
        }
        self.docker.get_json(&path.join("?")).await
    }

    /// Returns a reference to a set of operations available to a specific network instance
    pub fn get<S>(
        &self,
        id: S,
    ) -> Network<'docker>
    where
        S: Into<String>,
    {
        Network::new(self.docker, id)
    }

    /// Create a new Network instance
    pub async fn create(
        &self,
        opts: &NetworkCreateOptions,
    ) -> Result<NetworkCreateInfo> {
        let body: Body = opts.serialize()?.into();
        let path = vec!["/networks/create".to_owned()];

        self.docker
            .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON)))
            .await
    }
}

/// Interface for accessing and manipulating a docker network
pub struct Network<'docker> {
    docker: &'docker Docker,
    id: String,
}

impl<'docker> Network<'docker> {
    /// Exports an interface exposing operations against a network instance
    pub fn new<S>(
        docker: &'docker Docker,
        id: S,
    ) -> Self
    where
        S: Into<String>,
    {
        Network {
            docker,
            id: id.into(),
        }
    }

    /// a getter for the Network id
    pub fn id(&self) -> &str {
        &self.id
    }

    /// Inspects the current docker network instance's details
    pub async fn inspect(&self) -> Result<NetworkInfo> {
        self.docker
            .get_json(&format!("/networks/{}", self.id)[..])
            .await
    }

    /// Delete the network instance
    pub async fn delete(&self) -> Result<()> {
        self.docker
            .delete(&format!("/networks/{}", self.id)[..])
            .await?;
        Ok(())
    }

    /// Connect container to network
    pub async fn connect(
        &self,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        self.do_connection("connect", opts).await
    }

    /// Disconnect container to network
    pub async fn disconnect(
        &self,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        self.do_connection("disconnect", opts).await
    }

    async fn do_connection(
        &self,
        segment: &str,
        opts: &ContainerConnectionOptions,
    ) -> Result<()> {
        let body: Body = opts.serialize()?.into();

        self.docker
            .post(
                &format!("/networks/{}/{}", self.id, segment)[..],
                Some((body, mime::APPLICATION_JSON)),
            )
            .await?;
        Ok(())
    }
}

/// Options for filtering networks list results
#[derive(Default, Debug)]
pub struct NetworkListOptions {
    params: HashMap<&'static str, String>,
}

impl NetworkListOptions {
    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Option<String> {
        if self.params.is_empty() {
            None
        } else {
            Some(
                form_urlencoded::Serializer::new(String::new())
                    .extend_pairs(&self.params)
                    .finish(),
            )
        }
    }
}

/// Interface for creating new docker network
#[derive(Serialize, Debug)]
pub struct NetworkCreateOptions {
    params: HashMap<&'static str, Value>,
}

impl NetworkCreateOptions {
    /// return a new instance of a builder for options
    pub fn builder(name: &str) -> NetworkCreateOptionsBuilder {
        NetworkCreateOptionsBuilder::new(name)
    }

    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Result<String> {
        serde_json::to_string(&self.params).map_err(Error::from)
    }

    pub fn parse_from<'a, K, V>(
        &self,
        params: &'a HashMap<K, V>,
        body: &mut serde_json::Map<String, Value>,
    ) where
        &'a HashMap<K, V>: IntoIterator,
        K: ToString + Eq + Hash,
        V: Serialize,
    {
        for (k, v) in params.iter() {
            let key = k.to_string();
            let value = serde_json::to_value(v).unwrap();

            body.insert(key, value);
        }
    }
}

#[derive(Default)]
pub struct NetworkCreateOptionsBuilder {
    params: HashMap<&'static str, Value>,
}

impl NetworkCreateOptionsBuilder {
    pub(crate) fn new(name: &str) -> Self {
        let mut params = HashMap::new();
        params.insert("Name", json!(name));
        NetworkCreateOptionsBuilder { params }
    }

    pub fn driver(
        &mut self,
        name: &str,
    ) -> &mut Self {
        if !name.is_empty() {
            self.params.insert("Driver", json!(name));
        }
        self
    }

    pub fn label(
        &mut self,
        labels: HashMap<String, String>,
    ) -> &mut Self {
        self.params.insert("Labels", json!(labels));
        self
    }

    pub fn build(&self) -> NetworkCreateOptions {
        NetworkCreateOptions {
            params: self.params.clone(),
        }
    }
}

/// Interface for connect container to network
#[derive(Serialize, Debug)]
pub struct ContainerConnectionOptions {
    params: HashMap<&'static str, Value>,
}

impl ContainerConnectionOptions {
    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Result<String> {
        serde_json::to_string(&self.params).map_err(Error::from)
    }

    pub fn parse_from<'a, K, V>(
        &self,
        params: &'a HashMap<K, V>,
        body: &mut BTreeMap<String, Value>,
    ) where
        &'a HashMap<K, V>: IntoIterator,
        K: ToString + Eq + Hash,
        V: Serialize,
    {
        for (k, v) in params.iter() {
            let key = k.to_string();
            let value = serde_json::to_value(v).unwrap();

            body.insert(key, value);
        }
    }

    /// return a new instance of a builder for options
    pub fn builder(container_id: &str) -> ContainerConnectionOptionsBuilder {
        ContainerConnectionOptionsBuilder::new(container_id)
    }
}

#[derive(Default)]
pub struct ContainerConnectionOptionsBuilder {
    params: HashMap<&'static str, Value>,
}

impl ContainerConnectionOptionsBuilder {
    pub(crate) fn new(container_id: &str) -> Self {
        let mut params = HashMap::new();
        params.insert("Container", json!(container_id));
        ContainerConnectionOptionsBuilder { params }
    }

    pub fn aliases(
        &mut self,
        aliases: Vec<&str>,
    ) -> &mut Self {
        self.params
            .insert("EndpointConfig", json!({ "Aliases": json!(aliases) }));
        self
    }

    pub fn force(&mut self) -> &mut Self {
        self.params.insert("Force", json!(true));
        self
    }

    pub fn build(&self) -> ContainerConnectionOptions {
        ContainerConnectionOptions {
            params: self.params.clone(),
        }
    }
}

type PortDescription = HashMap<String, Option<Vec<HashMap<String, String>>>>;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkSettings {
    pub bridge: String,
    pub gateway: String,
    #[serde(rename = "IPAddress")]
    pub ip_address: String,
    #[serde(rename = "IPPrefixLen")]
    pub ip_prefix_len: u64,
    pub mac_address: String,
    pub ports: Option<PortDescription>,
    pub networks: HashMap<String, NetworkEntry>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkEntry {
    #[serde(rename = "NetworkID")]
    pub network_id: String,
    #[serde(rename = "EndpointID")]
    pub endpoint_id: String,
    pub gateway: String,
    #[serde(rename = "IPAddress")]
    pub ip_address: String,
    #[serde(rename = "IPPrefixLen")]
    pub ip_prefix_len: u64,
    #[serde(rename = "IPv6Gateway")]
    pub ipv6_gateway: String,
    #[serde(rename = "GlobalIPv6Address")]
    pub global_ipv6_address: String,
    #[serde(rename = "GlobalIPv6PrefixLen")]
    pub global_ipv6_prefix_len: u64,
    pub mac_address: String,
    pub links: Option<Vec<String>>,
    pub aliases: Option<Vec<String>>,
    #[serde(rename = "IPAMConfig")]
    pub ipam_config: Option<EndpointIPAMConfig>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EndpointIPAMConfig {
    #[serde(rename = "IPv4Address")]
    pub ipv4_address: String,
    #[serde(rename = "IPv6Address")]
    pub ipv6_address: String,
    #[serde(rename = "LinkLocalIPs")]
    pub link_local_ips: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NetworkInfo {
    pub rx_dropped: u64,
    pub rx_bytes: u64,
    pub rx_errors: u64,
    pub tx_packets: u64,
    pub tx_dropped: u64,
    pub rx_packets: u64,
    pub tx_errors: u64,
    pub tx_bytes: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct IPAM {
    pub driver: String,
    pub config: Vec<HashMap<String, String>>,
    pub options: Option<HashMap<String, String>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkDetails {
    pub name: String,
    pub id: String,
    pub scope: String,
    pub driver: String,
    #[serde(rename = "EnableIPv6")]
    pub enable_ipv6: bool,
    #[serde(rename = "IPAM")]
    pub ipam: IPAM,
    pub internal: bool,
    pub attachable: bool,
    pub containers: HashMap<String, NetworkContainerDetails>,
    pub options: Option<HashMap<String, String>>,
    pub labels: Option<HashMap<String, String>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkContainerDetails {
    #[serde(rename = "EndpointID")]
    pub endpoint_id: String,
    pub mac_address: String,
    #[serde(rename = "IPv4Address")]
    pub ipv4_address: String,
    #[serde(rename = "IPv6Address")]
    pub ipv6_address: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkCreateInfo {
    pub id: String,
    pub warning: String,
}
<|endoftext|>


<commit_before>#[derive(Debug, serde_derive::Deserialize)]
pub struct Config {
    pub name: String,
    pub package_key: Option<String>,
    pub repo_key: Option<String>,
    pub srcdest: String,
    pub logdest: String,
    pub pkgbuild: String,
    pub builds: std::collections::HashMap<super::builder::Arch, BuildConfig>,
    pub s3: Option<S3Config>,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct BuildConfig {
    pub chroot: String,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct S3Config {
    pub bucket: String,
    pub region: Region,
}

#[derive(Debug)]
pub struct Region(rusoto_core::Region);

impl<'de> serde::Deserialize<'de> for Region {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = Region;

            fn expecting(
                &self,
                formatter: &mut std::fmt::Formatter,
            ) -> Result<(), std::fmt::Error> {
                write!(formatter, "a valid AWS region name")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use std::str::FromStr;

                match rusoto_core::Region::from_str(v) {
                    Ok(r) => Ok(Region(r)),
                    Err(e) => Err(serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(v),
                        &format!("{}", e).as_str(),
                    )),
                }
            }
        }
        deserializer.deserialize_str(Visitor {})
    }
}

impl Config {
    pub fn from_reader<R>(reader: R) -> serde_yaml::Result<Self>
    where
        R: std::io::Read,
    {
        serde_yaml::from_reader(reader)
    }

    pub fn repo_dir(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.name)
            .join("os")
            .join(format!("{}", arch))
    }

    pub fn db_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".db");
        std::path::PathBuf::from(path)
    }

    pub fn files_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".files");
        std::path::PathBuf::from(path)
    }

    pub fn package_dir(&self, package_name: &str) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.pkgbuild).join(package_name)
    }
}

pub struct S3 {
    client: rusoto_s3::S3Client,
    bucket: String,
}

impl S3 {
    pub fn new(config: &S3Config) -> Self {
        let Region(ref region) = config.region;
        let client = rusoto_s3::S3Client::new(region.clone());
        S3 {
            client,
            bucket: config.bucket.to_owned(),
        }
    }

    pub async fn download_repository(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
    ) -> Result<(), anyhow::Error> {
        self.get(config.db_path(arch)).await?;
        self.get(config.files_path(arch)).await
    }

    pub async fn upload_repository<P>(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
        package_paths: &[P],
    ) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        const XZ_MIME_TYPE: &str = "application/x-xz";
        const SIG_MIME_TYPE: &str = "application/pgp-signature";
        const GZIP_MIME_TYPE: &str = "application/gzip";

        for package_path in package_paths {
            self.put(package_path, XZ_MIME_TYPE).await?;
            if config.package_key.is_some() {
                let mut sig_path = package_path.as_ref().as_os_str().to_os_string();
                sig_path.push(".sig");
                self.put(&sig_path, SIG_MIME_TYPE).await?;
            }
        }
        self.put(config.files_path(arch), GZIP_MIME_TYPE).await?;
        let db_path = config.db_path(arch);
        self.put(&db_path, GZIP_MIME_TYPE).await?;
        if config.repo_key.is_some() {
            let mut sig_path = db_path.clone().into_os_string();
            sig_path.push(".sig");
            self.put(sig_path, SIG_MIME_TYPE).await?;
        }
        Ok(())
    }

    async fn get<P>(&self, path: P) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use rusoto_s3::S3;
        use std::io::Write;

        let path = path.as_ref();
        let mut file = std::fs::File::create(path)?;
        let request = rusoto_s3::GetObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            ..rusoto_s3::GetObjectRequest::default()
        };
        println!("Download {}", path.display());
        match self.client.get_object(request).await {
            Ok(output) => {
                if let Some(body) = output.body {
                    use futures::StreamExt;
                    body.for_each(|buf| {
                        let bytes = buf.unwrap();
                        file.write_all(&bytes[..]).unwrap();
                        futures::future::ready(())
                    })
                    .await;
                }
                Ok(())
            }
            Err(rusoto_core::RusotoError::Service(rusoto_s3::GetObjectError::NoSuchKey(_))) => {
                Ok(())
            }
            Err(e) => Err(anyhow::Error::from(e)),
        }
    }

    async fn put<P>(&self, path: P, content_type: &str) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use futures::FutureExt as _;
        use futures::TryStreamExt as _;
        use rusoto_s3::S3;

        let path = path.as_ref();
        let metadata = tokio::fs::metadata(path).await?;
        let stream = rusoto_s3::StreamingBody::new(
            tokio::fs::read(path.to_owned())
                .into_stream()
                .map_ok(|b| bytes::Bytes::from(b)),
        );
        let request = rusoto_s3::PutObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            content_type: Some(content_type.to_owned()),
            content_length: Some(metadata.len() as i64),
            body: Some(stream),
            ..Default::default()
        };
        println!("Upload {}", path.display());
        self.client.put_object(request).await?;
        Ok(())
    }
}
<commit_msg>Use tokio helpers<commit_after>#[derive(Debug, serde_derive::Deserialize)]
pub struct Config {
    pub name: String,
    pub package_key: Option<String>,
    pub repo_key: Option<String>,
    pub srcdest: String,
    pub logdest: String,
    pub pkgbuild: String,
    pub builds: std::collections::HashMap<super::builder::Arch, BuildConfig>,
    pub s3: Option<S3Config>,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct BuildConfig {
    pub chroot: String,
}

#[derive(Debug, serde_derive::Deserialize)]
pub struct S3Config {
    pub bucket: String,
    pub region: Region,
}

#[derive(Debug)]
pub struct Region(rusoto_core::Region);

impl<'de> serde::Deserialize<'de> for Region {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = Region;

            fn expecting(
                &self,
                formatter: &mut std::fmt::Formatter,
            ) -> Result<(), std::fmt::Error> {
                write!(formatter, "a valid AWS region name")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use std::str::FromStr;

                match rusoto_core::Region::from_str(v) {
                    Ok(r) => Ok(Region(r)),
                    Err(e) => Err(serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(v),
                        &format!("{}", e).as_str(),
                    )),
                }
            }
        }
        deserializer.deserialize_str(Visitor {})
    }
}

impl Config {
    pub fn from_reader<R>(reader: R) -> serde_yaml::Result<Self>
    where
        R: std::io::Read,
    {
        serde_yaml::from_reader(reader)
    }

    pub fn repo_dir(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.name)
            .join("os")
            .join(format!("{}", arch))
    }

    pub fn db_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".db");
        std::path::PathBuf::from(path)
    }

    pub fn files_path(&self, arch: &super::builder::Arch) -> std::path::PathBuf {
        let mut path = self.repo_dir(arch).join(&self.name).into_os_string();
        path.push(".files");
        std::path::PathBuf::from(path)
    }

    pub fn package_dir(&self, package_name: &str) -> std::path::PathBuf {
        std::path::PathBuf::from(&self.pkgbuild).join(package_name)
    }
}

pub struct S3 {
    client: rusoto_s3::S3Client,
    bucket: String,
}

impl S3 {
    pub fn new(config: &S3Config) -> Self {
        let Region(ref region) = config.region;
        let client = rusoto_s3::S3Client::new(region.clone());
        S3 {
            client,
            bucket: config.bucket.to_owned(),
        }
    }

    pub async fn download_repository(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
    ) -> Result<(), anyhow::Error> {
        self.get(config.db_path(arch)).await?;
        self.get(config.files_path(arch)).await
    }

    pub async fn upload_repository<P>(
        &self,
        config: &Config,
        arch: &super::builder::Arch,
        package_paths: &[P],
    ) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        const XZ_MIME_TYPE: &str = "application/x-xz";
        const SIG_MIME_TYPE: &str = "application/pgp-signature";
        const GZIP_MIME_TYPE: &str = "application/gzip";

        for package_path in package_paths {
            self.put(package_path, XZ_MIME_TYPE).await?;
            if config.package_key.is_some() {
                let mut sig_path = package_path.as_ref().as_os_str().to_os_string();
                sig_path.push(".sig");
                self.put(&sig_path, SIG_MIME_TYPE).await?;
            }
        }
        self.put(config.files_path(arch), GZIP_MIME_TYPE).await?;
        let db_path = config.db_path(arch);
        self.put(&db_path, GZIP_MIME_TYPE).await?;
        if config.repo_key.is_some() {
            let mut sig_path = db_path.clone().into_os_string();
            sig_path.push(".sig");
            self.put(sig_path, SIG_MIME_TYPE).await?;
        }
        Ok(())
    }

    async fn get<P>(&self, path: P) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use rusoto_s3::S3;

        let path = path.as_ref();
        let request = rusoto_s3::GetObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            ..rusoto_s3::GetObjectRequest::default()
        };
        println!("Download {}", path.display());
        match self.client.get_object(request).await {
            Ok(output) => {
                if let Some(mut body) = output.body {
                    use futures::StreamExt as _;
                    use tokio::io::AsyncWriteExt as _;

                    let file = tokio::fs::File::create(path).await?;
                    let mut writer = tokio::io::BufWriter::new(file);
                    while let Some(item) = body.next().await {
                        writer.write_all(&item?).await?;
                    }
                    writer.shutdown().await?;
                }
                Ok(())
            }
            Err(rusoto_core::RusotoError::Service(rusoto_s3::GetObjectError::NoSuchKey(_))) => {
                Ok(())
            }
            Err(e) => Err(anyhow::Error::from(e)),
        }
    }

    async fn put<P>(&self, path: P, content_type: &str) -> Result<(), anyhow::Error>
    where
        P: AsRef<std::path::Path>,
    {
        use futures::FutureExt as _;
        use futures::TryStreamExt as _;
        use rusoto_s3::S3;

        let path = path.as_ref();
        let metadata = tokio::fs::metadata(path).await?;
        let stream = rusoto_s3::StreamingBody::new(
            tokio::fs::read(path.to_owned())
                .into_stream()
                .map_ok(|b| bytes::Bytes::from(b)),
        );
        let request = rusoto_s3::PutObjectRequest {
            bucket: self.bucket.to_owned(),
            key: path.to_string_lossy().into_owned(),
            content_type: Some(content_type.to_owned()),
            content_length: Some(metadata.len() as i64),
            body: Some(stream),
            ..Default::default()
        };
        println!("Upload {}", path.display());
        self.client.put_object(request).await?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! This module is strictly internal.
//!
//! These functions are used by `date()`, `time()` and `datetime()`.
//! They are currently not private, because the need to be accessible,
//! but are not useful by themselves.
//!
//! Please refer to the top-level functions instead, as they offer a better abstraction.
//!
//! **These functions may be made private later.**

use helper::*;
use nom::{self, is_digit};
use super::{Time, DateTime, Date};

macro_rules! empty_or(
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        if $i.len() == 0 {
            nom::IResult::Done($i, None)
        } else {
            match $submac!($i, $($args)*) {
                nom::IResult::Done(i,o)     => nom::IResult::Done(i, Some(o)),
                nom::IResult::Error(_)      => nom::IResult::Done($i, None),
                nom::IResult::Incomplete(i) => nom::IResult::Incomplete(i)

            }
        }
    );
);

macro_rules! check(
  ($input:expr, $submac:ident!( $($args:tt)* )) => (

    {
      let mut failed = false;
      for idx in 0..$input.len() {
        if !$submac!($input[idx], $($args)*) {
            failed = true;
            break;
        }
      }
      if failed {
        nom::IResult::Error(nom::Err::Position(nom::ErrorKind::Custom(20),$input))
      } else {
        nom::IResult::Done(&b""[..], $input)
      }
    }
  );
  ($input:expr, $f:expr) => (
    check!($input, call!($f));
  );
);

macro_rules! char_between(
    ($input:expr, $min:expr, $max:expr) => (
        {
        fn f(c: u8) -> bool { c >= ($min as u8) && c <= ($max as u8)}
        flat_map!($input, take!(1), check!(f))
        }
    );
);

named!(take_4_digits, flat_map!(take!(4), check!(is_digit)));

// year
named!(year_prefix, alt!(tag!("+") | tag!("-")));
named!(year <i32>, chain!(
        pref: opt!(year_prefix) ~
        year: call!(take_4_digits)
        ,
        || {
            match pref {
                Some(b"-") => -buf_to_i32(year),
                _ => buf_to_i32(year)
            }
        }));

// MM
named!(lower_month <u32>, chain!(tag!("0") ~ s:char_between!('1', '9') , || buf_to_u32(s)));
named!(upper_month <u32>, chain!(tag!("1") ~ s:char_between!('0', '2') , || 10+buf_to_u32(s)));
named!(month <u32>, alt!(lower_month | upper_month));

// DD
named!(day_zero  <u32>, chain!(tag!("0") ~ s:char_between!('1', '9') , || buf_to_u32(s)));
named!(day_one   <u32>, chain!(tag!("1") ~ s:char_between!('0', '9') , || 10+buf_to_u32(s)));
named!(day_two   <u32>, chain!(tag!("2") ~ s:char_between!('0', '9') , || 20+buf_to_u32(s)));
named!(day_three <u32>, chain!(tag!("3") ~ s:char_between!('0', '1') , || 30+buf_to_u32(s)));
named!(day <u32>, alt!(day_zero | day_one | day_two | day_three));

// WW
// reusing day_N parsers, sorry
named!(week_three <u32>, chain!(tag!("3") ~ s:char_between!('0', '9') , || 30+buf_to_u32(s)));
named!(week_four  <u32>, chain!(tag!("4") ~ s:char_between!('0', '9') , || 40+buf_to_u32(s)));
named!(week_five  <u32>, chain!(tag!("5") ~ s:char_between!('0', '3') , || 50+buf_to_u32(s)));

named!(week <u32>, alt!(day_zero | day_one | day_two | week_three| week_four | week_five ));
named!(week_day <u32>, chain!(s:char_between!('1', '7') , || buf_to_u32(s)));

// ordinal DDD
named!(ord_day <u32>, chain!(
        a:char_between!('0','3') ~
        b:char_between!('0','9') ~
        c:char_between!('0','9')
        ,
        || { buf_to_u32(a)*100 + buf_to_u32(b)*10 + buf_to_u32(c) }
        ));

// YYYY-MM-DD
named!(pub ymd_date <Date>, chain!(
        y: year ~
        opt!(tag!("-")) ~
        m: month ~
        opt!(tag!("-")) ~
        d: day
        ,
        || { Date::YMD{ year: y, month: m, day: d } }
        ));

// YYYY-MM-DD
named!(pub ordinal_date <Date>, chain!(
        y: year ~
        opt!(tag!("-")) ~
        d: ord_day
        ,
        || { Date::Ordinal{ year: y, ddd: d } }
        ));

// YYYY-"W"WW-D
named!(pub iso_week_date <Date>, chain!(
        y: year ~
        opt!(tag!("-")) ~
        tag!("W") ~
        w: week ~
        opt!(tag!("-")) ~
        d: week_day
        ,
        || { Date::Week{ year: y, ww: w, d: d } }
        ));

named!(pub parse_date <Date>, alt!( ymd_date | iso_week_date | ordinal_date ) );

// TIME

// HH
named!(lower_hour <u32>, chain!(f:char_between!('0','1') ~ s:char_between!('0','9') ,
                                       || { buf_to_u32(f)*10 + buf_to_u32(s) } ));
named!(upper_hour <u32>, chain!(tag!("2") ~ s:char_between!('0','4') , || 20+buf_to_u32(s)));
named!(hour <u32>, alt!(lower_hour | upper_hour));

// MM
named!(below_sixty <u32>, chain!(f:char_between!('0','5') ~ s:char_between!('0','9'), || { buf_to_u32(f)*10 + buf_to_u32(s) } ));
named!(upto_sixty <u32>, alt!(below_sixty | map!(tag!("60"), |_| 60)));

named!(minute <u32>, call!(below_sixty));
named!(second <u32>, call!(upto_sixty));
named!(millisecond <u32>, map!( is_a!("0123456789"), |ms| buf_to_u32(ms) ) );

// HH:MM:[SS][.(m*)][(Z|+...|-...)]
named!(pub parse_time <Time>, chain!(
        h: hour ~
        opt!(tag!(":")) ~
        m: minute ~
        s:  opt!( chain!( opt!(tag!(":")) ~ s:second, || s)) ~
        ms: opt!( chain!( tag!(".") ~ ms:millisecond, || ms)) ~
        z:  opt!( alt!( timezone_hour | timezone_utc) ) ,
        || {
            Time {
                hour: h,
                minute: m,
                second: s.unwrap_or(0),
                millisecond: ms.unwrap_or(0),
                tz_offset_hours: z.unwrap_or((0,0)).0,
                tz_offset_minutes: z.unwrap_or((0,0)).1
            }
        }
        ));

named!(sign <i32>, alt!(
        tag!("-") => { |_| -1 } |
        tag!("+") => { |_| 1 }
        )
    );

named!(timezone_hour <(i32,i32)>, chain!(
        s: sign ~
        h: hour ~
        m: empty_or!(
            chain!(
                tag!(":")? ~ m: minute , || { m }
            ))
        ,
        || { (s * (h as i32) , s * (m.unwrap_or(0) as i32)) }
        ));

named!(timezone_utc <(i32,i32)>, map!(tag!("Z"), |_| (0,0)));

// Full ISO8601
named!(pub parse_datetime <DateTime>, chain!(
        d: parse_date ~
        tag!("T") ~
        t: parse_time
        ,
        || {
            DateTime{
                date: d,
                time: t,
            }
        }
        ));

#[cfg(test)]
mod tests{

    use super::{year, month, day};
    use super::{hour, minute, second};
    use nom::IResult::*;

    #[test]
    fn test_year() {
        assert_eq!(Done(&[][..],   2015), year(b"2015"));
        assert_eq!(Done(&[][..],  -0333), year(b"-0333"));
        assert_eq!(Done(&b"-"[..], 2015), year(b"2015-"));
        assert!(year(b"abcd").is_err());
        assert!(year(b"2a03").is_err());
    }

    #[test]
    fn test_month() {
        assert_eq!(Done(&[][..], 1),    month(b"01"));
        assert_eq!(Done(&[][..], 6),    month(b"06"));
        assert_eq!(Done(&[][..], 12),   month(b"12"));
        assert_eq!(Done(&b"-"[..], 12), month(b"12-"));

        assert!(month(b"13").is_err());
        assert!(month(b"00").is_err());
    }

    #[test]
    fn test_day() {
        assert_eq!(Done(&[][..], 1),    day(b"01"));
        assert_eq!(Done(&[][..], 12),   day(b"12"));
        assert_eq!(Done(&[][..], 20),   day(b"20"));
        assert_eq!(Done(&[][..], 28),   day(b"28"));
        assert_eq!(Done(&[][..], 30),   day(b"30"));
        assert_eq!(Done(&[][..], 31),   day(b"31"));
        assert_eq!(Done(&b"-"[..], 31), day(b"31-"));

        assert!(day(b"00").is_err());
        assert!(day(b"32").is_err());
    }

    #[test]
    fn test_hour() {
        assert_eq!(Done(&[][..], 0),  hour(b"00"));
        assert_eq!(Done(&[][..], 1),  hour(b"01"));
        assert_eq!(Done(&[][..], 6),  hour(b"06"));
        assert_eq!(Done(&[][..], 12), hour(b"12"));
        assert_eq!(Done(&[][..], 13), hour(b"13"));
        assert_eq!(Done(&[][..], 20), hour(b"20"));
        assert_eq!(Done(&[][..], 24), hour(b"24"));

        assert!(hour(b"25").is_err());
        assert!(hour(b"30").is_err());
        assert!(hour(b"ab").is_err());
    }

    #[test]
    fn test_minute() {
        assert_eq!(Done(&[][..], 0),  minute(b"00"));
        assert_eq!(Done(&[][..], 1),  minute(b"01"));
        assert_eq!(Done(&[][..], 30), minute(b"30"));
        assert_eq!(Done(&[][..], 59), minute(b"59"));

        assert!(minute(b"60").is_err());
        assert!(minute(b"61").is_err());
        assert!(minute(b"ab").is_err());
    }

    #[test]
    fn test_second() {
        assert_eq!(Done(&[][..], 0),  second(b"00"));
        assert_eq!(Done(&[][..], 1),  second(b"01"));
        assert_eq!(Done(&[][..], 30), second(b"30"));
        assert_eq!(Done(&[][..], 59), second(b"59"));
        assert_eq!(Done(&[][..], 60), second(b"60"));

        assert!(second(b"61").is_err());
        assert!(second(b"ab").is_err());
    }

}
<commit_msg>fixed: opt! behaviour change in nom 1.0.0 final<commit_after>//! This module is strictly internal.
//!
//! These functions are used by `date()`, `time()` and `datetime()`.
//! They are currently not private, because the need to be accessible,
//! but are not useful by themselves.
//!
//! Please refer to the top-level functions instead, as they offer a better abstraction.
//!
//! **These functions may be made private later.**

use helper::*;
use nom::{self, is_digit};
use super::{Time, DateTime, Date};

macro_rules! empty_or(
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        if $i.len() == 0 {
            nom::IResult::Done($i, None)
        } else {
            match $submac!($i, $($args)*) {
                nom::IResult::Done(i,o)     => nom::IResult::Done(i, Some(o)),
                nom::IResult::Error(_)      => nom::IResult::Done($i, None),
                nom::IResult::Incomplete(i) => nom::IResult::Incomplete(i)

            }
        }
    );
);

macro_rules! check(
  ($input:expr, $submac:ident!( $($args:tt)* )) => (

    {
      let mut failed = false;
      for idx in 0..$input.len() {
        if !$submac!($input[idx], $($args)*) {
            failed = true;
            break;
        }
      }
      if failed {
        nom::IResult::Error(nom::Err::Position(nom::ErrorKind::Custom(20),$input))
      } else {
        nom::IResult::Done(&b""[..], $input)
      }
    }
  );
  ($input:expr, $f:expr) => (
    check!($input, call!($f));
  );
);

macro_rules! char_between(
    ($input:expr, $min:expr, $max:expr) => (
        {
        fn f(c: u8) -> bool { c >= ($min as u8) && c <= ($max as u8)}
        flat_map!($input, take!(1), check!(f))
        }
    );
);

named!(take_4_digits, flat_map!(take!(4), check!(is_digit)));

// year
named!(year_prefix, alt!(tag!("+") | tag!("-")));
named!(year <i32>, chain!(
        pref: opt!(complete!(year_prefix)) ~
        year: call!(take_4_digits)
        ,
        || {
            match pref {
                Some(b"-") => -buf_to_i32(year),
                _ => buf_to_i32(year)
            }
        }));

// MM
named!(lower_month <u32>, chain!(tag!("0") ~ s:char_between!('1', '9') , || buf_to_u32(s)));
named!(upper_month <u32>, chain!(tag!("1") ~ s:char_between!('0', '2') , || 10+buf_to_u32(s)));
named!(month <u32>, alt!(lower_month | upper_month));

// DD
named!(day_zero  <u32>, chain!(tag!("0") ~ s:char_between!('1', '9') , || buf_to_u32(s)));
named!(day_one   <u32>, chain!(tag!("1") ~ s:char_between!('0', '9') , || 10+buf_to_u32(s)));
named!(day_two   <u32>, chain!(tag!("2") ~ s:char_between!('0', '9') , || 20+buf_to_u32(s)));
named!(day_three <u32>, chain!(tag!("3") ~ s:char_between!('0', '1') , || 30+buf_to_u32(s)));
named!(day <u32>, alt!(day_zero | day_one | day_two | day_three));

// WW
// reusing day_N parsers, sorry
named!(week_three <u32>, chain!(tag!("3") ~ s:char_between!('0', '9') , || 30+buf_to_u32(s)));
named!(week_four  <u32>, chain!(tag!("4") ~ s:char_between!('0', '9') , || 40+buf_to_u32(s)));
named!(week_five  <u32>, chain!(tag!("5") ~ s:char_between!('0', '3') , || 50+buf_to_u32(s)));

named!(week <u32>, alt!(day_zero | day_one | day_two | week_three| week_four | week_five ));
named!(week_day <u32>, chain!(s:char_between!('1', '7') , || buf_to_u32(s)));

// ordinal DDD
named!(ord_day <u32>, chain!(
        a:char_between!('0','3') ~
        b:char_between!('0','9') ~
        c:char_between!('0','9')
        ,
        || { buf_to_u32(a)*100 + buf_to_u32(b)*10 + buf_to_u32(c) }
        ));

// YYYY-MM-DD
named!(pub ymd_date <Date>, chain!(
        y: year ~
        opt!(complete!(tag!("-"))) ~
        m: month ~
        opt!(complete!(tag!("-"))) ~
        d: day
        ,
        || { Date::YMD{ year: y, month: m, day: d } }
        ));

// YYYY-MM-DD
named!(pub ordinal_date <Date>, chain!(
        y: year ~
        opt!(complete!(tag!("-"))) ~
        d: ord_day
        ,
        || { Date::Ordinal{ year: y, ddd: d } }
        ));

// YYYY-"W"WW-D
named!(pub iso_week_date <Date>, chain!(
        y: year ~
        opt!(complete!(tag!("-"))) ~
        tag!("W") ~
        w: week ~
        opt!(complete!(tag!("-"))) ~
        d: week_day
        ,
        || { Date::Week{ year: y, ww: w, d: d } }
        ));

named!(pub parse_date <Date>, alt!( ymd_date | iso_week_date | ordinal_date ) );

// TIME

// HH
named!(lower_hour <u32>, chain!(f:char_between!('0','1') ~ s:char_between!('0','9') ,
                                       || { buf_to_u32(f)*10 + buf_to_u32(s) } ));
named!(upper_hour <u32>, chain!(tag!("2") ~ s:char_between!('0','4') , || 20+buf_to_u32(s)));
named!(hour <u32>, alt!(lower_hour | upper_hour));

// MM
named!(below_sixty <u32>, chain!(f:char_between!('0','5') ~ s:char_between!('0','9'), || { buf_to_u32(f)*10 + buf_to_u32(s) } ));
named!(upto_sixty <u32>, alt!(below_sixty | map!(tag!("60"), |_| 60)));

named!(minute <u32>, call!(below_sixty));
named!(second <u32>, call!(upto_sixty));
named!(millisecond <u32>, map!( is_a!("0123456789"), |ms| buf_to_u32(ms) ) );

// HH:MM:[SS][.(m*)][(Z|+...|-...)]
named!(pub parse_time <Time>, chain!(
        h: hour ~
        opt!(complete!(tag!(":"))) ~
        m: minute ~
        s:  opt!(complete!( chain!( opt!(tag!(":")) ~ s:second, || s))) ~
        ms: opt!(complete!( chain!( tag!(".") ~ ms:millisecond, || ms))) ~
        z:  opt!(complete!( alt!( timezone_hour | timezone_utc) )) ,
        || {
            Time {
                hour: h,
                minute: m,
                second: s.unwrap_or(0),
                millisecond: ms.unwrap_or(0),
                tz_offset_hours: z.unwrap_or((0,0)).0,
                tz_offset_minutes: z.unwrap_or((0,0)).1
            }
        }
        ));

named!(sign <i32>, alt!(
        tag!("-") => { |_| -1 } |
        tag!("+") => { |_| 1 }
        )
    );

named!(timezone_hour <(i32,i32)>, chain!(
        s: sign ~
        h: hour ~
        m: empty_or!(
            chain!(
                tag!(":")? ~ m: minute , || { m }
            ))
        ,
        || { (s * (h as i32) , s * (m.unwrap_or(0) as i32)) }
        ));

named!(timezone_utc <(i32,i32)>, map!(tag!("Z"), |_| (0,0)));

// Full ISO8601
named!(pub parse_datetime <DateTime>, chain!(
        d: parse_date ~
        tag!("T") ~
        t: parse_time
        ,
        || {
            DateTime{
                date: d,
                time: t,
            }
        }
        ));

#[cfg(test)]
mod tests{

    use super::{year, month, day};
    use super::{hour, minute, second};
    use nom::IResult::*;

    #[test]
    fn test_year() {
        assert_eq!(Done(&[][..],   2015), year(b"2015"));
        assert_eq!(Done(&[][..],  -0333), year(b"-0333"));
        assert_eq!(Done(&b"-"[..], 2015), year(b"2015-"));
        assert!(year(b"abcd").is_err());
        assert!(year(b"2a03").is_err());
    }

    #[test]
    fn test_month() {
        assert_eq!(Done(&[][..], 1),    month(b"01"));
        assert_eq!(Done(&[][..], 6),    month(b"06"));
        assert_eq!(Done(&[][..], 12),   month(b"12"));
        assert_eq!(Done(&b"-"[..], 12), month(b"12-"));

        assert!(month(b"13").is_err());
        assert!(month(b"00").is_err());
    }

    #[test]
    fn test_day() {
        assert_eq!(Done(&[][..], 1),    day(b"01"));
        assert_eq!(Done(&[][..], 12),   day(b"12"));
        assert_eq!(Done(&[][..], 20),   day(b"20"));
        assert_eq!(Done(&[][..], 28),   day(b"28"));
        assert_eq!(Done(&[][..], 30),   day(b"30"));
        assert_eq!(Done(&[][..], 31),   day(b"31"));
        assert_eq!(Done(&b"-"[..], 31), day(b"31-"));

        assert!(day(b"00").is_err());
        assert!(day(b"32").is_err());
    }

    #[test]
    fn test_hour() {
        assert_eq!(Done(&[][..], 0),  hour(b"00"));
        assert_eq!(Done(&[][..], 1),  hour(b"01"));
        assert_eq!(Done(&[][..], 6),  hour(b"06"));
        assert_eq!(Done(&[][..], 12), hour(b"12"));
        assert_eq!(Done(&[][..], 13), hour(b"13"));
        assert_eq!(Done(&[][..], 20), hour(b"20"));
        assert_eq!(Done(&[][..], 24), hour(b"24"));

        assert!(hour(b"25").is_err());
        assert!(hour(b"30").is_err());
        assert!(hour(b"ab").is_err());
    }

    #[test]
    fn test_minute() {
        assert_eq!(Done(&[][..], 0),  minute(b"00"));
        assert_eq!(Done(&[][..], 1),  minute(b"01"));
        assert_eq!(Done(&[][..], 30), minute(b"30"));
        assert_eq!(Done(&[][..], 59), minute(b"59"));

        assert!(minute(b"60").is_err());
        assert!(minute(b"61").is_err());
        assert!(minute(b"ab").is_err());
    }

    #[test]
    fn test_second() {
        assert_eq!(Done(&[][..], 0),  second(b"00"));
        assert_eq!(Done(&[][..], 1),  second(b"01"));
        assert_eq!(Done(&[][..], 30), second(b"30"));
        assert_eq!(Done(&[][..], 59), second(b"59"));
        assert_eq!(Done(&[][..], 60), second(b"60"));

        assert!(second(b"61").is_err());
        assert!(second(b"ab").is_err());
    }

}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::marker::PhantomData;
use std::ffi::CString;
use super::gl_lib as gl;
use super::gl_lib::types::*;
use super::types::GLError;
use super::context::Context;
use super::shader::Shader;

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

impl Context {
    pub fn create_program(&self) -> Result<Program, ()> {
        unsafe {
            let id = gl::CreateProgram();
            if id > 0 {
                Ok(Program { gl_id: id })
            }
            else {
                Err(())
            }
        }
    }

    pub fn attach_shader(&self, program: &mut Program, shader: &Shader) {
        unsafe {
            gl::AttachShader(program.gl_id(), shader.gl_id());
        }
    }

    pub fn link_program(&self, program: &mut Program) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(program.gl_id());
            let mut link_status : GLint = 0;
            gl::GetProgramiv(program.gl_id(),
                             gl::LINK_STATUS,
                             &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            unsafe {
                let mut info_length : GLint = mem::uninitialized();
                gl::GetProgramiv(program.gl_id(),
                                 gl::INFO_LOG_LENGTH,
                                 &mut info_length as *mut GLint);

                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(program.gl_id(),
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                bytes.set_len((info_length - 1) as usize);

                let msg = String::from_utf8(bytes)
                                 .unwrap_or(String::from("<Unknown error>"));

                Err(GLError { message: msg })
            }
        }
    }

    pub fn get_attrib_location(&self, program: &Program, name: &str)
        -> Result<ProgramAttrib, ()>
    {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(program.gl_id(), str_ptr);
            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }

    pub fn get_uniform_location(&self, program: &Program, name: &str)
        -> Result<ProgramUniform, ()>
    {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetUniformLocation(program.gl_id(), str_ptr);
            if index >= 0 {
                Ok(ProgramUniform { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }
}

pub struct ProgramBinding<'a> {
    phantom: PhantomData<&'a mut Program>
}

impl<'a> ProgramBinding<'a> {
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind<'a>(&'a mut self, program: &mut Program)
        -> ProgramBinding<'a>
    {
        let binding = ProgramBinding { phantom: PhantomData };
        unsafe {
            gl::UseProgram(program.gl_id());
        }
        binding
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramUniform {
    pub gl_index: GLuint
}
<commit_msg>Add `ProgramBinding.set_uniform()`<commit_after>use std::mem;
use std::ptr;
use std::marker::PhantomData;
use std::ffi::CString;
use super::gl_lib as gl;
use super::gl_lib::types::*;
use super::types::GLError;
use super::context::Context;
use super::shader::Shader;
use super::uniform_data::{UniformData, UniformDatumType, UniformPrimitiveType};

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

impl Context {
    pub fn create_program(&self) -> Result<Program, ()> {
        unsafe {
            let id = gl::CreateProgram();
            if id > 0 {
                Ok(Program { gl_id: id })
            }
            else {
                Err(())
            }
        }
    }

    pub fn attach_shader(&self, program: &mut Program, shader: &Shader) {
        unsafe {
            gl::AttachShader(program.gl_id(), shader.gl_id());
        }
    }

    pub fn link_program(&self, program: &mut Program) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(program.gl_id());
            let mut link_status : GLint = 0;
            gl::GetProgramiv(program.gl_id(),
                             gl::LINK_STATUS,
                             &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            unsafe {
                let mut info_length : GLint = mem::uninitialized();
                gl::GetProgramiv(program.gl_id(),
                                 gl::INFO_LOG_LENGTH,
                                 &mut info_length as *mut GLint);

                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(program.gl_id(),
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                bytes.set_len((info_length - 1) as usize);

                let msg = String::from_utf8(bytes)
                                 .unwrap_or(String::from("<Unknown error>"));

                Err(GLError { message: msg })
            }
        }
    }

    pub fn get_attrib_location(&self, program: &Program, name: &str)
        -> Result<ProgramAttrib, ()>
    {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(program.gl_id(), str_ptr);
            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }

    pub fn get_uniform_location(&self, program: &Program, name: &str)
        -> Result<ProgramUniform, ()>
    {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetUniformLocation(program.gl_id(), str_ptr);
            if index >= 0 {
                Ok(ProgramUniform { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }
}

pub struct ProgramBinding<'a> {
    phantom: PhantomData<&'a mut Program>
}

impl<'a> ProgramBinding<'a> {
    pub fn set_uniform<T>(&self, uniform: ProgramUniform, val: T)
        where T: UniformData
    {
        let idx = uniform.gl_index as GLint;
        let count = val.uniform_elements() as GLsizei;
        let ptr = val.uniform_bytes().as_ptr();
        unsafe {
            match T::uniform_datum_type() {
                UniformDatumType::Vec1(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform1fv(idx, count, ptr as *const GLfloat)
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform1iv(idx, count, ptr as *const GLint)
                        }
                    }
                },
                UniformDatumType::Vec2(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform2fv(idx, count, ptr as *const GLfloat)
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform2iv(idx, count, ptr as *const GLint)
                        }
                    }
                },
                UniformDatumType::Vec3(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform2fv(idx, count, ptr as *const GLfloat)
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform2iv(idx, count, ptr as *const GLint)
                        }
                    }
                },
                UniformDatumType::Vec4(p) => {
                    match p {
                        UniformPrimitiveType::Float => {
                            gl::Uniform2fv(idx, count, ptr as *const GLfloat)
                        },
                        UniformPrimitiveType::Int => {
                            gl::Uniform2iv(idx, count, ptr as *const GLint)
                        }
                    }
                },
                UniformDatumType::Matrix2x2 => {
                    gl::UniformMatrix2fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat)
                },
                UniformDatumType::Matrix3x3 => {
                    gl::UniformMatrix3fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat)
                },
                UniformDatumType::Matrix4x4 => {
                    gl::UniformMatrix4fv(idx,
                                         count,
                                         gl::FALSE,
                                         ptr as *const GLfloat)
                },
            }
        }
    }
}

pub struct ProgramBinder;
impl ProgramBinder {
    pub fn bind<'a>(&'a mut self, program: &mut Program)
        -> ProgramBinding<'a>
    {
        let binding = ProgramBinding { phantom: PhantomData };
        unsafe {
            gl::UseProgram(program.gl_id());
        }
        binding
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramUniform {
    pub gl_index: GLuint
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    vm.apply(command);
                    index = index + 1;
                }
                self.instruction_pointer = Some(index);
            }
        }
    }
}
<commit_msg>Adjust depth when jump commands are seen<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward { self.current_depth = self.current_depth + 1}
                    if command == Command::JumpBackward { self.current_depth = self.current_depth - 1}
                    vm.apply(command);
                    index = index + 1;
                }
                self.instruction_pointer = Some(index);
            }
        }
    }
}
<|endoftext|>


<commit_before>use {CameraCalibration, Error, MountCalibration, Result, ScanPosition, utils};
use element::Extension;
use nalgebra::Projective3;
use scan_position::Image;
use std::collections::HashMap;
use std::io::Read;
use std::path::{Path, PathBuf};
use xmltree::Element;

/// A RiSCAN Pro project.
///
/// These are always created by pointing at a path, either the `.RiSCAN` path or the `project.rsp`
/// in that directory:
///
/// ```
/// use riscan_pro::Project;
/// let project1 = Project::from_path("data/project.RiSCAN").unwrap();
/// let project2 = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
/// assert_eq!(project1, project2);
/// ```
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct Project {
    /// The camera calibrations, by name.
    pub camera_calibrations: HashMap<String, CameraCalibration>,
    /// The camera mount calibrations, by name.
    pub mount_calibrations: HashMap<String, MountCalibration>,
    /// The scan positions, by name.
    pub scan_positions: HashMap<String, ScanPosition>,
    /// The project's own position.
    pub pop: Projective3<f64>,
}

impl Project {
    /// Creates a project from a filesystem path.
    ///
    /// This path can be either the `.RiSCAN` directory or the contained `project.rsp`.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        use std::fs::File;

        let path = rsp_path(path)?;
        let file = File::open(path)?;
        Project::from_read(file)
    }

    /// Returns a scan position, as determined by the path.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_position1 = project.scan_positions.get("SP01").unwrap();
    /// let path = "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv";
    /// let scan_position2 = project.scan_position_from_path(path).unwrap();
    /// assert_eq!(scan_position1, scan_position2);
    /// ```
    pub fn scan_position_from_path<P: AsRef<Path>>(&self, path: P) -> Result<&ScanPosition> {
        path.as_ref()
            .file_stem()
            .map(|file_stem| file_stem.to_string_lossy())
            .and_then(|file_stem| {
                file_stem.split(" - ").next().and_then(|name| {
                    self.scan_positions.get(name)
                })
            })
            .ok_or_else(|| Error::ScanPositionFromPath(path.as_ref().to_path_buf()))
    }

    fn from_read<R: Read>(read: R) -> Result<Project> {
        let xml = Element::parse(read)?;

        let camera_calibrations = xml.children("calibrations/camcalibs/camcalib_opencv")?
            .iter()
            .map(|camcalib_opencv| {
                let camera_calibration = CameraCalibration::from_element(camcalib_opencv)?;
                Ok((camera_calibration.name.clone(), camera_calibration))
            })
            .collect::<Result<HashMap<_, _>>>()?;
        let mount_calibrations = xml.children("calibrations/mountcalibs/mountcalib")?
            .iter()
            .map(|mountcalib| {
                let mount_calibration = MountCalibration::from_element(mountcalib)?;
                Ok((mount_calibration.name.clone(), mount_calibration))
            })
            .collect::<Result<HashMap<_, _>>>()?;
        let scan_positions = xml.children("scanpositions/scanposition")?
            .iter()
            .map(|scanposition| {
                let scan_position = ScanPosition::from_element(scanposition)?;
                Ok((scan_position.name.clone(), scan_position))
            })
            .collect::<Result<HashMap<_, _>>>()?;

        Ok(Project {
            camera_calibrations: camera_calibrations,
            mount_calibrations: mount_calibrations,
            scan_positions: scan_positions,
            pop: utils::parse_projective3(xml.child("pop/matrix")?.as_str()?)?,
        })
    }
}

impl CameraCalibration {
    fn from_element(element: &Element) -> Result<CameraCalibration> {
        let version = element.child("version")?.as_str()?;
        if version == "2" {
            Ok(CameraCalibration {
                name: element.child("name")?.as_str()?.to_string(),
                cx: element.child("internal_opencv/cx")?.parse_text()?,
                cy: element.child("internal_opencv/cy")?.parse_text()?,
                fx: element.child("internal_opencv/fx")?.parse_text()?,
                fy: element.child("internal_opencv/fy")?.parse_text()?,
                k1: element.child("internal_opencv/k1")?.parse_text()?,
                k2: element.child("internal_opencv/k2")?.parse_text()?,
                k3: element.child("internal_opencv/k3")?.parse_text()?,
                k4: element.child("internal_opencv/k4")?.parse_text()?,
                p1: element.child("internal_opencv/p1")?.parse_text()?,
                p2: element.child("internal_opencv/p2")?.parse_text()?,
                tan_max_horz: element.child("angle_extents/tan_max_horz")?.parse_text()?,
                tan_max_vert: element.child("angle_extents/tan_max_vert")?.parse_text()?,
                tan_min_horz: element.child("angle_extents/tan_min_horz")?.parse_text()?,
                tan_min_vert: element.child("angle_extents/tan_min_vert")?.parse_text()?,
                width: element.child("intrinsic_opencv/nx")?.parse_text()?,
                height: element.child("intrinsic_opencv/ny")?.parse_text()?,
            })
        } else {
            Err(Error::CameraCalibrationVersion(version.to_string()))
        }
    }
}

impl MountCalibration {
    fn from_element(element: &Element) -> Result<MountCalibration> {
        Ok(MountCalibration {
            name: element.child("name")?.as_str()?.to_string(),
            matrix: utils::parse_projective3(element.child("matrix")?.as_str()?)?,
        })
    }
}

impl ScanPosition {
    fn from_element(element: &Element) -> Result<ScanPosition> {
        Ok(ScanPosition {
            name: element.child("name")?.as_str()?.to_string(),
            images: element
                .children("scanposimages/scanposimage")?
                .iter()
                .map(|scanposimage| {
                    let image = Image::from_element(scanposimage)?;
                    Ok((image.name.clone(), image))
                })
                .collect::<Result<HashMap<_, _>>>()?,
            sop: utils::parse_projective3(element.child("sop/matrix")?.as_str()?)?,
        })
    }
}

impl Image {
    fn from_element(element: &Element) -> Result<Image> {
        Ok(Image {
            name: element.child("name")?.as_str()?.to_string(),
            cop: utils::parse_projective3(element.child("cop/matrix")?.as_str()?)?,
            camera_calibration_name: element.child("camcalib_ref")?.noderef()?.to_string(),
            mount_calibration_name: element.child("mountcalib_ref")?.noderef()?.to_string(),
        })
    }
}

fn rsp_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
    if let Some(extension) = path.as_ref().extension() {
        let mut path_buf = path.as_ref().to_path_buf();
        if extension == "rsp" {
            return Ok(path_buf);
        } else if extension == "RiSCAN" {
            path_buf.push("project.rsp");
            return Ok(path_buf);
        }
    }
    let mut path_buf = PathBuf::new();
    for component in path.as_ref().iter() {
        path_buf.push(component);
        if Path::new(component)
            .extension()
            .map(|e| e == "RiSCAN")
            .unwrap_or(false)
        {
            return rsp_path(path_buf);
        }
    }
    Err(Error::ProjectPath(path_buf))
}

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

    #[test]
    fn from_path() {
        Project::from_path("data/project.RiSCAN").unwrap();
        Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
        Project::from_path("data/project.RiSCAN/SCANS").unwrap();
        assert!(Project::from_path("data").is_err());
    }

    #[test]
    fn mount_calibrations() {
        use utils;

        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let mount_calibration = project
            .mount_calibrations
            .get("Infratec_VarioCAM_HD_15mm_11-16-2015_Preston")
            .unwrap();
        let matrix = utils::parse_projective3("-0.010877741999999997 -0.003724941 -0.999933898 0.18508641   0.019274697 0.999806486 -0.0039341460000000013 0.000460517   0.99975505 -0.019316217 -0.01080384 -0.092802787   0 0 0 1").unwrap();
        assert_eq!(matrix, **mount_calibration);
    }

    #[test]
    fn scan_position_from_path() {
        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let scan_position1 = project.scan_positions.get("SP01").unwrap();
        let scan_position2 = project
            .scan_position_from_path(
                "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv",
            )
            .unwrap();
        assert_eq!(scan_position1, scan_position2);
    }

    #[test]
    fn only_accept_version_2_camera_calibrations() {
        Project::from_path("data/project.RiSCAN").unwrap();
        assert!(Project::from_path("data/camera-calibration-version-0.rsp").is_err());
        assert!(Project::from_path("data/camera-calibration-version-1.rsp").is_err());
    }
}
<commit_msg>Add path attribute to project<commit_after>use {CameraCalibration, Error, MountCalibration, Result, ScanPosition, utils};
use element::Extension;
use nalgebra::Projective3;
use scan_position::Image;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use xmltree::Element;

/// A RiSCAN Pro project.
///
/// These are always created by pointing at a path, either the `.RiSCAN` path or the `project.rsp`
/// in that directory:
///
/// ```
/// use riscan_pro::Project;
/// let project1 = Project::from_path("data/project.RiSCAN").unwrap();
/// let project2 = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
/// assert_eq!(project1, project2);
/// ```
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct Project {
    /// The path to the project rsp file.
    pub path: PathBuf,
    /// The camera calibrations, by name.
    pub camera_calibrations: HashMap<String, CameraCalibration>,
    /// The camera mount calibrations, by name.
    pub mount_calibrations: HashMap<String, MountCalibration>,
    /// The scan positions, by name.
    pub scan_positions: HashMap<String, ScanPosition>,
    /// The project's own position.
    pub pop: Projective3<f64>,
}

impl Project {
    /// Creates a project from a filesystem path.
    ///
    /// This path can be either the `.RiSCAN` directory or the contained `project.rsp`.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        use std::fs::File;

        let path = rsp_path(path)?;
        let file = File::open(&path)?;
        let xml = Element::parse(file)?;

        let camera_calibrations = xml.children("calibrations/camcalibs/camcalib_opencv")?
            .iter()
            .map(|camcalib_opencv| {
                let camera_calibration = CameraCalibration::from_element(camcalib_opencv)?;
                Ok((camera_calibration.name.clone(), camera_calibration))
            })
            .collect::<Result<HashMap<_, _>>>()?;
        let mount_calibrations = xml.children("calibrations/mountcalibs/mountcalib")?
            .iter()
            .map(|mountcalib| {
                let mount_calibration = MountCalibration::from_element(mountcalib)?;
                Ok((mount_calibration.name.clone(), mount_calibration))
            })
            .collect::<Result<HashMap<_, _>>>()?;
        let scan_positions = xml.children("scanpositions/scanposition")?
            .iter()
            .map(|scanposition| {
                let scan_position = ScanPosition::from_element(scanposition)?;
                Ok((scan_position.name.clone(), scan_position))
            })
            .collect::<Result<HashMap<_, _>>>()?;

        Ok(Project {
            camera_calibrations: camera_calibrations,
            mount_calibrations: mount_calibrations,
            scan_positions: scan_positions,
            path: path.to_path_buf(),
            pop: utils::parse_projective3(xml.child("pop/matrix")?.as_str()?)?,
        })
    }

    /// Returns a scan position, as determined by the path.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_position1 = project.scan_positions.get("SP01").unwrap();
    /// let path = "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv";
    /// let scan_position2 = project.scan_position_from_path(path).unwrap();
    /// assert_eq!(scan_position1, scan_position2);
    /// ```
    pub fn scan_position_from_path<P: AsRef<Path>>(&self, path: P) -> Result<&ScanPosition> {
        path.as_ref()
            .file_stem()
            .map(|file_stem| file_stem.to_string_lossy())
            .and_then(|file_stem| {
                file_stem.split(" - ").next().and_then(|name| {
                    self.scan_positions.get(name)
                })
            })
            .ok_or_else(|| Error::ScanPositionFromPath(path.as_ref().to_path_buf()))
    }
}

impl CameraCalibration {
    fn from_element(element: &Element) -> Result<CameraCalibration> {
        let version = element.child("version")?.as_str()?;
        if version == "2" {
            Ok(CameraCalibration {
                name: element.child("name")?.as_str()?.to_string(),
                cx: element.child("internal_opencv/cx")?.parse_text()?,
                cy: element.child("internal_opencv/cy")?.parse_text()?,
                fx: element.child("internal_opencv/fx")?.parse_text()?,
                fy: element.child("internal_opencv/fy")?.parse_text()?,
                k1: element.child("internal_opencv/k1")?.parse_text()?,
                k2: element.child("internal_opencv/k2")?.parse_text()?,
                k3: element.child("internal_opencv/k3")?.parse_text()?,
                k4: element.child("internal_opencv/k4")?.parse_text()?,
                p1: element.child("internal_opencv/p1")?.parse_text()?,
                p2: element.child("internal_opencv/p2")?.parse_text()?,
                tan_max_horz: element.child("angle_extents/tan_max_horz")?.parse_text()?,
                tan_max_vert: element.child("angle_extents/tan_max_vert")?.parse_text()?,
                tan_min_horz: element.child("angle_extents/tan_min_horz")?.parse_text()?,
                tan_min_vert: element.child("angle_extents/tan_min_vert")?.parse_text()?,
                width: element.child("intrinsic_opencv/nx")?.parse_text()?,
                height: element.child("intrinsic_opencv/ny")?.parse_text()?,
            })
        } else {
            Err(Error::CameraCalibrationVersion(version.to_string()))
        }
    }
}

impl MountCalibration {
    fn from_element(element: &Element) -> Result<MountCalibration> {
        Ok(MountCalibration {
            name: element.child("name")?.as_str()?.to_string(),
            matrix: utils::parse_projective3(element.child("matrix")?.as_str()?)?,
        })
    }
}

impl ScanPosition {
    fn from_element(element: &Element) -> Result<ScanPosition> {
        Ok(ScanPosition {
            name: element.child("name")?.as_str()?.to_string(),
            images: element
                .children("scanposimages/scanposimage")?
                .iter()
                .map(|scanposimage| {
                    let image = Image::from_element(scanposimage)?;
                    Ok((image.name.clone(), image))
                })
                .collect::<Result<HashMap<_, _>>>()?,
            sop: utils::parse_projective3(element.child("sop/matrix")?.as_str()?)?,
        })
    }
}

impl Image {
    fn from_element(element: &Element) -> Result<Image> {
        Ok(Image {
            name: element.child("name")?.as_str()?.to_string(),
            cop: utils::parse_projective3(element.child("cop/matrix")?.as_str()?)?,
            camera_calibration_name: element.child("camcalib_ref")?.noderef()?.to_string(),
            mount_calibration_name: element.child("mountcalib_ref")?.noderef()?.to_string(),
        })
    }
}

fn rsp_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
    if let Some(extension) = path.as_ref().extension() {
        let mut path_buf = path.as_ref().to_path_buf();
        if extension == "rsp" {
            return Ok(path_buf);
        } else if extension == "RiSCAN" {
            path_buf.push("project.rsp");
            return Ok(path_buf);
        }
    }
    let mut path_buf = PathBuf::new();
    for component in path.as_ref().iter() {
        path_buf.push(component);
        if Path::new(component)
            .extension()
            .map(|e| e == "RiSCAN")
            .unwrap_or(false)
        {
            return rsp_path(path_buf);
        }
    }
    Err(Error::ProjectPath(path_buf))
}

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

    #[test]
    fn from_path() {
        Project::from_path("data/project.RiSCAN").unwrap();
        Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
        Project::from_path("data/project.RiSCAN/SCANS").unwrap();
        assert!(Project::from_path("data").is_err());
    }

    #[test]
    fn mount_calibrations() {
        use utils;

        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let mount_calibration = project
            .mount_calibrations
            .get("Infratec_VarioCAM_HD_15mm_11-16-2015_Preston")
            .unwrap();
        let matrix = utils::parse_projective3("-0.010877741999999997 -0.003724941 -0.999933898 0.18508641   0.019274697 0.999806486 -0.0039341460000000013 0.000460517   0.99975505 -0.019316217 -0.01080384 -0.092802787   0 0 0 1").unwrap();
        assert_eq!(matrix, **mount_calibration);
    }

    #[test]
    fn scan_position_from_path() {
        let project = Project::from_path("data/project.RiSCAN").unwrap();
        let scan_position1 = project.scan_positions.get("SP01").unwrap();
        let scan_position2 = project
            .scan_position_from_path(
                "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv",
            )
            .unwrap();
        assert_eq!(scan_position1, scan_position2);
    }

    #[test]
    fn only_accept_version_2_camera_calibrations() {
        Project::from_path("data/project.RiSCAN").unwrap();
        assert!(Project::from_path("data/camera-calibration-version-0.rsp").is_err());
        assert!(Project::from_path("data/camera-calibration-version-1.rsp").is_err());
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::os::unix::prelude::*;
#[cfg(unix)]
use std::ffi::OsString;
#[cfg(windows)]
use std::str;

#[allow(dead_code)]
#[derive(Debug)]
pub enum RawPath {
    Path(PathBuf),
    Bytes(Vec<u8>),
}

impl RawPath {
    #[allow(dead_code)]
    pub fn new() -> Self {
        RawPath::Bytes(vec![])
    }

    pub fn from_bytes(bytes: Vec<u8>) -> Self {
        Self::from_bytes_(bytes)
    }

    pub fn as_path(&self) -> Option<&Path> {
        match *self {
            RawPath::Path(ref p) => Some(&p),
            RawPath::Bytes(_) => None,
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        match *self {
            RawPath::Path(ref p) => path2bytes(p),
            RawPath::Bytes(ref b) => b,
        }
    }

    #[cfg(windows)]
    fn from_bytes_(bytes: Vec<u8>) -> Self {
        if let Ok(s) = str::from_utf8(&bytes) {
            return RawPath::Path(PathBuf::from(s));
        }
        RawPath::Bytes(bytes)
    }

    #[cfg(unix)]
    fn from_bytes_(bytes: Vec<u8>) -> Self {
        RawPath::Path(PathBuf::from(OsString::from_vec(bytes)))
    }
}

#[cfg(windows)]
fn path2bytes(p: &Path) -> &[u8] {
    p.as_os_str().to_str().unwrap().as_bytes()
}

#[cfg(unix)]
fn path2bytes(p: &Path) -> &[u8] {
    p.as_os_str().as_bytes()
}
<commit_msg>Cleanup rawpath module<commit_after>pub use self::os::RawPath;

#[cfg(unix)]
mod os {
    use std::path::{Path, PathBuf};
    use std::os::unix::prelude::*;
    use std::ffi::OsString;

    #[derive(Debug)]
    pub struct RawPath(PathBuf);

    impl RawPath {
        #[allow(dead_code)]
        pub fn new() -> Self {
            Self::from_bytes(vec![])
        }

        pub fn from_bytes(bytes: Vec<u8>) -> Self {
            RawPath(PathBuf::from(OsString::from_vec(bytes)))
        }

        pub fn as_path(&self) -> Option<&Path> {
            Some(&self.0)
        }

        pub fn as_bytes(&self) -> &[u8] {
            self.0.as_os_str().as_bytes()
        }
    }
}

#[cfg(windows)]
mod os {
    use std::path::{Path, PathBuf};
    use std::str;

    #[derive(Debug)]
    pub enum RawPath {
        Path(PathBuf),
        Bytes(Vec<u8>),
    }

    impl RawPath {
        #[allow(dead_code)]
        pub fn new() -> Self {
            RawPath::Bytes(vec![])
        }

        pub fn from_bytes(bytes: Vec<u8>) -> Self {
            if let Ok(s) = str::from_utf8(&bytes) {
                return RawPath::Path(PathBuf::from(s));
            }
            RawPath::Bytes(bytes)
        }

        pub fn as_path(&self) -> Option<&Path> {
            match *self {
                RawPath::Path(ref p) => Some(&p),
                RawPath::Bytes(_) => None,
            }
        }

        pub fn as_bytes(&self) -> &[u8] {
            match *self {
                RawPath::Path(ref p) => p.as_os_str().to_str().unwrap().as_bytes(),
                RawPath::Bytes(ref b) => b,
            }
        }
    }
}
<|endoftext|>