<commit_before>//! Scan positions and their consituant parts.

use Result;
use nalgebra::Projective3;
use std::collections::HashMap;
use std::path::Path;

/// A scan position
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct ScanPosition {
    /// The name of the scan position.
    pub name: String,
    /// The scan position images.
    pub images: HashMap<String, Image>,
    /// The scanner's own position.
    pub sop: Projective3<f64>,
}

/// A scan position image.
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct Image {
    /// The name of the image.
    pub name: String,
    /// The camera's own position when taking the image.
    pub cop: Projective3<f64>,
    /// The name of the image's camera calibration.
    pub camera_calibration_name: String,
    /// The name of the image's mount calibration.
    pub mount_calibration_name: String,
}

impl ScanPosition {
    /// Returns a scan position image, as determined by the path.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_position = project.scan_positions.get("SP01").unwrap();
    /// let image1 = scan_position.images.get("SP01 - Image001").unwrap();
    /// let path = "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv";
    /// let image2 = scan_position.image_from_path(path).unwrap();
    /// assert_eq!(image1, image2);
    /// ```
    pub fn image_from_path<P: AsRef<Path>>(&self, path: P) -> Result<&Image> {
        use Error;
        path.as_ref()
            .file_stem()
            .map(|file_stem| file_stem.to_string_lossy())
            .and_then(|file_stem| self.images.get(file_stem.as_ref()))
            .ok_or(Error::ImageFromPath(path.as_ref().to_path_buf()))
    }
}
<commit_msg>Add calibration accessors to Image<commit_after>//! Scan positions and their consituant parts.

use {CameraCalibration, Error, MountCalibration, Project, Result};
use nalgebra::Projective3;
use std::collections::HashMap;
use std::path::Path;

/// A scan position
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct ScanPosition {
    /// The name of the scan position.
    pub name: String,
    /// The scan position images.
    pub images: HashMap<String, Image>,
    /// The scanner's own position.
    pub sop: Projective3<f64>,
}

/// A scan position image.
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct Image {
    /// The name of the image.
    pub name: String,
    /// The camera's own position when taking the image.
    pub cop: Projective3<f64>,
    /// The name of the image's camera calibration.
    pub camera_calibration_name: String,
    /// The name of the image's mount calibration.
    pub mount_calibration_name: String,
}

impl ScanPosition {
    /// Returns a scan position image, as determined by the path.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_position = project.scan_positions.get("SP01").unwrap();
    /// let image1 = scan_position.images.get("SP01 - Image001").unwrap();
    /// let path = "data/project.RiSCAN/SCANS/SP01/SCANPOSIMAGES/SP01 - Image001.csv";
    /// let image2 = scan_position.image_from_path(path).unwrap();
    /// assert_eq!(image1, image2);
    /// ```
    pub fn image_from_path<P: AsRef<Path>>(&self, path: P) -> Result<&Image> {
        use Error;
        path.as_ref()
            .file_stem()
            .map(|file_stem| file_stem.to_string_lossy())
            .and_then(|file_stem| self.images.get(file_stem.as_ref()))
            .ok_or(Error::ImageFromPath(path.as_ref().to_path_buf()))
    }
}

impl Image {
    /// Finds and returns this image's camera calibration.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/Project.RiSCAN").unwrap();
    /// let mut image = project.scan_positions
    ///     .get("SP01")
    ///     .unwrap()
    ///     .images
    ///     .get("SP01 - Image001")
    ///     .unwrap()
    ///     .clone();
    /// image.camera_calibration(&project).unwrap();
    /// image.camera_calibration_name = "Not a camera calibration".to_string();
    /// assert!(image.camera_calibration(&project).is_err());
    /// ```
    pub fn camera_calibration<'a>(&self, project: &'a Project) -> Result<&'a CameraCalibration> {
        project
            .camera_calibrations
            .get(&self.camera_calibration_name)
            .ok_or(Error::MissingCameraCalibration(
                self.camera_calibration_name.clone(),
            ))
    }

    /// Finds and returns this image's mount calibration.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/Project.RiSCAN").unwrap();
    /// let mut image = project.scan_positions
    ///     .get("SP01")
    ///     .unwrap()
    ///     .images
    ///     .get("SP01 - Image001")
    ///     .unwrap()
    ///     .clone();
    /// image.mount_calibration(&project).unwrap();
    /// image.mount_calibration_name = "Not a mount calibration".to_string();
    /// assert!(image.mount_calibration(&project).is_err());
    /// ```
    pub fn mount_calibration<'a>(&self, project: &'a Project) -> Result<&'a MountCalibration> {
        project
            .mount_calibrations
            .get(&self.mount_calibration_name)
            .ok_or(Error::MissingMountCalibration(
                self.mount_calibration_name.clone(),
            ))
    }
}
<|endoftext|>


<commit_before>//! Schannel credentials.
use winapi::shared::{sspi, winerror};
use winapi::shared::minwindef as winapi;
use winapi::um::{self, wincrypt};
use std::io;
use std::mem;
use std::ptr;
use std::sync::Arc;

use crate::Inner;
use crate::cert_context::CertContext;

lazy_static! {
    static ref UNISP_NAME: Vec<u8> = um::schannel::UNISP_NAME.bytes().chain(Some(0)).collect();
}

/// The communication direction that an `SchannelCred` will support.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum Direction {
    /// Server-side, inbound connections.
    Inbound,
    /// Client-side, outbound connections.
    Outbound,
}

/// Algorithms supported by Schannel.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum Algorithm {
    /// Advanced Encryption Standard (AES).
    Aes = wincrypt::CALG_AES,
    /// 128 bit AES.
    Aes128 = wincrypt::CALG_AES_128,
    /// 192 bit AES.
    Aes192 = wincrypt::CALG_AES_192,
    /// 256 bit AES.
    Aes256 = wincrypt::CALG_AES_256,
    /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
    AgreedkeyAny = wincrypt::CALG_AGREEDKEY_ANY,
    /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
    /// its key length 64 bits.
    CylinkMek = wincrypt::CALG_CYLINK_MEK,
    /// DES encryption algorithm.
    Des = wincrypt::CALG_DES,
    /// DESX encryption algorithm.
    Desx = wincrypt::CALG_DESX,
    /// Diffie-Hellman ephemeral key exchange algorithm.
    DhEphem = wincrypt::CALG_DH_EPHEM,
    /// Diffie-Hellman store and forward key exchange algorithm.
    DhSf = wincrypt::CALG_DH_SF,
    /// DSA public key signature algorithm.
    DssSign = wincrypt::CALG_DSS_SIGN,
    /// Elliptic curve Diffie-Hellman key exchange algorithm.
    Ecdh = wincrypt::CALG_ECDH,
    // https://github.com/retep998/wincrypt-rs/issues/287
    // /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
    // EcdhEphem = wincrypt::CALG_ECDH_EPHEM,
    /// Elliptic curve digital signature algorithm.
    Ecdsa = wincrypt::CALG_ECDSA,
    /// One way function hashing algorithm.
    HashReplaceOwf = wincrypt::CALG_HASH_REPLACE_OWF,
    /// Hughes MD5 hashing algorithm.
    HughesMd5 = wincrypt::CALG_HUGHES_MD5,
    /// HMAC keyed hash algorithm.
    Hmac = wincrypt::CALG_HMAC,
    /// MAC keyed hash algorithm.
    Mac = wincrypt::CALG_MAC,
    /// MD2 hashing algorithm.
    Md2 = wincrypt::CALG_MD2,
    /// MD4 hashing algorithm.
    Md4 = wincrypt::CALG_MD4,
    /// MD5 hashing algorithm.
    Md5 = wincrypt::CALG_MD5,
    /// No signature algorithm..
    NoSign = wincrypt::CALG_NO_SIGN,
    /// RC2 block encryption algorithm.
    Rc2 = wincrypt::CALG_RC2,
    /// RC4 stream encryption algorithm.
    Rc4 = wincrypt::CALG_RC4,
    /// RC5 block encryption algorithm.
    Rc5 = wincrypt::CALG_RC5,
    /// RSA public key exchange algorithm.
    RsaKeyx = wincrypt::CALG_RSA_KEYX,
    /// RSA public key signature algorithm.
    RsaSign = wincrypt::CALG_RSA_SIGN,
    /// SHA hashing algorithm.
    Sha1 = wincrypt::CALG_SHA1,
    /// 256 bit SHA hashing algorithm.
    Sha256 = wincrypt::CALG_SHA_256,
    /// 384 bit SHA hashing algorithm.
    Sha384 = wincrypt::CALG_SHA_384,
    /// 512 bit SHA hashing algorithm.
    Sha512 = wincrypt::CALG_SHA_512,
    /// Triple DES encryption algorithm.
    TripleDes = wincrypt::CALG_3DES,
    /// Two-key triple DES encryption with effective key length equal to 112 bits.
    TripleDes112 = wincrypt::CALG_3DES_112,
    #[doc(hidden)]
    __ForExtensibility,
}

/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
pub enum Protocol {
    /// Secure Sockets Layer 3.0
    Ssl3,
    /// Transport Layer Security 1.0
    Tls10,
    /// Transport Layer Security 1.1
    Tls11,
    /// Transport Layer Security 1.2
    Tls12,
    #[doc(hidden)]
    __ForExtensibility,
}

impl Protocol {
    fn dword(self, direction: Direction) -> winapi::DWORD {
        match (self, direction) {
            (Protocol::Ssl3, Direction::Inbound) => um::schannel::SP_PROT_SSL3_SERVER,
            (Protocol::Tls10, Direction::Inbound) => um::schannel::SP_PROT_TLS1_0_SERVER,
            (Protocol::Tls11, Direction::Inbound) => um::schannel::SP_PROT_TLS1_1_SERVER,
            (Protocol::Tls12, Direction::Inbound) => um::schannel::SP_PROT_TLS1_2_SERVER,
            (Protocol::Ssl3, Direction::Outbound) => um::schannel::SP_PROT_SSL3_CLIENT,
            (Protocol::Tls10, Direction::Outbound) => um::schannel::SP_PROT_TLS1_0_CLIENT,
            (Protocol::Tls11, Direction::Outbound) => um::schannel::SP_PROT_TLS1_1_CLIENT,
            (Protocol::Tls12, Direction::Outbound) => um::schannel::SP_PROT_TLS1_2_CLIENT,
            (Protocol::__ForExtensibility, _) => unreachable!(),
        }
    }
}

/// A builder type for `SchannelCred`s.
#[derive(Default, Debug)]
pub struct Builder {
    supported_algorithms: Option<Vec<Algorithm>>,
    enabled_protocols: Option<Vec<Protocol>>,
    certs: Vec<CertContext>,
}

impl Builder {
    /// Returns a new `Builder`.
    pub fn new() -> Builder {
        Builder::default()
    }

    /// Sets the algorithms supported for credentials created from this builder.
    pub fn supported_algorithms(&mut self,
                                supported_algorithms: &[Algorithm])
                                -> &mut Builder {
        assert!(supported_algorithms.iter()
            .all(|a| {
                match *a {
                    Algorithm::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.supported_algorithms = Some(supported_algorithms.to_owned());
        self
    }

    /// Sets the protocols enabled for credentials created from this builder.
    pub fn enabled_protocols(&mut self,
                             enabled_protocols: &[Protocol])
                             -> &mut Builder {
        assert!(enabled_protocols.iter()
            .all(|a| {
                match *a {
                    Protocol::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.enabled_protocols = Some(enabled_protocols.to_owned());
        self
    }

    /// Add a certificate to get passed down when the credentials are acquired.
    ///
    /// Certificates passed here may specify a certificate that contains a
    /// private key to be used in authenticating the application. Typically,
    /// this is called once for each key exchange method supported by
    /// servers.
    ///
    /// Clients often do not call this function and either depend on Schannel to
    /// find an appropriate certificate or create a certificate later if needed.
    pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
        self.certs.push(cx);
        self
    }

    /// Creates a new `SchannelCred`.
    pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
        unsafe {
            let mut handle = mem::zeroed();
            let mut cred_data: um::schannel::SCHANNEL_CRED = mem::zeroed();
            cred_data.dwVersion = um::schannel::SCHANNEL_CRED_VERSION;
            cred_data.dwFlags = um::schannel::SCH_USE_STRONG_CRYPTO | um::schannel::SCH_CRED_NO_DEFAULT_CREDS;
            if let Some(ref supported_algorithms) = self.supported_algorithms {
                cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD;
                cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
            }
            if let Some(ref enabled_protocols) = self.enabled_protocols {
                cred_data.grbitEnabledProtocols = enabled_protocols.iter()
                    .map(|p| p.dword(direction))
                    .fold(0, |acc, p| acc | p);
            }
            let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
            cred_data.cCreds = certs.len() as winapi::DWORD;
            cred_data.paCred = certs.as_mut_ptr();

            let direction = match direction {
                Direction::Inbound => sspi::SECPKG_CRED_INBOUND,
                Direction::Outbound => sspi::SECPKG_CRED_OUTBOUND,
            };

            match sspi::AcquireCredentialsHandleA(ptr::null_mut(),
                                                  UNISP_NAME.as_ptr() as *const _ as *mut _,
                                                  direction,
                                                  ptr::null_mut(),
                                                  &mut cred_data as *mut _ as *mut _,
                                                  None,
                                                  ptr::null_mut(),
                                                  &mut handle,
                                                  ptr::null_mut()) {
                winerror::SEC_E_OK => Ok(SchannelCred::from_inner(handle)),
                err => Err(io::Error::from_raw_os_error(err as i32)),
            }
        }
    }
}

/// An SChannel credential.
#[derive(Clone)]
pub struct SchannelCred(Arc<RawCredHandle>);

struct RawCredHandle(sspi::CredHandle);

impl Drop for RawCredHandle {
    fn drop(&mut self) {
        unsafe {
            sspi::FreeCredentialsHandle(&mut self.0);
        }
    }
}

impl SchannelCred {
    /// Returns a builder.
    pub fn builder() -> Builder {
        Builder::new()
    }

    unsafe fn from_inner(inner: sspi::CredHandle) -> SchannelCred {
        SchannelCred(Arc::new(RawCredHandle(inner)))
    }

    pub(crate) fn as_inner(&self) -> sspi::CredHandle {
        self.0.as_ref().0
    }
}
<commit_msg>Add EcdhEphem to the list of algorithms<commit_after>//! Schannel credentials.
use winapi::shared::{sspi, winerror};
use winapi::shared::minwindef as winapi;
use winapi::um::{self, wincrypt};
use std::io;
use std::mem;
use std::ptr;
use std::sync::Arc;

use crate::Inner;
use crate::cert_context::CertContext;

lazy_static! {
    static ref UNISP_NAME: Vec<u8> = um::schannel::UNISP_NAME.bytes().chain(Some(0)).collect();
}

/// The communication direction that an `SchannelCred` will support.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum Direction {
    /// Server-side, inbound connections.
    Inbound,
    /// Client-side, outbound connections.
    Outbound,
}

/// Algorithms supported by Schannel.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum Algorithm {
    /// Advanced Encryption Standard (AES).
    Aes = wincrypt::CALG_AES,
    /// 128 bit AES.
    Aes128 = wincrypt::CALG_AES_128,
    /// 192 bit AES.
    Aes192 = wincrypt::CALG_AES_192,
    /// 256 bit AES.
    Aes256 = wincrypt::CALG_AES_256,
    /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
    AgreedkeyAny = wincrypt::CALG_AGREEDKEY_ANY,
    /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
    /// its key length 64 bits.
    CylinkMek = wincrypt::CALG_CYLINK_MEK,
    /// DES encryption algorithm.
    Des = wincrypt::CALG_DES,
    /// DESX encryption algorithm.
    Desx = wincrypt::CALG_DESX,
    /// Diffie-Hellman ephemeral key exchange algorithm.
    DhEphem = wincrypt::CALG_DH_EPHEM,
    /// Diffie-Hellman store and forward key exchange algorithm.
    DhSf = wincrypt::CALG_DH_SF,
    /// DSA public key signature algorithm.
    DssSign = wincrypt::CALG_DSS_SIGN,
    /// Elliptic curve Diffie-Hellman key exchange algorithm.
    Ecdh = wincrypt::CALG_ECDH,
    /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
    EcdhEphem = wincrypt::CALG_ECDH_EPHEM,
    /// Elliptic curve digital signature algorithm.
    Ecdsa = wincrypt::CALG_ECDSA,
    /// One way function hashing algorithm.
    HashReplaceOwf = wincrypt::CALG_HASH_REPLACE_OWF,
    /// Hughes MD5 hashing algorithm.
    HughesMd5 = wincrypt::CALG_HUGHES_MD5,
    /// HMAC keyed hash algorithm.
    Hmac = wincrypt::CALG_HMAC,
    /// MAC keyed hash algorithm.
    Mac = wincrypt::CALG_MAC,
    /// MD2 hashing algorithm.
    Md2 = wincrypt::CALG_MD2,
    /// MD4 hashing algorithm.
    Md4 = wincrypt::CALG_MD4,
    /// MD5 hashing algorithm.
    Md5 = wincrypt::CALG_MD5,
    /// No signature algorithm..
    NoSign = wincrypt::CALG_NO_SIGN,
    /// RC2 block encryption algorithm.
    Rc2 = wincrypt::CALG_RC2,
    /// RC4 stream encryption algorithm.
    Rc4 = wincrypt::CALG_RC4,
    /// RC5 block encryption algorithm.
    Rc5 = wincrypt::CALG_RC5,
    /// RSA public key exchange algorithm.
    RsaKeyx = wincrypt::CALG_RSA_KEYX,
    /// RSA public key signature algorithm.
    RsaSign = wincrypt::CALG_RSA_SIGN,
    /// SHA hashing algorithm.
    Sha1 = wincrypt::CALG_SHA1,
    /// 256 bit SHA hashing algorithm.
    Sha256 = wincrypt::CALG_SHA_256,
    /// 384 bit SHA hashing algorithm.
    Sha384 = wincrypt::CALG_SHA_384,
    /// 512 bit SHA hashing algorithm.
    Sha512 = wincrypt::CALG_SHA_512,
    /// Triple DES encryption algorithm.
    TripleDes = wincrypt::CALG_3DES,
    /// Two-key triple DES encryption with effective key length equal to 112 bits.
    TripleDes112 = wincrypt::CALG_3DES_112,
    #[doc(hidden)]
    __ForExtensibility,
}

/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
pub enum Protocol {
    /// Secure Sockets Layer 3.0
    Ssl3,
    /// Transport Layer Security 1.0
    Tls10,
    /// Transport Layer Security 1.1
    Tls11,
    /// Transport Layer Security 1.2
    Tls12,
    #[doc(hidden)]
    __ForExtensibility,
}

impl Protocol {
    fn dword(self, direction: Direction) -> winapi::DWORD {
        match (self, direction) {
            (Protocol::Ssl3, Direction::Inbound) => um::schannel::SP_PROT_SSL3_SERVER,
            (Protocol::Tls10, Direction::Inbound) => um::schannel::SP_PROT_TLS1_0_SERVER,
            (Protocol::Tls11, Direction::Inbound) => um::schannel::SP_PROT_TLS1_1_SERVER,
            (Protocol::Tls12, Direction::Inbound) => um::schannel::SP_PROT_TLS1_2_SERVER,
            (Protocol::Ssl3, Direction::Outbound) => um::schannel::SP_PROT_SSL3_CLIENT,
            (Protocol::Tls10, Direction::Outbound) => um::schannel::SP_PROT_TLS1_0_CLIENT,
            (Protocol::Tls11, Direction::Outbound) => um::schannel::SP_PROT_TLS1_1_CLIENT,
            (Protocol::Tls12, Direction::Outbound) => um::schannel::SP_PROT_TLS1_2_CLIENT,
            (Protocol::__ForExtensibility, _) => unreachable!(),
        }
    }
}

/// A builder type for `SchannelCred`s.
#[derive(Default, Debug)]
pub struct Builder {
    supported_algorithms: Option<Vec<Algorithm>>,
    enabled_protocols: Option<Vec<Protocol>>,
    certs: Vec<CertContext>,
}

impl Builder {
    /// Returns a new `Builder`.
    pub fn new() -> Builder {
        Builder::default()
    }

    /// Sets the algorithms supported for credentials created from this builder.
    pub fn supported_algorithms(&mut self,
                                supported_algorithms: &[Algorithm])
                                -> &mut Builder {
        assert!(supported_algorithms.iter()
            .all(|a| {
                match *a {
                    Algorithm::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.supported_algorithms = Some(supported_algorithms.to_owned());
        self
    }

    /// Sets the protocols enabled for credentials created from this builder.
    pub fn enabled_protocols(&mut self,
                             enabled_protocols: &[Protocol])
                             -> &mut Builder {
        assert!(enabled_protocols.iter()
            .all(|a| {
                match *a {
                    Protocol::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.enabled_protocols = Some(enabled_protocols.to_owned());
        self
    }

    /// Add a certificate to get passed down when the credentials are acquired.
    ///
    /// Certificates passed here may specify a certificate that contains a
    /// private key to be used in authenticating the application. Typically,
    /// this is called once for each key exchange method supported by
    /// servers.
    ///
    /// Clients often do not call this function and either depend on Schannel to
    /// find an appropriate certificate or create a certificate later if needed.
    pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
        self.certs.push(cx);
        self
    }

    /// Creates a new `SchannelCred`.
    pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
        unsafe {
            let mut handle = mem::zeroed();
            let mut cred_data: um::schannel::SCHANNEL_CRED = mem::zeroed();
            cred_data.dwVersion = um::schannel::SCHANNEL_CRED_VERSION;
            cred_data.dwFlags = um::schannel::SCH_USE_STRONG_CRYPTO | um::schannel::SCH_CRED_NO_DEFAULT_CREDS;
            if let Some(ref supported_algorithms) = self.supported_algorithms {
                cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD;
                cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
            }
            if let Some(ref enabled_protocols) = self.enabled_protocols {
                cred_data.grbitEnabledProtocols = enabled_protocols.iter()
                    .map(|p| p.dword(direction))
                    .fold(0, |acc, p| acc | p);
            }
            let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
            cred_data.cCreds = certs.len() as winapi::DWORD;
            cred_data.paCred = certs.as_mut_ptr();

            let direction = match direction {
                Direction::Inbound => sspi::SECPKG_CRED_INBOUND,
                Direction::Outbound => sspi::SECPKG_CRED_OUTBOUND,
            };

            match sspi::AcquireCredentialsHandleA(ptr::null_mut(),
                                                  UNISP_NAME.as_ptr() as *const _ as *mut _,
                                                  direction,
                                                  ptr::null_mut(),
                                                  &mut cred_data as *mut _ as *mut _,
                                                  None,
                                                  ptr::null_mut(),
                                                  &mut handle,
                                                  ptr::null_mut()) {
                winerror::SEC_E_OK => Ok(SchannelCred::from_inner(handle)),
                err => Err(io::Error::from_raw_os_error(err as i32)),
            }
        }
    }
}

/// An SChannel credential.
#[derive(Clone)]
pub struct SchannelCred(Arc<RawCredHandle>);

struct RawCredHandle(sspi::CredHandle);

impl Drop for RawCredHandle {
    fn drop(&mut self) {
        unsafe {
            sspi::FreeCredentialsHandle(&mut self.0);
        }
    }
}

impl SchannelCred {
    /// Returns a builder.
    pub fn builder() -> Builder {
        Builder::new()
    }

    unsafe fn from_inner(inner: sspi::CredHandle) -> SchannelCred {
        SchannelCred(Arc::new(RawCredHandle(inner)))
    }

    pub(crate) fn as_inner(&self) -> sspi::CredHandle {
        self.0.as_ref().0
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_id="http://olsonjeffery.github.io#schemers:0.0.1"]
#![desc = "Simple Scheme Interpreter"]
#![license = "3-Clause BSD"]

use std::from_str::FromStr;

// this is starting out as a straight port of Peter Norvig's
// lis.py (the first iteration) to Rust

#[cfg(not(test))]
fn main() {
    use std::os::args;
    let args: Vec<~str> = args().move_iter().skip(1).collect();
    if args.len() == 0 {
        fail!("no program provided");
    }
    let program: ~str = args.move_iter()
        .fold(~"", |memo, arg| memo + arg + " ").trim().to_owned();
    let tokens = &mut tokenize(program);
    println!("{:?}", read(tokens));
}

// parser impl
fn pad_input(input: ~str) -> ~str {
    input.replace("(", " ( ").replace(")", " ) ")
}

fn tokenize(input: ~str) -> Vec<~str> {
    pad_input(input).split_str(" ")
        .filter(|i| *i != "").map(|i| i.to_owned()).collect()
}

#[deriving(Eq, Show)]
pub enum ParseItem {
    Atom(AtomType),
    List(Vec<~ParseItem>)
}
impl ParseItem {
    pub fn new_atom(input: ~str) -> ParseItem {
        let first_char = input.char_at(0);
        match first_char {
            '0' | '1' | '2' | '3' | '4' | '5' |
            '6' | '7' | '8' | '9' => {
                let val = FromStr::from_str(input);
                match val {
                    Some(val) => Atom(Integer(val)),
                    None => fail!("Cannot parse number-like input of: {}", input)
                }
            },
            _ => Atom(Symbol(input))
        }
    }
}

#[deriving(Eq, Show)]
pub enum AtomType {
    Symbol(~str),
    Integer(int)
}

impl ParseItem {
    pub fn is_atom(&self) -> bool {
        match *self {
            Atom(_) => true,
            _ => false
        }
    }
}

fn read(tokens: &mut Vec<~str>) -> ParseItem {
    let current_token =
        tokens.shift().expect("calling read() w/ empty token list; shouldn't happen");
    match current_token {
        ref x if *x == ~"(" => {
            let mut list = Vec::new();
            while *tokens.get(0) != ~")" {
                list.push(box read(tokens));
            }
            tokens.shift();
            List(list)
        },
        ref x if *x == ~")" => fail!("hit ) token; shouldn't happen"),
        x => ParseItem::new_atom(x)
    }
}

#[cfg(test)]
mod parser_test {
    use super::{pad_input, tokenize, read, Atom, List, Symbol, Integer};

    #[test]
    fn pad_input_should_insert_spaces_before_and_after_parens() {
        assert_eq!(pad_input(~"(x 1 2 3)"), ~" ( x 1 2 3 ) ");
    }

    #[test]
    fn tokenize_string_into_tokens() {
        let tokens = tokenize(~"(foo 12 3)");
        assert_eq!(tokens.len(), 5);
        assert_eq!(tokens.get(0).to_owned(), ~"(");
        assert_eq!(tokens.get(1).to_owned(), ~"foo");
        assert_eq!(tokens.get(2).to_owned(), ~"12");
        assert_eq!(tokens.get(3).to_owned(), ~"3");
        assert_eq!(tokens.get(4).to_owned(), ~")");
    }

    #[test]
    fn should_parse_tokens_consisting_of_a_str_atom_and_convert_it_to_a_parse_item() {
        let tokens = &mut tokenize(~"bar");
        let parsed_item = read(tokens);
        println!("{:?}", parsed_item);
        match parsed_item {
            Atom(Symbol(val)) => {
                assert_eq!(val, ~"bar")
            },
            _ => assert!(false)
        }
    }
    #[test]
    fn should_parse_tokens_consisting_of_an_int_atom_and_convert_it_to_a_parse_item() {
        let tokens = &mut tokenize(~"42");
        let parsed_item = read(tokens);
        println!("{:?}", parsed_item);
        match parsed_item {
            Atom(Integer(val)) => {
                assert_eq!(val, 42)
            },
            _ => assert!(false)
        }
    }

    #[test]
    fn should_parse_tokens_consisting_of_a_list_of_atoms_and_parse_it() {
        let tokens = &mut tokenize(~"(bar 12 45)");
        let parsed_item = read(tokens);
        assert_eq!(parsed_item.is_atom(), false);
        match parsed_item {
            List(items) => {
                let items_len = items.len();
                assert!(3 == items_len);
                assert_eq!(items.get(0).is_atom(), true);
                assert_eq!(*items.get(0), ~Atom(Symbol(~"bar")));
                assert_eq!(items.get(1).is_atom(), true);
                assert_eq!(*items.get(1), ~Atom(Integer(12)));
                assert_eq!(items.get(2).is_atom(), true);
                assert_eq!(*items.get(2), ~Atom(Integer(45)));
            },
            _ => fail!("got back an atom, it seems")
        }
    }
    #[test]
    fn should_parse_tokens_consisting_of_a_list_of_atoms_and_a_nested_list() {
        let tokens = &mut tokenize(~"((2 3) bar 12 (hee (hah)))");
        let parsed_item = read(tokens);
        assert_eq!(parsed_item.is_atom(), false);
        match parsed_item {
            List(items) => {
                let items_len = items.len();
                assert!(4 == items_len);
                match *items.get(0) {
                    ~List(ref items) => {
                        assert_eq!(items.len(), 2);
                        assert_eq!(*items.get(0), ~Atom(Integer(2)));
                        assert_eq!(*items.get(1), ~Atom(Integer(3)));
                    },
                    _ => fail!("shoulda got a list")
                }
                assert_eq!(*items.get(1), ~Atom(Symbol(~"bar")));
                assert_eq!(*items.get(2), ~Atom(Integer(12)));
                match *items.get(3) {
                    ~List(ref items) => {
                        assert_eq!(items.len(), 2);
                        assert_eq!(*items.get(0), ~Atom(Symbol(~"hee")));
                        match *items.get(1) {
                            ~List(ref items) => {
                                assert_eq!(items.len(), 1);
                                assert_eq!(*items.get(0), ~Atom(Symbol(~"hah")));
                            },
                            _ => fail!("shoulda got a list")
                        }
                    },
                    _ => fail!("shoulda got a list")
                }
            },
            _ => fail!("got back an atom, it seems")
        }
    }
}
<commit_msg>rename read() to parse()<commit_after>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_id="http://olsonjeffery.github.io#schemers:0.0.1"]
#![desc = "Simple Scheme Interpreter"]
#![license = "3-Clause BSD"]

use std::from_str::FromStr;

// this is starting out as a straight port of Peter Norvig's
// lis.py (the first iteration) to Rust

#[cfg(not(test))]
fn main() {
    use std::os::args;
    let args: Vec<~str> = args().move_iter().skip(1).collect();
    if args.len() == 0 {
        fail!("no program provided");
    }
    let program: ~str = args.move_iter()
        .fold(~"", |memo, arg| memo + arg + " ").trim().to_owned();
    let tokens = &mut tokenize(program);
    println!("{:?}", parse(tokens));
}

// parser impl
fn pad_input(input: ~str) -> ~str {
    input.replace("(", " ( ").replace(")", " ) ")
}

fn tokenize(input: ~str) -> Vec<~str> {
    pad_input(input).split_str(" ")
        .filter(|i| *i != "").map(|i| i.to_owned()).collect()
}

#[deriving(Eq, Show)]
pub enum ParseItem {
    Atom(AtomType),
    List(Vec<~ParseItem>)
}
impl ParseItem {
    pub fn new_atom(input: ~str) -> ParseItem {
        let first_char = input.char_at(0);
        match first_char {
            '0' | '1' | '2' | '3' | '4' | '5' |
            '6' | '7' | '8' | '9' => {
                let val = FromStr::from_str(input);
                match val {
                    Some(val) => Atom(Integer(val)),
                    None => fail!("Cannot parse number-like input of: {}", input)
                }
            },
            _ => Atom(Symbol(input))
        }
    }
}

#[deriving(Eq, Show)]
pub enum AtomType {
    Symbol(~str),
    Integer(int)
}

impl ParseItem {
    pub fn is_atom(&self) -> bool {
        match *self {
            Atom(_) => true,
            _ => false
        }
    }
}

fn parse(tokens: &mut Vec<~str>) -> ParseItem {
    let current_token =
        tokens.shift().expect("calling parse() w/ empty token list; shouldn't happen");
    match current_token {
        ref x if *x == ~"(" => {
            let mut list = Vec::new();
            while *tokens.get(0) != ~")" {
                list.push(box parse(tokens));
            }
            tokens.shift();
            List(list)
        },
        ref x if *x == ~")" => fail!("hit ) token; shouldn't happen"),
        x => ParseItem::new_atom(x)
    }
}

#[cfg(test)]
mod parser_test {
    use super::{pad_input, tokenize, parse, Atom, List, Symbol, Integer};

    #[test]
    fn pad_input_should_insert_spaces_before_and_after_parens() {
        assert_eq!(pad_input(~"(x 1 2 3)"), ~" ( x 1 2 3 ) ");
    }

    #[test]
    fn tokenize_string_into_tokens() {
        let tokens = tokenize(~"(foo 12 3)");
        assert_eq!(tokens.len(), 5);
        assert_eq!(tokens.get(0).to_owned(), ~"(");
        assert_eq!(tokens.get(1).to_owned(), ~"foo");
        assert_eq!(tokens.get(2).to_owned(), ~"12");
        assert_eq!(tokens.get(3).to_owned(), ~"3");
        assert_eq!(tokens.get(4).to_owned(), ~")");
    }

    #[test]
    fn should_parse_tokens_consisting_of_a_str_atom_and_convert_it_to_a_parse_item() {
        let tokens = &mut tokenize(~"bar");
        let parsed_item = parse(tokens);
        println!("{:?}", parsed_item);
        match parsed_item {
            Atom(Symbol(val)) => {
                assert_eq!(val, ~"bar")
            },
            _ => assert!(false)
        }
    }
    #[test]
    fn should_parse_tokens_consisting_of_an_int_atom_and_convert_it_to_a_parse_item() {
        let tokens = &mut tokenize(~"42");
        let parsed_item = parse(tokens);
        println!("{:?}", parsed_item);
        match parsed_item {
            Atom(Integer(val)) => {
                assert_eq!(val, 42)
            },
            _ => assert!(false)
        }
    }

    #[test]
    fn should_parse_tokens_consisting_of_a_list_of_atoms_and_parse_it() {
        let tokens = &mut tokenize(~"(bar 12 45)");
        let parsed_item = parse(tokens);
        assert_eq!(parsed_item.is_atom(), false);
        match parsed_item {
            List(items) => {
                let items_len = items.len();
                assert!(3 == items_len);
                assert_eq!(items.get(0).is_atom(), true);
                assert_eq!(*items.get(0), ~Atom(Symbol(~"bar")));
                assert_eq!(items.get(1).is_atom(), true);
                assert_eq!(*items.get(1), ~Atom(Integer(12)));
                assert_eq!(items.get(2).is_atom(), true);
                assert_eq!(*items.get(2), ~Atom(Integer(45)));
            },
            _ => fail!("got back an atom, it seems")
        }
    }
    #[test]
    fn should_parse_tokens_consisting_of_a_list_of_atoms_and_a_nested_list() {
        let tokens = &mut tokenize(~"((2 3) bar 12 (hee (hah)))");
        let parsed_item = parse(tokens);
        assert_eq!(parsed_item.is_atom(), false);
        match parsed_item {
            List(items) => {
                let items_len = items.len();
                assert!(4 == items_len);
                match *items.get(0) {
                    ~List(ref items) => {
                        assert_eq!(items.len(), 2);
                        assert_eq!(*items.get(0), ~Atom(Integer(2)));
                        assert_eq!(*items.get(1), ~Atom(Integer(3)));
                    },
                    _ => fail!("shoulda got a list")
                }
                assert_eq!(*items.get(1), ~Atom(Symbol(~"bar")));
                assert_eq!(*items.get(2), ~Atom(Integer(12)));
                match *items.get(3) {
                    ~List(ref items) => {
                        assert_eq!(items.len(), 2);
                        assert_eq!(*items.get(0), ~Atom(Symbol(~"hee")));
                        match *items.get(1) {
                            ~List(ref items) => {
                                assert_eq!(items.len(), 1);
                                assert_eq!(*items.get(0), ~Atom(Symbol(~"hah")));
                            },
                            _ => fail!("shoulda got a list")
                        }
                    },
                    _ => fail!("shoulda got a list")
                }
            },
            _ => fail!("got back an atom, it seems")
        }
    }
}
<|endoftext|>


<commit_before>mod common;

use crate::common::service;

use snowchains::config;
use snowchains::errors::{ServiceError, ServiceErrorKind};
use snowchains::service::ServiceKind;
use snowchains::terminal::{AnsiColorChoice, Dumb, TtyOrPiped};
use snowchains::{Opt, OutputKind, Retrieve, RetrieveLanguages, RetrieveSubmissions, Submit};

use difference::assert_diff;
use failure::Fallible;
use if_chain::if_chain;
use once_cell::sync::Lazy;
use pretty_assertions::assert_eq;
use regex::Regex;

use std::convert::TryFrom as _;

#[test]
fn it_logins() -> Fallible<()> {
    fn login(ctx: snowchains::Context<TtyOrPiped<&[u8]>, Dumb, Dumb>) -> Fallible<()> {
        service::login(ctx, ServiceKind::Yukicoder).map_err(Into::into)
    }

    let stdin = format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?);
    service::test_in_tempdir("it_logins", &stdin, login)
}

#[test]
fn it_scrapes_and_downloads_testcases() -> Fallible<()> {
    service::test_in_tempdir(
        "it_downloads_test_cases_from_master",
        &format!("Y\n{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |ctx| -> _ {
            static CONTEST: &str = "no";
            let wd = ctx.cwd.clone();
            service::retrieve_testcases(
                ctx,
                ServiceKind::Yukicoder,
                CONTEST,
                &["3", "725", "726"],
            )?;
            service::confirm_num_cases(
                &wd,
                ServiceKind::Yukicoder,
                CONTEST,
                &[("3", 31), ("725", 9), ("726", 25)],
            )
        },
    )
}

#[test]
fn retrieve_submissions_command_works_without_error() -> Fallible<()> {
    service::test_in_tempdir(
        "retrieve_submissions_command_works_without_error",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            let opt = Opt::Retrieve(Retrieve::Submissions(RetrieveSubmissions {
                fetch_all: false,
                no_save: true,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                mode: config::Mode::Debug,
                problems: vec!["1".to_owned()],
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
            }));
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            let stderr = String::try_from(ctx.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            assert!(stderr.starts_with(
                &r#"
GET https://yukicoder.me/api/v1/problems ... 200 OK
GET https://yukicoder.me/api/v1/languages ... 200 OK
GET https://yukicoder.me/problems/no/1/submissions?my_submission=enabled ... 200 OK
"#[1..],
            ));
            Ok(())
        },
    )
}

#[test]
fn it_fails_to_submit_if_the_lang_name_is_invalid() -> Fallible<()> {
    service::test_in_tempdir(
        "it_fails_to_submit_if_the_lang_name_is_invalid",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static CODE: &[u8] = b"#";
            let dir = ctx.cwd.join("yukicoder").join("no").join("py");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.py"), CODE)?;
            let opt = Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("python3-with-invalid-lang-names".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            });
            let err = snowchains::run(opt, &mut ctx).unwrap_err();
            if_chain! {
                if let snowchains::Error::Service(ServiceError::Context(ctx)) = &err;
                if let ServiceErrorKind::NoSuchLang(lang_name) = ctx.get_context();
                then {
                    assert_eq!(lang_name, "invalid");
                    Ok(())
                } else {
                    Err(err.into())
                }
            }
        },
    )
}

#[test]
#[ignore]
fn it_submits_to_no_9000() -> Fallible<()> {
    service::test_in_tempdir(
        "it_submits_to_no_9000",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static CODE: &[u8] = b"Hello World!\n";
            let dir = ctx.cwd.join("yukicoder").join("no").join("txt");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.txt"), CODE)?;
            let opt = Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("text".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            });
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            String::try_from(ctx.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            Ok(())
        },
    )
}

#[test]
fn it_retrieves_languages() -> Fallible<()> {
    service::test_in_tempdir(
        "it_retrieves_languages",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static MASK_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new("Username: .*").unwrap());

            let opt = Opt::Retrieve(Retrieve::Languages(RetrieveLanguages {
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: Some("9000".to_owned()),
            }));
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            let stderr = String::try_from(ctx.stderr)?;
            assert_diff!(
                &r#"
┌──────────────────────────────────────────────┬─────────────┐
│ Name                                         │ ID          │
├──────────────────────────────────────────────┼─────────────┤
│ C++14 (gcc 9.3.0)                            │ cpp14       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(1z) (gcc 9.3.0)                        │ cpp17       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(clang Beta) (clang 7.0.0)              │ cpp-clang   │
├──────────────────────────────────────────────┼─────────────┤
│ C++11 (gcc 4.8.5)                            │ cpp         │
├──────────────────────────────────────────────┼─────────────┤
│ C (gcc 9.3.0)                                │ c11         │
├──────────────────────────────────────────────┼─────────────┤
│ C90 (gcc 4.8.5)                              │ c           │
├──────────────────────────────────────────────┼─────────────┤
│ Java11 (openjdk 11.0.6)                      │ java8       │
├──────────────────────────────────────────────┼─────────────┤
│ C# (csc 3.5.0-beta1-19606-04)                │ csharp      │
├──────────────────────────────────────────────┼─────────────┤
│ C#(mono) (mono 6.8.0.105)                    │ csharp_mono │
├──────────────────────────────────────────────┼─────────────┤
│ Perl (5.16.3)                                │ perl        │
├──────────────────────────────────────────────┼─────────────┤
│ Perl6 (rakudo 2019.11-303-g5c65a12)          │ perl6       │
├──────────────────────────────────────────────┼─────────────┤
│ PHP (5.4.16)                                 │ php         │
├──────────────────────────────────────────────┼─────────────┤
│ PHP7 (7.4.1)                                 │ php7        │
├──────────────────────────────────────────────┼─────────────┤
│ Python2 (2.7.17)                             │ python      │
├──────────────────────────────────────────────┼─────────────┤
│ Python3 (3.8.2 + numpy 1.14.5 + scipy 1.1.0) │ python3     │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy2 (7.1.1)                                │ pypy2       │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy3 (7.0.0)                                │ pypy3       │
├──────────────────────────────────────────────┼─────────────┤
│ Ruby (2.7.0p0)                               │ ruby        │
├──────────────────────────────────────────────┼─────────────┤
│ D (dmd 2.091.0)                              │ d           │
├──────────────────────────────────────────────┼─────────────┤
│ Go (1.14)                                    │ go          │
├──────────────────────────────────────────────┼─────────────┤
│ Haskell (8.8.1)                              │ haskell     │
├──────────────────────────────────────────────┼─────────────┤
│ Scala(Beta) (2.13.1)                         │ scala       │
├──────────────────────────────────────────────┼─────────────┤
│ Nim (1.0.6)                                  │ nim         │
├──────────────────────────────────────────────┼─────────────┤
│ Rust (1.42.0)                                │ rust        │
├──────────────────────────────────────────────┼─────────────┤
│ Kotlin (1.3.70)                              │ kotlin      │
├──────────────────────────────────────────────┼─────────────┤
│ Scheme (Gauche-0.9.9)                        │ scheme      │
├──────────────────────────────────────────────┼─────────────┤
│ Crystal (0.33.0)                             │ crystal     │
├──────────────────────────────────────────────┼─────────────┤
│ Swift(Beta) (5.1.3)                          │ swift       │
├──────────────────────────────────────────────┼─────────────┤
│ OCaml (4.05.0)                               │ ocaml       │
├──────────────────────────────────────────────┼─────────────┤
│ Clojure(Beta) (1.10.1.469)                   │ clojure     │
├──────────────────────────────────────────────┼─────────────┤
│ F# (4.5)                                     │ fsharp      │
├──────────────────────────────────────────────┼─────────────┤
│ Elixir (1.10.0-dev)                          │ elixir      │
├──────────────────────────────────────────────┼─────────────┤
│ Lua (LuaJit 2.0.5)                           │ lua         │
├──────────────────────────────────────────────┼─────────────┤
│ Fortran (gFortran 4.8.5)                     │ fortran     │
├──────────────────────────────────────────────┼─────────────┤
│ JavaScript (node v13.5.0)                    │ node        │
├──────────────────────────────────────────────┼─────────────┤
│ Common Lisp (sbcl 1.5.5)                     │ lisp        │
├──────────────────────────────────────────────┼─────────────┤
│ Kuin(beta) (v.2019.9.17 on Wine)             │ kuin        │
├──────────────────────────────────────────────┼─────────────┤
│ KuinKuin(beta) (KuinInKuin)                  │ kuininkuin  │
├──────────────────────────────────────────────┼─────────────┤
│ Vim script (v8.1.127)                        │ vim         │
├──────────────────────────────────────────────┼─────────────┤
│ Bash (Bash 4.2.46)                           │ sh          │
├──────────────────────────────────────────────┼─────────────┤
│ Text (cat 8.22)                              │ text        │
├──────────────────────────────────────────────┼─────────────┤
│ Assembler (nasm 2.10.07)                     │ nasm        │
├──────────────────────────────────────────────┼─────────────┤
│ cLay (20200317-1)                            │ clay        │
├──────────────────────────────────────────────┼─────────────┤
│ Brainfuck (BFI 1.1)                          │ bf          │
├──────────────────────────────────────────────┼─────────────┤
│ Whitespace (0.3)                             │ Whitespace  │
└──────────────────────────────────────────────┴─────────────┘
"#[1..],
                &stdout,
                "\n",
                0
            );
            assert_diff!(
                &MASK_USERNAME.replace(&stderr, "Username: ██████████"),
                &r#"
GET https://yukicoder.me/ ... 200 OK

Input "REVEL_SESSION".

Chrome: chrome://settings/cookies/detail?site=yukicoder.me&search=cookie
Firefox: sqlite3 "$YOUR_FIREFOX_PROFILE/cookies.sqlite" 'SELECT value FROM moz_cookies WHERE baseDomain="yukicoder.me" AND name="REVEL_SESSION"'

REVEL_SESSION: GET https://yukicoder.me/ ... 200 OK
Confirmed.
Username: ██████████
GET https://yukicoder.me/problems/no/9000/submit ... 200 OK

"#[1..],
                "\n",
                0
            );
            Ok(())
        },
    )
}
<commit_msg>Update the expected number of tests for `yukicoder.me/problems/no/3`<commit_after>mod common;

use crate::common::service;

use snowchains::config;
use snowchains::errors::{ServiceError, ServiceErrorKind};
use snowchains::service::ServiceKind;
use snowchains::terminal::{AnsiColorChoice, Dumb, TtyOrPiped};
use snowchains::{Opt, OutputKind, Retrieve, RetrieveLanguages, RetrieveSubmissions, Submit};

use difference::assert_diff;
use failure::Fallible;
use if_chain::if_chain;
use once_cell::sync::Lazy;
use pretty_assertions::assert_eq;
use regex::Regex;

use std::convert::TryFrom as _;

#[test]
fn it_logins() -> Fallible<()> {
    fn login(ctx: snowchains::Context<TtyOrPiped<&[u8]>, Dumb, Dumb>) -> Fallible<()> {
        service::login(ctx, ServiceKind::Yukicoder).map_err(Into::into)
    }

    let stdin = format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?);
    service::test_in_tempdir("it_logins", &stdin, login)
}

#[test]
fn it_scrapes_and_downloads_testcases() -> Fallible<()> {
    service::test_in_tempdir(
        "it_downloads_test_cases_from_master",
        &format!("Y\n{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |ctx| -> _ {
            static CONTEST: &str = "no";
            let wd = ctx.cwd.clone();
            service::retrieve_testcases(
                ctx,
                ServiceKind::Yukicoder,
                CONTEST,
                &["3", "725", "726"],
            )?;
            service::confirm_num_cases(
                &wd,
                ServiceKind::Yukicoder,
                CONTEST,
                &[("3", 32), ("725", 9), ("726", 25)],
            )
        },
    )
}

#[test]
fn retrieve_submissions_command_works_without_error() -> Fallible<()> {
    service::test_in_tempdir(
        "retrieve_submissions_command_works_without_error",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            let opt = Opt::Retrieve(Retrieve::Submissions(RetrieveSubmissions {
                fetch_all: false,
                no_save: true,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                mode: config::Mode::Debug,
                problems: vec!["1".to_owned()],
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
            }));
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            let stderr = String::try_from(ctx.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            assert!(stderr.starts_with(
                &r#"
GET https://yukicoder.me/api/v1/problems ... 200 OK
GET https://yukicoder.me/api/v1/languages ... 200 OK
GET https://yukicoder.me/problems/no/1/submissions?my_submission=enabled ... 200 OK
"#[1..],
            ));
            Ok(())
        },
    )
}

#[test]
fn it_fails_to_submit_if_the_lang_name_is_invalid() -> Fallible<()> {
    service::test_in_tempdir(
        "it_fails_to_submit_if_the_lang_name_is_invalid",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static CODE: &[u8] = b"#";
            let dir = ctx.cwd.join("yukicoder").join("no").join("py");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.py"), CODE)?;
            let opt = Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("python3-with-invalid-lang-names".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            });
            let err = snowchains::run(opt, &mut ctx).unwrap_err();
            if_chain! {
                if let snowchains::Error::Service(ServiceError::Context(ctx)) = &err;
                if let ServiceErrorKind::NoSuchLang(lang_name) = ctx.get_context();
                then {
                    assert_eq!(lang_name, "invalid");
                    Ok(())
                } else {
                    Err(err.into())
                }
            }
        },
    )
}

#[test]
#[ignore]
fn it_submits_to_no_9000() -> Fallible<()> {
    service::test_in_tempdir(
        "it_submits_to_no_9000",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static CODE: &[u8] = b"Hello World!\n";
            let dir = ctx.cwd.join("yukicoder").join("no").join("txt");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.txt"), CODE)?;
            let opt = Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("text".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            });
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            String::try_from(ctx.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            Ok(())
        },
    )
}

#[test]
fn it_retrieves_languages() -> Fallible<()> {
    service::test_in_tempdir(
        "it_retrieves_languages",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut ctx| -> _ {
            static MASK_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new("Username: .*").unwrap());

            let opt = Opt::Retrieve(Retrieve::Languages(RetrieveLanguages {
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: Some("9000".to_owned()),
            }));
            let code = snowchains::run(opt, &mut ctx)?;
            assert_eq!(code, 0);
            let stdout = String::try_from(ctx.stdout)?;
            let stderr = String::try_from(ctx.stderr)?;
            assert_diff!(
                &r#"
┌──────────────────────────────────────────────┬─────────────┐
│ Name                                         │ ID          │
├──────────────────────────────────────────────┼─────────────┤
│ C++14 (gcc 9.3.0)                            │ cpp14       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(1z) (gcc 9.3.0)                        │ cpp17       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(clang Beta) (clang 7.0.0)              │ cpp-clang   │
├──────────────────────────────────────────────┼─────────────┤
│ C++11 (gcc 4.8.5)                            │ cpp         │
├──────────────────────────────────────────────┼─────────────┤
│ C (gcc 9.3.0)                                │ c11         │
├──────────────────────────────────────────────┼─────────────┤
│ C90 (gcc 4.8.5)                              │ c           │
├──────────────────────────────────────────────┼─────────────┤
│ Java11 (openjdk 11.0.6)                      │ java8       │
├──────────────────────────────────────────────┼─────────────┤
│ C# (csc 3.5.0-beta1-19606-04)                │ csharp      │
├──────────────────────────────────────────────┼─────────────┤
│ C#(mono) (mono 6.8.0.105)                    │ csharp_mono │
├──────────────────────────────────────────────┼─────────────┤
│ Perl (5.16.3)                                │ perl        │
├──────────────────────────────────────────────┼─────────────┤
│ Perl6 (rakudo 2019.11-303-g5c65a12)          │ perl6       │
├──────────────────────────────────────────────┼─────────────┤
│ PHP (5.4.16)                                 │ php         │
├──────────────────────────────────────────────┼─────────────┤
│ PHP7 (7.4.1)                                 │ php7        │
├──────────────────────────────────────────────┼─────────────┤
│ Python2 (2.7.17)                             │ python      │
├──────────────────────────────────────────────┼─────────────┤
│ Python3 (3.8.2 + numpy 1.14.5 + scipy 1.1.0) │ python3     │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy2 (7.1.1)                                │ pypy2       │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy3 (7.0.0)                                │ pypy3       │
├──────────────────────────────────────────────┼─────────────┤
│ Ruby (2.7.0p0)                               │ ruby        │
├──────────────────────────────────────────────┼─────────────┤
│ D (dmd 2.091.0)                              │ d           │
├──────────────────────────────────────────────┼─────────────┤
│ Go (1.14)                                    │ go          │
├──────────────────────────────────────────────┼─────────────┤
│ Haskell (8.8.1)                              │ haskell     │
├──────────────────────────────────────────────┼─────────────┤
│ Scala(Beta) (2.13.1)                         │ scala       │
├──────────────────────────────────────────────┼─────────────┤
│ Nim (1.0.6)                                  │ nim         │
├──────────────────────────────────────────────┼─────────────┤
│ Rust (1.42.0)                                │ rust        │
├──────────────────────────────────────────────┼─────────────┤
│ Kotlin (1.3.70)                              │ kotlin      │
├──────────────────────────────────────────────┼─────────────┤
│ Scheme (Gauche-0.9.9)                        │ scheme      │
├──────────────────────────────────────────────┼─────────────┤
│ Crystal (0.33.0)                             │ crystal     │
├──────────────────────────────────────────────┼─────────────┤
│ Swift(Beta) (5.1.3)                          │ swift       │
├──────────────────────────────────────────────┼─────────────┤
│ OCaml (4.05.0)                               │ ocaml       │
├──────────────────────────────────────────────┼─────────────┤
│ Clojure(Beta) (1.10.1.469)                   │ clojure     │
├──────────────────────────────────────────────┼─────────────┤
│ F# (4.5)                                     │ fsharp      │
├──────────────────────────────────────────────┼─────────────┤
│ Elixir (1.10.0-dev)                          │ elixir      │
├──────────────────────────────────────────────┼─────────────┤
│ Lua (LuaJit 2.0.5)                           │ lua         │
├──────────────────────────────────────────────┼─────────────┤
│ Fortran (gFortran 4.8.5)                     │ fortran     │
├──────────────────────────────────────────────┼─────────────┤
│ JavaScript (node v13.5.0)                    │ node        │
├──────────────────────────────────────────────┼─────────────┤
│ Common Lisp (sbcl 1.5.5)                     │ lisp        │
├──────────────────────────────────────────────┼─────────────┤
│ Kuin(beta) (v.2019.9.17 on Wine)             │ kuin        │
├──────────────────────────────────────────────┼─────────────┤
│ KuinKuin(beta) (KuinInKuin)                  │ kuininkuin  │
├──────────────────────────────────────────────┼─────────────┤
│ Vim script (v8.1.127)                        │ vim         │
├──────────────────────────────────────────────┼─────────────┤
│ Bash (Bash 4.2.46)                           │ sh          │
├──────────────────────────────────────────────┼─────────────┤
│ Text (cat 8.22)                              │ text        │
├──────────────────────────────────────────────┼─────────────┤
│ Assembler (nasm 2.10.07)                     │ nasm        │
├──────────────────────────────────────────────┼─────────────┤
│ cLay (20200317-1)                            │ clay        │
├──────────────────────────────────────────────┼─────────────┤
│ Brainfuck (BFI 1.1)                          │ bf          │
├──────────────────────────────────────────────┼─────────────┤
│ Whitespace (0.3)                             │ Whitespace  │
└──────────────────────────────────────────────┴─────────────┘
"#[1..],
                &stdout,
                "\n",
                0
            );
            assert_diff!(
                &MASK_USERNAME.replace(&stderr, "Username: ██████████"),
                &r#"
GET https://yukicoder.me/ ... 200 OK

Input "REVEL_SESSION".

Chrome: chrome://settings/cookies/detail?site=yukicoder.me&search=cookie
Firefox: sqlite3 "$YOUR_FIREFOX_PROFILE/cookies.sqlite" 'SELECT value FROM moz_cookies WHERE baseDomain="yukicoder.me" AND name="REVEL_SESSION"'

REVEL_SESSION: GET https://yukicoder.me/ ... 200 OK
Confirmed.
Username: ██████████
GET https://yukicoder.me/problems/no/9000/submit ... 200 OK

"#[1..],
                "\n",
                0
            );
            Ok(())
        },
    )
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate rust_base58;
extern crate serde_json;

use self::libc::c_int;
use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::collections::HashMap;
use std::{fmt, fs, thread};
use std::fmt::Debug;
use std::io::Write;
use rustc_serialize::json;
use zmq;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                let mut socks_to_poll: [zmq::PollItem; 1] = [
                    recv_cmd_sock.as_poll_item(zmq::POLLIN),
                ];
                CommandExecutor::instance().send(Command::Pool(
                    PoolCommand::OpenAck(cmd_id, Ok(pool_id)))); //TODO send only after catch-up?
                loop {
                    trace!("zmq poll loop >>");
                    let r = zmq::poll(&mut socks_to_poll, -1);
                    //FIXME implement
                    trace!("zmq poll loop << ret {:?}, at cmd sock {:?}", r, recv_cmd_sock.recv_string(0));
                }
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0); //TODO
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct NodeData {
    alias: String,
    client_ip: String,
    client_port: u32,
    node_ip: String,
    node_port: u32,
    services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct GenTransaction {
    data: NodeData,
    dest: String,
    identifier: String,
    #[serde(rename = "txnId")]
    txn_id: String,
    #[serde(rename = "type")]
    txn_type: String,
}

struct RemoteNode {
    public_key: Vec<u8>,
    verify_key: Vec<u8>,
    zaddr: String,
    zsock: Option<zmq::Socket>,
}

impl Debug for RemoteNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RemoteNode: {{ public_key: {:?}, verify_key {:?}, zaddr {:?}, zsock is_some {} }}",
               self.public_key, self.verify_key, self.zaddr, self.zsock.is_some())
    }
}

impl RemoteNode {
    fn new(txn: &str) -> RemoteNode {
        let gen_tx: GenTransaction = serde_json::from_str(txn).expect("RemoteNode parsing");
        RemoteNode::from(gen_tx)
    }

    fn connect(&mut self, ctx: &zmq::Context) {
        let key_pair = zmq::CurveKeyPair::new().expect("create key pair");
        let s = ctx.socket(zmq::SocketType::DEALER).expect("socket for Node");
        s.set_curve_secretkey(key_pair.secret_key.as_str()).expect("set secret key");
        s.set_curve_publickey(key_pair.public_key.as_str()).expect("set public key");
        s.set_curve_serverkey(zmq::z85_encode(self.verify_key.as_slice()).unwrap().as_str()).expect("set verify key");
        s.connect(self.zaddr.as_str()).expect("connect to Node");
        self.zsock = Some(s);
    }
}

impl From<GenTransaction> for RemoteNode {
    fn from(tx: GenTransaction) -> RemoteNode {
        fn crypto_sign_ed25519_pk_to_curve25519(pk: &Vec<u8>) -> Vec<u8> {
            // TODO: fix hack:
            // this function isn't included to sodiumoxide rust wrappers,
            // temporary local binding is used to call libsodium-sys function
            extern {
                pub fn crypto_sign_ed25519_pk_to_curve25519(
                    curve25519_pk: *mut [u8; 32],
                    ed25519_pk: *const [u8; 32]) -> c_int;
            }
            let mut from: [u8; 32] = [0; 32];
            from.clone_from_slice(pk.as_slice());
            let mut to: [u8; 32] = [0; 32];
            unsafe {
                crypto_sign_ed25519_pk_to_curve25519(&mut to, &from);
            }
            to.iter().cloned().collect()
        }

        let public_key = tx.dest.as_str().from_base58().expect("dest field in GenTransaction isn't valid");
        RemoteNode {
            verify_key: crypto_sign_ed25519_pk_to_curve25519(&public_key),
            public_key: public_key,
            zaddr: format!("tcp://{}:{}", tx.data.client_ip, tx.data.client_port),
            zsock: None,
        }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => json::decode(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path());

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(json::encode(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<commit_msg>Introduce PoolWorker, implement connection to Nodes from genesis transactions file.<commit_after>extern crate libc;
extern crate rust_base58;
extern crate serde_json;

use self::libc::c_int;
use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::collections::HashMap;
use std::{env, fmt, fs, io, path, thread};
use std::fmt::Debug;
use std::fs::Metadata;
use std::io::{BufRead, Error, ErrorKind, Write};
use std::path::{Path, PathBuf};
use rustc_serialize;
use rustc_serialize::json;
use zmq;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

struct PoolWorker {
    cmd_sock: zmq::Socket,
    open_cmd_id: i32,
    pool_id: i32,
    nodes: Vec<RemoteNode>,
}

impl PoolWorker {
    fn connect_to_known_nodes(&mut self) {
        let f = fs::File::open("pool_transactions_sandbox").expect("open file"); //FIXME use file for the pool
        let mut reader = io::BufReader::new(&f);
        for line in reader.lines() {
            let line: String = line.expect("read transaction line");
            let mut rn: RemoteNode = RemoteNode::new(line.as_str());
            let ctx: zmq::Context = zmq::Context::new();
            rn.connect(&ctx);
            rn.zsock.as_ref().unwrap().send("pi".as_bytes(), 0).expect("send ping");
            self.nodes.push(rn);
        }
    }

    pub fn run(&mut self) {
        self.connect_to_known_nodes();
        let mut ss_to_poll: Vec<zmq::PollItem> = Vec::new();
        ss_to_poll.push(self.cmd_sock.as_poll_item(zmq::POLLIN));
        for ref node in &self.nodes {
            let s: &zmq::Socket = node.zsock.as_ref().unwrap();
            ss_to_poll.push(s.as_poll_item(zmq::POLLIN));
        }
        CommandExecutor::instance().send(Command::Pool(
            PoolCommand::OpenAck(self.open_cmd_id, Ok(self.pool_id)))); //TODO send only after catch-up?
        loop {
            trace!("zmq poll loop >>");
            let r = zmq::poll(ss_to_poll.as_mut_slice(), -1).expect("poll");
            trace!("zmq poll loop ...");
            for ref node in &self.nodes {
                let s: &zmq::Socket = node.zsock.as_ref().unwrap();
                trace!("{:?}", s.recv_string(zmq::DONTWAIT));
            }
            let cmd = self.cmd_sock.recv_string(zmq::DONTWAIT);
            trace!("{:?}", cmd);
            if cmd.is_ok() {
                if "exit".eq(cmd.unwrap().expect("non-string command").as_str()) {
                    break;
                }
            }
            trace!("zmq poll loop << ret {:?}", r);
        }
        info!("zmq poll loop finished");
    }
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();
        let mut pool_worker: PoolWorker = PoolWorker {
            cmd_sock: recv_cmd_sock,
            open_cmd_id: cmd_id,
            pool_id: pool_id,
            nodes: Vec::new(),
        };

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                pool_worker.run();
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0); //TODO
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct NodeData {
    alias: String,
    client_ip: String,
    client_port: u32,
    node_ip: String,
    node_port: u32,
    services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct GenTransaction {
    data: NodeData,
    dest: String,
    identifier: String,
    #[serde(rename = "txnId")]
    txn_id: String,
    #[serde(rename = "type")]
    txn_type: String,
}

struct RemoteNode {
    public_key: Vec<u8>,
    verify_key: Vec<u8>,
    zaddr: String,
    zsock: Option<zmq::Socket>,
}

impl Debug for RemoteNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RemoteNode: {{ public_key: {:?}, verify_key {:?}, zaddr {:?}, zsock is_some {} }}",
               self.public_key, self.verify_key, self.zaddr, self.zsock.is_some())
    }
}

impl RemoteNode {
    fn new(txn: &str) -> RemoteNode {
        let gen_tx: GenTransaction = serde_json::from_str(txn).expect("RemoteNode parsing");
        RemoteNode::from(gen_tx)
    }

    fn connect(&mut self, ctx: &zmq::Context) {
        let key_pair = zmq::CurveKeyPair::new().expect("create key pair");
        let s = ctx.socket(zmq::SocketType::DEALER).expect("socket for Node");
        s.set_curve_secretkey(key_pair.secret_key.as_str()).expect("set secret key");
        s.set_curve_publickey(key_pair.public_key.as_str()).expect("set public key");
        s.set_curve_serverkey(zmq::z85_encode(self.verify_key.as_slice()).unwrap().as_str()).expect("set verify key");
        s.connect(self.zaddr.as_str()).expect("connect to Node");
        self.zsock = Some(s);
    }
}

impl From<GenTransaction> for RemoteNode {
    fn from(tx: GenTransaction) -> RemoteNode {
        fn crypto_sign_ed25519_pk_to_curve25519(pk: &Vec<u8>) -> Vec<u8> {
            // TODO: fix hack:
            // this function isn't included to sodiumoxide rust wrappers,
            // temporary local binding is used to call libsodium-sys function
            extern {
                pub fn crypto_sign_ed25519_pk_to_curve25519(
                    curve25519_pk: *mut [u8; 32],
                    ed25519_pk: *const [u8; 32]) -> c_int;
            }
            let mut from: [u8; 32] = [0; 32];
            from.clone_from_slice(pk.as_slice());
            let mut to: [u8; 32] = [0; 32];
            unsafe {
                crypto_sign_ed25519_pk_to_curve25519(&mut to, &from);
            }
            to.iter().cloned().collect()
        }

        let public_key = tx.dest.as_str().from_base58().expect("dest field in GenTransaction isn't valid");
        RemoteNode {
            verify_key: crypto_sign_ed25519_pk_to_curve25519(&public_key),
            public_key: public_key,
            zaddr: format!("tcp://{}:{}", tx.data.client_ip, tx.data.client_port),
            zsock: None,
        }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => json::decode(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path());

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(json::encode(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<|endoftext|>


<commit_before>use librespot_core::authentication::Credentials;
use librespot_core::config::SessionConfig;
use librespot_core::session::Session;

#[tokio::test]
async fn test_connection() {
    let result = Session::connect(
        SessionConfig::default(),
        Credentials::with_password("test", "test"),
        None,
    )
    .await;

    match result {
        Ok(_) => panic!("Authentication succeeded despite of bad credentials."),
        Err(e) => assert_eq!(e.to_string(), "Login failed with reason: Bad credentials"),
    };
}
<commit_msg>Add timeout to test<commit_after>use std::time::Duration;

use librespot_core::authentication::Credentials;
use librespot_core::config::SessionConfig;
use librespot_core::session::Session;

use tokio::time::timeout;

#[tokio::test]
async fn test_connection() {
    timeout(Duration::from_secs(30), async {
        let result = Session::connect(
            SessionConfig::default(),
            Credentials::with_password("test", "test"),
            None,
        )
        .await;

        match result {
            Ok(_) => panic!("Authentication succeeded despite of bad credentials."),
            Err(e) => assert_eq!(e.to_string(), "Login failed with reason: Bad credentials"),
        }
    })
    .await
    .unwrap();
}
<|endoftext|>