<commit_before>use std::io::{self, Read};
use std::fs::File;
use std::path::Path;

use response::{Response, ResponseWriter};
use mime::{Mime, TopLevel, SubLevel};
use header::ContentType;

include!(concat!(env!("OUT_DIR"), "/mime.rs"));

///Returns the MIME type from a given file extension, if known.
///
///The file extension to MIME type mapping is based on [data from the Apache
///server][apache].
///
///```
///use rustful::file::ext_to_mime;
///use rustful::mime::Mime;
///use rustful::mime::TopLevel::Image;
///use rustful::mime::SubLevel::Jpeg;
///
///let mime = ext_to_mime("jpg");
///assert_eq!(mime, Some(Mime(Image, Jpeg, vec![])));
///```
///
///[apache]: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=markup
pub fn ext_to_mime(ext: &str) -> Option<Mime> {
    MIME.get(ext).map(|&(ref top, ref sub)| {
        Mime(top.into(), sub.into(), vec![])
    })
}

enum Top {
    Known(TopLevel),
    Unknown(&'static str)
}

impl<'a> Into<TopLevel> for &'a Top {
    fn into(self) -> TopLevel {
        match *self {
            Top::Known(ref t) => t.clone(),
            Top::Unknown(t) => TopLevel::Ext(t.into())
        }
    }
}

enum Sub {
    Known(SubLevel),
    Unknown(&'static str)
}

impl<'a> Into<SubLevel> for &'a Sub {
    fn into(self) -> SubLevel {
        match *self {
            Sub::Known(ref s) => s.clone(),
            Sub::Unknown(s) => SubLevel::Ext(s.into())
        }
    }
}

pub struct Loader {
    ///The size, in bytes, of the file chunks. Default is 1048576 (1 megabyte).
    pub chunk_size: usize
}

impl Loader {
    pub fn new() -> Loader {
        Loader {
            chunk_size: 1048576
        }
    }

    pub fn send_file<'a, 'b, P: AsRef<Path>>(&self, path: P, mut response: Response<'a, 'b>) -> Result<(), Error<'a, 'b>> {
        let path: &Path = path.as_ref();
        let mime = path
            .extension()
            .and_then(|ext| ext_to_mime(&ext.to_string_lossy()))
            .unwrap_or(Mime(TopLevel::Application, SubLevel::Ext("octet-stream".into()), vec![]));

        let mut file = match File::open(path) {
            Ok(file) => file,
            Err(e) => return Err(Error::Open(e, response))
        };

        response.set_header(ContentType(mime));

        let mut writer = response.into_writer();
        let mut buffer = vec![0; self.chunk_size];
        loop {
            match file.read(&mut buffer) {
                Ok(len) if len == 0 => break,
                Ok(len) => writer.send(&buffer[..len]),
                Err(e) => return Err(Error::Read(e, writer))
            }
        }

        Ok(())
    }
}

pub enum Error<'a, 'b> {
    Open(io::Error, Response<'a, 'b>),
    Read(io::Error, ResponseWriter<'a, 'b>)
}

impl<'a, 'b> Error<'a, 'b> {
    pub fn io_error(&self) -> &io::Error {
        match *self {
            Error::Open(ref e, _) => e,
            Error::Read(ref e, _) => e
        }
    }
}
<commit_msg>Write more documentation in file module<commit_after>//!File related utilities.

use std::io::{self, Read};
use std::fs::File;
use std::path::Path;

use response::{Response, ResponseWriter};
use mime::{Mime, TopLevel, SubLevel};
use header::ContentType;

include!(concat!(env!("OUT_DIR"), "/mime.rs"));

///Returns the MIME type from a given file extension, if known.
///
///The file extension to MIME type mapping is based on [data from the Apache
///server][apache].
///
///```
///use rustful::file::ext_to_mime;
///use rustful::mime::Mime;
///use rustful::mime::TopLevel::Image;
///use rustful::mime::SubLevel::Jpeg;
///
///let mime = ext_to_mime("jpg");
///assert_eq!(mime, Some(Mime(Image, Jpeg, vec![])));
///```
///
///[apache]: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=markup
pub fn ext_to_mime(ext: &str) -> Option<Mime> {
    MIME.get(ext).map(|&(ref top, ref sub)| {
        Mime(top.into(), sub.into(), vec![])
    })
}

enum Top {
    Known(TopLevel),
    Unknown(&'static str)
}

impl<'a> Into<TopLevel> for &'a Top {
    fn into(self) -> TopLevel {
        match *self {
            Top::Known(ref t) => t.clone(),
            Top::Unknown(t) => TopLevel::Ext(t.into())
        }
    }
}

enum Sub {
    Known(SubLevel),
    Unknown(&'static str)
}

impl<'a> Into<SubLevel> for &'a Sub {
    fn into(self) -> SubLevel {
        match *self {
            Sub::Known(ref s) => s.clone(),
            Sub::Unknown(s) => SubLevel::Ext(s.into())
        }
    }
}

///A utility for loading files from the file system and sending them to the
///client.
pub struct Loader {
    ///The size, in bytes, of the file chunks. Default is 1048576 (1 megabyte).
    pub chunk_size: usize
}

impl Loader {
    pub fn new() -> Loader {
        Loader {
            chunk_size: 1048576
        }
    }

    ///Send a file to the client.
    ///
    ///A MIME type is automatically applied to the response, based on the file
    ///extension, and `application/octet-stream` is used as a fallback if the
    ///extension is unknown. See [`ext_to_mime`](fn.ext_to_mime.html) for more
    ///information.
    pub fn send_file<'a, 'b, P: AsRef<Path>>(&self, path: P, mut response: Response<'a, 'b>) -> Result<(), Error<'a, 'b>> {
        let path: &Path = path.as_ref();
        let mime = path
            .extension()
            .and_then(|ext| ext_to_mime(&ext.to_string_lossy()))
            .unwrap_or(Mime(TopLevel::Application, SubLevel::Ext("octet-stream".into()), vec![]));

        let mut file = match File::open(path) {
            Ok(file) => file,
            Err(e) => return Err(Error::Open(e, response))
        };

        response.set_header(ContentType(mime));

        let mut writer = response.into_writer();
        let mut buffer = vec![0; self.chunk_size];
        loop {
            match file.read(&mut buffer) {
                Ok(len) if len == 0 => break,
                Ok(len) => writer.send(&buffer[..len]),
                Err(e) => return Err(Error::Read(e, writer))
            }
        }

        Ok(())
    }
}

///Error types from `Loader`.
pub enum Error<'a, 'b> {
    ///Failed to open the file.
    Open(io::Error, Response<'a, 'b>),
    ///Failed while reading the file.
    Read(io::Error, ResponseWriter<'a, 'b>)
}

impl<'a, 'b> Error<'a, 'b> {
    pub fn io_error(&self) -> &io::Error {
        match *self {
            Error::Open(ref e, _) => e,
            Error::Read(ref e, _) => e
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

/// Port selected RocksDB options for tuning underlying rocksdb instance of DiemDB.
/// see https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
}

impl Default for RocksdbConfig {
    fn default() -> Self {
        Self {
            // Set max_open_files to 10k instead of -1 to avoid keep-growing memory in accordance
            // with the number of files.
            max_open_files: 10_000,
            // For now we set the max total WAL size to be 1G. This config can be useful when column
            // families are updated at non-uniform frequencies.
            max_total_wal_size: 1u64 << 30,
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub address: SocketAddr,
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub grpc_max_receive_len: Option<i32>,
    /// None disables pruning. The windows is in number of versions, consider system tps
    /// (transaction per second) when calculating proper window.
    pub prune_window: Option<u64>,
    #[serde(skip)]
    data_dir: PathBuf,
    /// Read, Write, Connect timeout for network operations in milliseconds
    pub timeout_ms: u64,
    /// Rocksdb-specific configurations
    pub rocksdb_config: RocksdbConfig,
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6666),
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            grpc_max_receive_len: Some(100_000_000),
            // ~50GB state tree history (about 1 day at 100 tps)
            prune_window: Some(10_000_000),
            data_dir: PathBuf::from("/opt/diem/data"),
            // Default read/write/connection timeout, in milliseconds
            timeout_ms: 30_000,
            rocksdb_config: RocksdbConfig::default(),
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.address.set_port(utils::get_available_port());
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}
<commit_msg>[storage] smaller prune_window<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

/// Port selected RocksDB options for tuning underlying rocksdb instance of DiemDB.
/// see https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
}

impl Default for RocksdbConfig {
    fn default() -> Self {
        Self {
            // Set max_open_files to 10k instead of -1 to avoid keep-growing memory in accordance
            // with the number of files.
            max_open_files: 10_000,
            // For now we set the max total WAL size to be 1G. This config can be useful when column
            // families are updated at non-uniform frequencies.
            max_total_wal_size: 1u64 << 30,
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub address: SocketAddr,
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub grpc_max_receive_len: Option<i32>,
    /// None disables pruning. The windows is in number of versions, consider system tps
    /// (transaction per second) when calculating proper window.
    pub prune_window: Option<u64>,
    #[serde(skip)]
    data_dir: PathBuf,
    /// Read, Write, Connect timeout for network operations in milliseconds
    pub timeout_ms: u64,
    /// Rocksdb-specific configurations
    pub rocksdb_config: RocksdbConfig,
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6666),
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            grpc_max_receive_len: Some(100_000_000),
            // The prune window must at least out live a RPC request because its sub requests are
            // to return a consistent view of the DB at exactly same version. Considering a few
            // thousand TPS we are potentially going to achieve, and a few minutes a consistent view
            // of the DB might require, 10k (TPS)  * 100 (seconds)  =  1 Million might be a
            // conservatively safe minimal prune window. It'll take a few Gigabytes of disk space
            // depending on the size of an average account blob.
            prune_window: Some(1_000_000),
            data_dir: PathBuf::from("/opt/diem/data"),
            // Default read/write/connection timeout, in milliseconds
            timeout_ms: 30_000,
            rocksdb_config: RocksdbConfig::default(),
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.address.set_port(utils::get_available_port());
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::map_err_ignore)]
#![allow(clippy::unnecessary_wraps)]
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
enum Errors {
    Ignored,
}

impl Error for Errors {}

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

fn main() -> Result<(), Errors> {
    let x = u32::try_from(-123_i32);

    println!("{:?}", x.map_err(|_| Errors::Ignored));

    Ok(())
}
<commit_msg>Added test to make sure ignoring the error with a named wildcard value works<commit_after>#![warn(clippy::map_err_ignore)]
#![allow(clippy::unnecessary_wraps)]
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
enum Errors {
    Ignored,
}

impl Error for Errors {}

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

fn main() -> Result<(), Errors> {
    let x = u32::try_from(-123_i32);

    println!("{:?}", x.map_err(|_| Errors::Ignored));

    // Should not warn you because you explicitly ignore the parameter
    // using a named wildcard value
    println!("{:?}", x.map_err(|_foo| Errors::Ignored));

    Ok(())
}
<|endoftext|>


<commit_before>/*
 * Copyright 2022 Miklos Vajna. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

use anyhow::Context as _;

/// Downscales a bitmap (scanned document) to 96x96 DPI to avoid huge PDFs and then converts the
/// result to PDF.
///
/// Example usage: scan-document ~/Downloads/*.jpg out.pdf
fn main() -> anyhow::Result<()> {
    // Separate earlier input and last output arg.
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);
    let output = args.last().cloned().context("no output arg")?;
    args.pop();

    // Downconvert inputs.
    let mut converteds: Vec<String> = Vec::new();
    for arg in args {
        let tempfile = tempfile::Builder::new().suffix(".jpg").tempfile()?;
        let path = tempfile
            .path()
            .file_name()
            .context("file_name() failed")?
            .to_str()
            .context("to_str() failed")?;
        let convert_args = vec![arg.clone(), "-density".into(), "96".into(), path.into()];
        println!("convert {}", convert_args.join(" "));
        let exit_status = std::process::Command::new("convert")
            .args(convert_args)
            .status()?;
        let exit_code = exit_status.code().context("code() failed")?;
        if exit_code != 0 {
            return Err(anyhow::anyhow!("convert failed"));
        }
        converteds.push(path.to_string());
    }

    // Convert downconverted intputs into a single output.
    let mut convert_args = converteds.clone();
    convert_args.push(output);
    println!("convert {}", convert_args.join(" "));
    let exit_status = std::process::Command::new("convert")
        .args(convert_args)
        .status()?;
    let exit_code = exit_status.code().context("code() failed")?;
    if exit_code != 0 {
        return Err(anyhow::anyhow!("convert failed"));
    }

    for converted in converteds {
        std::fs::remove_file(converted)?;
    }

    Ok(())
}
<commit_msg>scan-docuent: get the full path from the tempfile, not just the filename<commit_after>/*
 * Copyright 2022 Miklos Vajna. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

use anyhow::Context as _;

/// Downscales a bitmap (scanned document) to 96x96 DPI to avoid huge PDFs and then converts the
/// result to PDF.
///
/// Example usage: scan-document ~/Downloads/*.jpg out.pdf
fn main() -> anyhow::Result<()> {
    // Separate earlier input and last output arg.
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);
    let output = args.last().cloned().context("no output arg")?;
    args.pop();

    // Downconvert inputs.
    let mut converteds: Vec<String> = Vec::new();
    for arg in args {
        let path: String;
        {
            let tempfile = tempfile::Builder::new().suffix(".jpg").tempfile()?;
            path = tempfile
                .path()
                .to_str()
                .context("to_str() failed")?
                .to_string();
        }
        let convert_args = vec![arg.clone(), "-density".into(), "96".into(), path.clone()];
        println!("convert {}", convert_args.join(" "));
        let exit_status = std::process::Command::new("convert")
            .args(convert_args)
            .status()?;
        let exit_code = exit_status.code().context("code() failed")?;
        if exit_code != 0 {
            return Err(anyhow::anyhow!("convert failed"));
        }
        converteds.push(path.to_string());
    }

    // Convert downconverted intputs into a single output.
    let mut convert_args = converteds.clone();
    convert_args.push(output);
    println!("convert {}", convert_args.join(" "));
    let exit_status = std::process::Command::new("convert")
        .args(convert_args)
        .status()?;
    let exit_code = exit_status.code().context("code() failed")?;
    if exit_code != 0 {
        return Err(anyhow::anyhow!("convert failed"));
    }

    for converted in converteds {
        std::fs::remove_file(converted)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate cairo;
extern crate cairo_sys;
extern crate cssparser;
extern crate glib;
extern crate glib_sys;
extern crate libc;
extern crate itertools;
extern crate pango;
extern crate pango_sys;

#[macro_use]
extern crate downcast_rs;

pub use bbox::{
    RsvgBbox,
    rsvg_bbox_init,
    rsvg_bbox_insert,
    rsvg_bbox_clip
};

pub use chars::{
    rsvg_node_chars_new,
    rsvg_node_chars_append,
    rsvg_node_chars_get_string,
};

pub use clip_path::{
    rsvg_node_clip_path_new,
    rsvg_node_clip_path_get_units
};

pub use cnode::{
    rsvg_rust_cnode_new,
    rsvg_rust_cnode_get_impl
};

pub use color::{
    AllowCurrentColor,
    AllowInherit,
    ColorKind,
    ColorSpec,
    rsvg_css_parse_color
};

pub use gradient::{
    rsvg_node_linear_gradient_new,
    rsvg_node_radial_gradient_new,
    gradient_resolve_fallbacks_and_set_pattern
};

pub use length::{
    LengthUnit,
    LengthDir,
    RsvgLength,
    rsvg_length_parse,
    rsvg_length_normalize,
    rsvg_length_hand_normalize,
};

pub use image::{
    rsvg_node_image_new,
};

pub use marker::{
    rsvg_node_marker_new,
};

pub use mask::{
    rsvg_node_mask_new,
    rsvg_node_mask_get_x,
    rsvg_node_mask_get_y,
    rsvg_node_mask_get_width,
    rsvg_node_mask_get_height,
    rsvg_node_mask_get_units,
    rsvg_node_mask_get_content_units,
};

pub use node::{
    rsvg_node_get_type,
    rsvg_node_get_parent,
    rsvg_node_ref,
    rsvg_node_unref,
    rsvg_node_is_same,
    rsvg_node_get_state,
    rsvg_node_add_child,
    rsvg_node_set_atts,
    rsvg_node_draw,
    rsvg_node_set_attribute_parse_error,
    rsvg_node_foreach_child,
    rsvg_node_draw_children,
};

pub use opacity::{
    OpacityKind,
    OpacitySpec,
    rsvg_css_parse_opacity
};

pub use parsers::{
    rsvg_css_parse_number_list,
    rsvg_css_parse_number_optional_number
};

pub use path_builder::{
    rsvg_path_builder_add_to_cairo_context
};

pub use pattern::{
    rsvg_node_pattern_new,
    pattern_resolve_fallbacks_and_set_pattern,
};

pub use shapes::{
    rsvg_node_circle_new,
    rsvg_node_ellipse_new,
    rsvg_node_line_new,
    rsvg_node_path_new,
    rsvg_node_polygon_new,
    rsvg_node_polyline_new,
    rsvg_node_rect_new,
};

pub use space::{
    rsvg_xml_space_normalize,
};

pub use stop::{
    rsvg_node_stop_new
};

pub use structure::{
    rsvg_node_group_new,
    rsvg_node_defs_new,
    rsvg_node_switch_new,
    rsvg_node_symbol_new,
    rsvg_node_svg_new,
    rsvg_node_svg_get_size,
    rsvg_node_svg_get_view_box,
    rsvg_node_svg_apply_atts,
    rsvg_node_use_new,
};

pub use text::{
    rsvg_text_create_layout,
};

pub use transform::{
    rsvg_parse_transform,
};

pub use viewbox::{
    RsvgViewBox
};

#[macro_use]
mod coord_units;

mod aspect_ratio;
mod bbox;
mod chars;
mod clip_path;
mod cnode;
mod color;
mod drawing_ctx;
mod error;
mod gradient;
mod handle;
mod image;
mod length;
mod marker;
mod mask;
mod node;
mod opacity;
mod paint_server;
mod parse_transform;
mod parsers;
mod path_builder;
mod path_parser;
mod pattern;
mod property_bag;
mod shapes;
mod space;
mod state;
mod stop;
mod structure;
mod text;
mod transform;
mod util;
mod viewbox;
mod viewport;
<commit_msg>Clippy: Ignore some warnings.<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(clone_on_ref_ptr))]
#![cfg_attr(feature = "cargo-clippy", allow(not_unsafe_ptr_arg_deref))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]

extern crate cairo;
extern crate cairo_sys;
extern crate cssparser;
extern crate glib;
extern crate glib_sys;
extern crate libc;
extern crate itertools;
extern crate pango;
extern crate pango_sys;

#[macro_use]
extern crate downcast_rs;

pub use bbox::{
    RsvgBbox,
    rsvg_bbox_init,
    rsvg_bbox_insert,
    rsvg_bbox_clip
};

pub use chars::{
    rsvg_node_chars_new,
    rsvg_node_chars_append,
    rsvg_node_chars_get_string,
};

pub use clip_path::{
    rsvg_node_clip_path_new,
    rsvg_node_clip_path_get_units
};

pub use cnode::{
    rsvg_rust_cnode_new,
    rsvg_rust_cnode_get_impl
};

pub use color::{
    AllowCurrentColor,
    AllowInherit,
    ColorKind,
    ColorSpec,
    rsvg_css_parse_color
};

pub use gradient::{
    rsvg_node_linear_gradient_new,
    rsvg_node_radial_gradient_new,
    gradient_resolve_fallbacks_and_set_pattern
};

pub use length::{
    LengthUnit,
    LengthDir,
    RsvgLength,
    rsvg_length_parse,
    rsvg_length_normalize,
    rsvg_length_hand_normalize,
};

pub use image::{
    rsvg_node_image_new,
};

pub use marker::{
    rsvg_node_marker_new,
};

pub use mask::{
    rsvg_node_mask_new,
    rsvg_node_mask_get_x,
    rsvg_node_mask_get_y,
    rsvg_node_mask_get_width,
    rsvg_node_mask_get_height,
    rsvg_node_mask_get_units,
    rsvg_node_mask_get_content_units,
};

pub use node::{
    rsvg_node_get_type,
    rsvg_node_get_parent,
    rsvg_node_ref,
    rsvg_node_unref,
    rsvg_node_is_same,
    rsvg_node_get_state,
    rsvg_node_add_child,
    rsvg_node_set_atts,
    rsvg_node_draw,
    rsvg_node_set_attribute_parse_error,
    rsvg_node_foreach_child,
    rsvg_node_draw_children,
};

pub use opacity::{
    OpacityKind,
    OpacitySpec,
    rsvg_css_parse_opacity
};

pub use parsers::{
    rsvg_css_parse_number_list,
    rsvg_css_parse_number_optional_number
};

pub use path_builder::{
    rsvg_path_builder_add_to_cairo_context
};

pub use pattern::{
    rsvg_node_pattern_new,
    pattern_resolve_fallbacks_and_set_pattern,
};

pub use shapes::{
    rsvg_node_circle_new,
    rsvg_node_ellipse_new,
    rsvg_node_line_new,
    rsvg_node_path_new,
    rsvg_node_polygon_new,
    rsvg_node_polyline_new,
    rsvg_node_rect_new,
};

pub use space::{
    rsvg_xml_space_normalize,
};

pub use stop::{
    rsvg_node_stop_new
};

pub use structure::{
    rsvg_node_group_new,
    rsvg_node_defs_new,
    rsvg_node_switch_new,
    rsvg_node_symbol_new,
    rsvg_node_svg_new,
    rsvg_node_svg_get_size,
    rsvg_node_svg_get_view_box,
    rsvg_node_svg_apply_atts,
    rsvg_node_use_new,
};

pub use text::{
    rsvg_text_create_layout,
};

pub use transform::{
    rsvg_parse_transform,
};

pub use viewbox::{
    RsvgViewBox
};

#[macro_use]
mod coord_units;

mod aspect_ratio;
mod bbox;
mod chars;
mod clip_path;
mod cnode;
mod color;
mod drawing_ctx;
mod error;
mod gradient;
mod handle;
mod image;
mod length;
mod marker;
mod mask;
mod node;
mod opacity;
mod paint_server;
mod parse_transform;
mod parsers;
mod path_builder;
mod path_parser;
mod pattern;
mod property_bag;
mod shapes;
mod space;
mod state;
mod stop;
mod structure;
mod text;
mod transform;
mod util;
mod viewbox;
mod viewport;
<|endoftext|>


<commit_before>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct ZopfliHash {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */

    /* Fields with similar purpose as the above hash, but for the second hash with
    a value that is calculated differently.  */
    head2: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev2: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval2: Vec<i32>,  /* Index to hash value at this index. */
    val2: i32,  /* Current hash value. */

    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
           head: vec![-1; 65536],
           prev: (0..window_size as u16).collect(),
           hashval: vec![-1; window_size],
           val: 0,

           /* Fields with similar purpose as the above hash, but for the second hash with
           a value that is calculated differently.  */
           head2: vec![-1; 65536],
           prev2: (0..window_size as u16).collect(),
           hashval2: vec![-1; window_size],
           val2: 0,

           same: vec![0; window_size],
       }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];

        self.same = vec![0; window_size];
        self.val2 = 0;
        self.head2 = vec![-1; 65536];
        self.prev2 = (0..window_size as u16).collect();
        self.hashval2 = vec![-1; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.val = ((self.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;
        self.hashval[hpos] = self.val;

        let index = self.val as usize;
        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as u16;
        } else {
            self.prev[hpos] = hpos as u16;
        }
        self.head[index] = hpos as i32;

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        while another_index < array.len() && array[pos] == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        self.same[hpos] = amount as u16;

        self.val2 = ((self.same[hpos].wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.val as u16) as i32;
        self.hashval2[hpos] = self.val2;

        let index2 = self.val2 as usize;
        if self.head2[index2] != -1 && self.hashval2[self.head2[index2] as usize] == self.val2 {
            self.prev2[hpos] = self.head2[index2] as u16;
        } else {
            self.prev2[hpos] = hpos as u16;
        }
        self.head2[index2] = hpos as i32;
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.head[index],
            Which::Hash2 => self.head2[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.prev[index],
            Which::Hash2 => self.prev2[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hashval[index],
            Which::Hash2 => self.hashval2[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.val,
            Which::Hash2 => self.val2,
        }
    }
}
<commit_msg>Make a hash1 and hash2 data structure that are the same<commit_after>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new(window_size: usize) -> HashThing {
        HashThing {
            head: vec![-1; 65536],
            prev: (0..window_size as u16).collect(),
            hashval: vec![-1; window_size],
            val: 0,
        }
    }

    fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(window_size),
            hash2: HashThing::new(window_size),
            same: vec![0; window_size],
        }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.hash1.reset(window_size);
        self.hash2.reset(window_size);
        self.same = vec![0; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;
        self.hash1.hashval[hpos] = self.hash1.val;

        let index = self.hash1.val as usize;
        if self.hash1.head[index] != -1 && self.hash1.hashval[self.hash1.head[index] as usize] == self.hash1.val {
            self.hash1.prev[hpos] = self.hash1.head[index] as u16;
        } else {
            self.hash1.prev[hpos] = hpos as u16;
        }
        self.hash1.head[index] = hpos as i32;

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        while another_index < array.len() && array[pos] == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        self.same[hpos] = amount as u16;

        self.hash2.val = ((self.same[hpos].wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;
        self.hash2.hashval[hpos] = self.hash2.val;

        let index2 = self.hash2.val as usize;
        if self.hash2.head[index2] != -1 && self.hash2.hashval[self.hash2.head[index2] as usize] == self.hash2.val {
            self.hash2.prev[hpos] = self.hash2.head[index2] as u16;
        } else {
            self.hash2.prev[hpos] = hpos as u16;
        }
        self.hash2.head[index2] = hpos as i32;
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev[index],
            Which::Hash2 => self.hash2.prev[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.hashval[index],
            Which::Hash2 => self.hash2.hashval[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<|endoftext|>


<commit_before>extern crate openzwave_stateful as openzwave;
extern crate foxbox_taxonomy as taxonomy;
extern crate transformable_channels;

use taxonomy::util::Id as TaxId;
use taxonomy::services::{ Setter, Getter, AdapterId, ServiceId, Service, Channel, ChannelKind };
use taxonomy::values::*;
use taxonomy::api::{ ResultMap, Error as TaxError, InternalError };
use taxonomy::adapter::{ AdapterManagerHandle, AdapterWatchGuard, WatchEvent };
use transformable_channels::mpsc::ExtSender;

use openzwave::{ InitOptions, ZWaveManager, ZWaveNotification };
use openzwave::{ CommandClass, ValueGenre, ValueType, ValueID };
use openzwave::{ Controller };

use std::error;
use std::fmt;
use std::thread;
use std::sync::mpsc;
use std::sync::{ Arc, Mutex, RwLock, Weak };
use std::collections::{ HashMap, HashSet };

pub use self::OpenzwaveAdapter as Adapter;

#[derive(Debug)]
pub enum Error {
    TaxonomyError(TaxError),
    NoDeviceFound(openzwave::Error),
    OpenzwaveError(openzwave::Error),
    UnknownError
}

impl From<TaxError> for Error {
    fn from(err: TaxError) -> Self {
        Error::TaxonomyError(err)
    }
}

impl From<()> for Error {
    fn from(_: ()) -> Self {
        Error::UnknownError
    }
}

impl From<openzwave::Error> for Error {
    fn from(error: openzwave::Error) -> Self {
        match error {
            openzwave::Error::NoDeviceFound => Error::NoDeviceFound(error),
            _                               => Error::OpenzwaveError(error)
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TaxonomyError(ref err)  => write!(f, "{}: {}", error::Error::description(self), err),
            Error::OpenzwaveError(ref err) => write!(f, "{}: {}", error::Error::description(self), err),
            Error::NoDeviceFound(_) | Error::UnknownError => write!(f, "{}", error::Error::description(self)),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TaxonomyError(_) => "Taxonomy Error",
            Error::OpenzwaveError(_) => "Openzwave Error",
            Error::NoDeviceFound(_) => "No Device Found",
            Error::UnknownError => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::TaxonomyError(ref err) => Some(err),
            Error::OpenzwaveError(ref err) => Some(err),
            Error::NoDeviceFound(ref err) => Some(err),
            Error::UnknownError => None,
        }
    }
}

#[derive(Debug, Clone)]
struct IdMap<Kind, Type> {
    map: Arc<RwLock<Vec<(TaxId<Kind>, Type)>>>
}

impl<Kind, Type> IdMap<Kind, Type> where Type: Eq + Clone, Kind: Clone {
    fn new() -> Self {
        IdMap {
            map: Arc::new(RwLock::new(Vec::new()))
        }
    }

    fn push(&mut self, id: TaxId<Kind>, ozw_object: Type) -> Result<(), ()> {
        let mut guard = try!(self.map.write().or(Err(())));
        guard.push((id, ozw_object));
        Ok(())
    }

    fn find_tax_id_from_ozw(&self, needle: &Type) -> Result<Option<TaxId<Kind>>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(_, ref controller)| controller == needle);
        Ok(find_result.map(|&(ref id, _)| id.clone()))
    }

    fn find_ozw_from_tax_id(&self, needle: &TaxId<Kind>) -> Result<Option<Type>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(ref id, _)| id == needle);
        Ok(find_result.map(|&(_, ref ozw_object)| ozw_object.clone()))
    }
}

type SyncExtSender = Mutex<Box<ExtSender<WatchEvent>>>;
type WatchersMap = HashMap<usize, Arc<SyncExtSender>>;
struct Watchers {
    current_index: usize,
    map: Arc<Mutex<WatchersMap>>,
    getter_map: HashMap<TaxId<Getter>, Vec<Weak<SyncExtSender>>>,
}

impl Watchers {
    fn new() -> Self {
        Watchers {
            current_index: 0,
            map: Arc::new(Mutex::new(HashMap::new())),
            getter_map: HashMap::new(),
        }
    }

    fn push(&mut self, tax_id: TaxId<Getter>, watcher: Arc<SyncExtSender>) -> WatcherGuard {
        let index = self.current_index;
        self.current_index += 1;
        {
            let mut map = self.map.lock().unwrap();
            map.insert(index, watcher.clone());
        }

        let entry = self.getter_map.entry(tax_id).or_insert(Vec::new());
        entry.push(Arc::downgrade(&watcher));

        WatcherGuard {
            key: index,
            map: self.map.clone()
        }
    }

    fn get(&self, index: usize) -> Option<Arc<SyncExtSender>> {
        let map = self.map.lock().unwrap();
        map.get(&index).cloned()
    }

    fn get_from_tax_id(&self, tax_id: &TaxId<Getter>) -> Option<Vec<Arc<SyncExtSender>>> {
        self.getter_map.get(tax_id).and_then(|vec| {
            let vec: Vec<_> = vec.iter().filter_map(|weak_sender| weak_sender.upgrade()).collect();
            if vec.len() == 0 { None } else { Some(vec) }
        })
    }
}

fn kind_from_value(value: ValueID) -> Option<ChannelKind> {
    value.get_command_class().map(|cc| match cc {
        CommandClass::SensorBinary => ChannelKind::OpenClosed,
        _ => ChannelKind::Ready // TODO
    })
}

fn to_open_closed(value: &ValueID) -> Option<Value> {
    debug_assert_eq!(value.get_type(), ValueType::ValueType_Bool);

    value.as_bool().ok().map(|val| {
        Value::OpenClosed(
            if val { OpenClosed::Open } else { OpenClosed::Closed }
        )
    })
}

struct WatcherGuard {
    key: usize,
    map: Arc<Mutex<WatchersMap>>,
}

impl Drop for WatcherGuard {
    fn drop(&mut self) {
        let mut map = self.map.lock().unwrap();
        map.remove(&self.key);
    }
}

impl AdapterWatchGuard for WatcherGuard {}

pub struct OpenzwaveAdapter {
    id: TaxId<AdapterId>,
    name: String,
    vendor: String,
    version: [u32; 4],
    ozw: ZWaveManager,
    controller_map: IdMap<ServiceId, Controller>,
    getter_map: IdMap<Getter, ValueID>,
    setter_map: IdMap<Setter, ValueID>,
    watchers: Arc<Mutex<Watchers>>,
}

impl OpenzwaveAdapter {
    pub fn init<T: AdapterManagerHandle + Send + Sync + 'static> (box_manager: &Arc<T>) -> Result<(), Error> {
        let options = InitOptions {
            device: None // TODO we should expose this as a Value
        };

        let (ozw, rx) = try!(openzwave::init(&options));

        let name = String::from("OpenZwave Adapter");
        let adapter = Arc::new(OpenzwaveAdapter {
            id: TaxId::new(&name),
            name: name,
            vendor: String::from("Mozilla"),
            version: [1, 0, 0, 0],
            ozw: ozw,
            controller_map: IdMap::new(),
            getter_map: IdMap::new(),
            setter_map: IdMap::new(),
            watchers: Arc::new(Mutex::new(Watchers::new())),
        });

        adapter.spawn_notification_thread(rx, box_manager);
        try!(box_manager.add_adapter(adapter));

        Ok(())
    }

    fn spawn_notification_thread<T: AdapterManagerHandle + Send + Sync + 'static>(&self, rx: mpsc::Receiver<ZWaveNotification>, box_manager: &Arc<T>) {
        let adapter_id = self.id.clone();
        let box_manager = box_manager.clone();
        let mut controller_map = self.controller_map.clone();
        let mut getter_map = self.getter_map.clone();
        let mut setter_map = self.setter_map.clone();
        let watchers = self.watchers.clone();

        thread::spawn(move || {
            for notification in rx {
                match notification {
                    ZWaveNotification::ControllerReady(controller) => {
                        let service = format!("OpenZWave/{}", controller.get_home_id());
                        let service_id = TaxId::new(&service);
                        controller_map.push(service_id.clone(), controller);

                        box_manager.add_service(Service::empty(service_id.clone(), adapter_id.clone()));
                    }
                    ZWaveNotification::NodeNew(node)               => {}
                    ZWaveNotification::NodeAdded(node)             => {}
                    ZWaveNotification::NodeRemoved(node)           => {}
                    ZWaveNotification::ValueAdded(value)           => {
                        if value.get_genre() != ValueGenre::ValueGenre_User { continue }

                        let value_id = format!("OpenZWave/{}", value.get_id());

                        let controller_id = controller_map.find_tax_id_from_ozw(&value.get_controller()).unwrap();
                        if controller_id.is_none() { continue }
                        let controller_id = controller_id.unwrap();

                        let has_getter = !value.is_write_only();
                        let has_setter = !value.is_read_only();

                        let kind = kind_from_value(value);
                        if kind.is_none() { continue }
                        let kind = kind.unwrap();

                        if has_getter {
                            let getter_id = TaxId::new(&value_id);
                            getter_map.push(getter_id.clone(), value);
                            box_manager.add_getter(Channel {
                                id: getter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Getter {
                                    kind: kind.clone(),
                                    updated: None
                                }
                            });
                        }

                        if has_setter {
                            let setter_id = TaxId::new(&value_id);
                            setter_map.push(setter_id.clone(), value);
                            box_manager.add_setter(Channel {
                                id: setter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Setter {
                                    kind: kind,
                                    updated: None
                                }
                            });
                        }
                    }
                    ZWaveNotification::ValueChanged(value)         => {
                        match value.get_type() {
                            ValueType::ValueType_Bool => {},
                            _ => continue // ignore non-bool vals for now
                        };

                        let tax_id = match getter_map.find_tax_id_from_ozw(&value) {
                            Ok(Some(tax_id)) => tax_id,
                            _ => continue
                        };

                        let watchers = watchers.lock().unwrap();

                        let watchers = match watchers.get_from_tax_id(&tax_id) {
                            Some(watchers) => watchers,
                            _ => continue
                        };

                        for sender in &watchers {
                            let sender = sender.lock().unwrap();
                            if let Some(value) = to_open_closed(&value) {
                                sender.send(
                                    WatchEvent::Enter { id: tax_id.clone(), value: value }
                                );
                            }
                        }
                    }
                    ZWaveNotification::ValueRemoved(value)         => {}
                    ZWaveNotification::Generic(string)             => {}
                }
            }
        });
    }
}

impl taxonomy::adapter::Adapter for OpenzwaveAdapter {
    fn id(&self) -> TaxId<AdapterId> {
        self.id.clone()
    }

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

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

    fn version(&self) -> &[u32; 4] {
        &self.version
    }

    fn fetch_values(&self, mut set: Vec<TaxId<Getter>>) -> ResultMap<TaxId<Getter>, Option<Value>, TaxError> {
        set.drain(..).map(|id| {
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); //FIXME no unwrap

            let ozw_value: Option<Option<Value>> = ozw_value.map(|ozw_value: ValueID| {
                if !ozw_value.is_set() { return None }

                let result: Option<Value> = match ozw_value.get_type() {
                    ValueType::ValueType_Bool => to_open_closed(&ozw_value),
                    _ => Some(Value::Unit)
                };
                result
            });
            let value_result: Result<Option<Value>, TaxError> = ozw_value.ok_or(TaxError::InternalError(InternalError::NoSuchGetter(id.clone())));
            (id, value_result)
        }).collect()
    }

    fn send_values(&self, values: HashMap<TaxId<Setter>, Value>) -> ResultMap<TaxId<Setter>, (), TaxError> {
        unimplemented!()
    }

    fn register_watch(&self, mut values: Vec<(TaxId<Getter>, Option<Range>)>, sender: Box<ExtSender<WatchEvent>>) -> ResultMap<TaxId<Getter>, Box<AdapterWatchGuard>, TaxError> {
        let sender = Arc::new(Mutex::new(sender)); // Mutex is necessary because cb is not Sync.
        values.drain(..).map(|(id, _)| {
            let watch_guard = {
                let mut watchers = self.watchers.lock().unwrap();
                watchers.push(id.clone(), sender.clone())
            };
            let value_result: Result<Box<AdapterWatchGuard>, TaxError> = Ok(Box::new(watch_guard));

            // if there is a set value already, let's send it.
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); // FIXME no unwrap
            if let Some(value) = ozw_value {
                if value.is_set() && value.get_type() == ValueType::ValueType_Bool {
                    if let Some(value) = to_open_closed(&value) {
                        let sender = sender.lock().unwrap();
                        sender.send(
                            WatchEvent::Enter { id: id.clone(), value: value }
                        );
                    }
                }
            }

            (id, value_result)
        }).collect()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

<commit_msg>[fxbox/openzwave-adapter] Specify config paths to openzwave::init<commit_after>extern crate openzwave_stateful as openzwave;
extern crate foxbox_taxonomy as taxonomy;
extern crate transformable_channels;

use taxonomy::util::Id as TaxId;
use taxonomy::services::{ Setter, Getter, AdapterId, ServiceId, Service, Channel, ChannelKind };
use taxonomy::values::*;
use taxonomy::api::{ ResultMap, Error as TaxError, InternalError };
use taxonomy::adapter::{ AdapterManagerHandle, AdapterWatchGuard, WatchEvent };
use transformable_channels::mpsc::ExtSender;

use openzwave::{ InitOptions, ZWaveManager, ZWaveNotification };
use openzwave::{ CommandClass, ValueGenre, ValueType, ValueID };
use openzwave::{ Controller };

use std::error;
use std::fmt;
use std::thread;
use std::sync::mpsc;
use std::sync::{ Arc, Mutex, RwLock, Weak };
use std::collections::{ HashMap, HashSet };

pub use self::OpenzwaveAdapter as Adapter;

#[derive(Debug)]
pub enum Error {
    TaxonomyError(TaxError),
    NoDeviceFound(openzwave::Error),
    OpenzwaveError(openzwave::Error),
    UnknownError
}

impl From<TaxError> for Error {
    fn from(err: TaxError) -> Self {
        Error::TaxonomyError(err)
    }
}

impl From<()> for Error {
    fn from(_: ()) -> Self {
        Error::UnknownError
    }
}

impl From<openzwave::Error> for Error {
    fn from(error: openzwave::Error) -> Self {
        match error {
            openzwave::Error::NoDeviceFound => Error::NoDeviceFound(error),
            _                               => Error::OpenzwaveError(error)
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TaxonomyError(ref err)  => write!(f, "{}: {}", error::Error::description(self), err),
            Error::OpenzwaveError(ref err) => write!(f, "{}: {}", error::Error::description(self), err),
            Error::NoDeviceFound(_) | Error::UnknownError => write!(f, "{}", error::Error::description(self)),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TaxonomyError(_) => "Taxonomy Error",
            Error::OpenzwaveError(_) => "Openzwave Error",
            Error::NoDeviceFound(_) => "No Device Found",
            Error::UnknownError => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::TaxonomyError(ref err) => Some(err),
            Error::OpenzwaveError(ref err) => Some(err),
            Error::NoDeviceFound(ref err) => Some(err),
            Error::UnknownError => None,
        }
    }
}

#[derive(Debug, Clone)]
struct IdMap<Kind, Type> {
    map: Arc<RwLock<Vec<(TaxId<Kind>, Type)>>>
}

impl<Kind, Type> IdMap<Kind, Type> where Type: Eq + Clone, Kind: Clone {
    fn new() -> Self {
        IdMap {
            map: Arc::new(RwLock::new(Vec::new()))
        }
    }

    fn push(&mut self, id: TaxId<Kind>, ozw_object: Type) -> Result<(), ()> {
        let mut guard = try!(self.map.write().or(Err(())));
        guard.push((id, ozw_object));
        Ok(())
    }

    fn find_tax_id_from_ozw(&self, needle: &Type) -> Result<Option<TaxId<Kind>>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(_, ref controller)| controller == needle);
        Ok(find_result.map(|&(ref id, _)| id.clone()))
    }

    fn find_ozw_from_tax_id(&self, needle: &TaxId<Kind>) -> Result<Option<Type>, ()> {
        let guard = try!(self.map.read().or(Err(())));
        let find_result = guard.iter().find(|&&(ref id, _)| id == needle);
        Ok(find_result.map(|&(_, ref ozw_object)| ozw_object.clone()))
    }
}

type SyncExtSender = Mutex<Box<ExtSender<WatchEvent>>>;
type WatchersMap = HashMap<usize, Arc<SyncExtSender>>;
struct Watchers {
    current_index: usize,
    map: Arc<Mutex<WatchersMap>>,
    getter_map: HashMap<TaxId<Getter>, Vec<Weak<SyncExtSender>>>,
}

impl Watchers {
    fn new() -> Self {
        Watchers {
            current_index: 0,
            map: Arc::new(Mutex::new(HashMap::new())),
            getter_map: HashMap::new(),
        }
    }

    fn push(&mut self, tax_id: TaxId<Getter>, watcher: Arc<SyncExtSender>) -> WatcherGuard {
        let index = self.current_index;
        self.current_index += 1;
        {
            let mut map = self.map.lock().unwrap();
            map.insert(index, watcher.clone());
        }

        let entry = self.getter_map.entry(tax_id).or_insert(Vec::new());
        entry.push(Arc::downgrade(&watcher));

        WatcherGuard {
            key: index,
            map: self.map.clone()
        }
    }

    fn get(&self, index: usize) -> Option<Arc<SyncExtSender>> {
        let map = self.map.lock().unwrap();
        map.get(&index).cloned()
    }

    fn get_from_tax_id(&self, tax_id: &TaxId<Getter>) -> Option<Vec<Arc<SyncExtSender>>> {
        self.getter_map.get(tax_id).and_then(|vec| {
            let vec: Vec<_> = vec.iter().filter_map(|weak_sender| weak_sender.upgrade()).collect();
            if vec.len() == 0 { None } else { Some(vec) }
        })
    }
}

fn kind_from_value(value: ValueID) -> Option<ChannelKind> {
    value.get_command_class().map(|cc| match cc {
        CommandClass::SensorBinary => ChannelKind::OpenClosed,
        _ => ChannelKind::Ready // TODO
    })
}

fn to_open_closed(value: &ValueID) -> Option<Value> {
    debug_assert_eq!(value.get_type(), ValueType::ValueType_Bool);

    value.as_bool().ok().map(|val| {
        Value::OpenClosed(
            if val { OpenClosed::Open } else { OpenClosed::Closed }
        )
    })
}

struct WatcherGuard {
    key: usize,
    map: Arc<Mutex<WatchersMap>>,
}

impl Drop for WatcherGuard {
    fn drop(&mut self) {
        let mut map = self.map.lock().unwrap();
        map.remove(&self.key);
    }
}

impl AdapterWatchGuard for WatcherGuard {}

pub struct OpenzwaveAdapter {
    id: TaxId<AdapterId>,
    name: String,
    vendor: String,
    version: [u32; 4],
    ozw: ZWaveManager,
    controller_map: IdMap<ServiceId, Controller>,
    getter_map: IdMap<Getter, ValueID>,
    setter_map: IdMap<Setter, ValueID>,
    watchers: Arc<Mutex<Watchers>>,
}

impl OpenzwaveAdapter {
    pub fn init<T: AdapterManagerHandle + Send + Sync + 'static> (box_manager: &Arc<T>) -> Result<(), Error> {
        let options = InitOptions {
            device: None, // TODO we should expose this as a Value
            config_path: "./config/openzwave/".to_string(),
            user_path: "./config/openzwave/".to_string(),
        };

        let (ozw, rx) = try!(openzwave::init(&options));

        let name = String::from("OpenZwave Adapter");
        let adapter = Arc::new(OpenzwaveAdapter {
            id: TaxId::new(&name),
            name: name,
            vendor: String::from("Mozilla"),
            version: [1, 0, 0, 0],
            ozw: ozw,
            controller_map: IdMap::new(),
            getter_map: IdMap::new(),
            setter_map: IdMap::new(),
            watchers: Arc::new(Mutex::new(Watchers::new())),
        });

        adapter.spawn_notification_thread(rx, box_manager);
        try!(box_manager.add_adapter(adapter));

        Ok(())
    }

    fn spawn_notification_thread<T: AdapterManagerHandle + Send + Sync + 'static>(&self, rx: mpsc::Receiver<ZWaveNotification>, box_manager: &Arc<T>) {
        let adapter_id = self.id.clone();
        let box_manager = box_manager.clone();
        let mut controller_map = self.controller_map.clone();
        let mut getter_map = self.getter_map.clone();
        let mut setter_map = self.setter_map.clone();
        let watchers = self.watchers.clone();

        thread::spawn(move || {
            for notification in rx {
                match notification {
                    ZWaveNotification::ControllerReady(controller) => {
                        let service = format!("OpenZWave/{}", controller.get_home_id());
                        let service_id = TaxId::new(&service);
                        controller_map.push(service_id.clone(), controller);

                        box_manager.add_service(Service::empty(service_id.clone(), adapter_id.clone()));
                    }
                    ZWaveNotification::NodeNew(node)               => {}
                    ZWaveNotification::NodeAdded(node)             => {}
                    ZWaveNotification::NodeRemoved(node)           => {}
                    ZWaveNotification::ValueAdded(value)           => {
                        if value.get_genre() != ValueGenre::ValueGenre_User { continue }

                        let value_id = format!("OpenZWave/{}", value.get_id());

                        let controller_id = controller_map.find_tax_id_from_ozw(&value.get_controller()).unwrap();
                        if controller_id.is_none() { continue }
                        let controller_id = controller_id.unwrap();

                        let has_getter = !value.is_write_only();
                        let has_setter = !value.is_read_only();

                        let kind = kind_from_value(value);
                        if kind.is_none() { continue }
                        let kind = kind.unwrap();

                        if has_getter {
                            let getter_id = TaxId::new(&value_id);
                            getter_map.push(getter_id.clone(), value);
                            box_manager.add_getter(Channel {
                                id: getter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Getter {
                                    kind: kind.clone(),
                                    updated: None
                                }
                            });
                        }

                        if has_setter {
                            let setter_id = TaxId::new(&value_id);
                            setter_map.push(setter_id.clone(), value);
                            box_manager.add_setter(Channel {
                                id: setter_id.clone(),
                                service: controller_id.clone(),
                                adapter: adapter_id.clone(),
                                last_seen: None,
                                tags: HashSet::new(),
                                mechanism: Setter {
                                    kind: kind,
                                    updated: None
                                }
                            });
                        }
                    }
                    ZWaveNotification::ValueChanged(value)         => {
                        match value.get_type() {
                            ValueType::ValueType_Bool => {},
                            _ => continue // ignore non-bool vals for now
                        };

                        let tax_id = match getter_map.find_tax_id_from_ozw(&value) {
                            Ok(Some(tax_id)) => tax_id,
                            _ => continue
                        };

                        let watchers = watchers.lock().unwrap();

                        let watchers = match watchers.get_from_tax_id(&tax_id) {
                            Some(watchers) => watchers,
                            _ => continue
                        };

                        for sender in &watchers {
                            let sender = sender.lock().unwrap();
                            if let Some(value) = to_open_closed(&value) {
                                sender.send(
                                    WatchEvent::Enter { id: tax_id.clone(), value: value }
                                );
                            }
                        }
                    }
                    ZWaveNotification::ValueRemoved(value)         => {}
                    ZWaveNotification::Generic(string)             => {}
                }
            }
        });
    }
}

impl taxonomy::adapter::Adapter for OpenzwaveAdapter {
    fn id(&self) -> TaxId<AdapterId> {
        self.id.clone()
    }

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

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

    fn version(&self) -> &[u32; 4] {
        &self.version
    }

    fn fetch_values(&self, mut set: Vec<TaxId<Getter>>) -> ResultMap<TaxId<Getter>, Option<Value>, TaxError> {
        set.drain(..).map(|id| {
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); //FIXME no unwrap

            let ozw_value: Option<Option<Value>> = ozw_value.map(|ozw_value: ValueID| {
                if !ozw_value.is_set() { return None }

                let result: Option<Value> = match ozw_value.get_type() {
                    ValueType::ValueType_Bool => to_open_closed(&ozw_value),
                    _ => Some(Value::Unit)
                };
                result
            });
            let value_result: Result<Option<Value>, TaxError> = ozw_value.ok_or(TaxError::InternalError(InternalError::NoSuchGetter(id.clone())));
            (id, value_result)
        }).collect()
    }

    fn send_values(&self, values: HashMap<TaxId<Setter>, Value>) -> ResultMap<TaxId<Setter>, (), TaxError> {
        unimplemented!()
    }

    fn register_watch(&self, mut values: Vec<(TaxId<Getter>, Option<Range>)>, sender: Box<ExtSender<WatchEvent>>) -> ResultMap<TaxId<Getter>, Box<AdapterWatchGuard>, TaxError> {
        let sender = Arc::new(Mutex::new(sender)); // Mutex is necessary because cb is not Sync.
        values.drain(..).map(|(id, _)| {
            let watch_guard = {
                let mut watchers = self.watchers.lock().unwrap();
                watchers.push(id.clone(), sender.clone())
            };
            let value_result: Result<Box<AdapterWatchGuard>, TaxError> = Ok(Box::new(watch_guard));

            // if there is a set value already, let's send it.
            let ozw_value: Option<ValueID> = self.getter_map.find_ozw_from_tax_id(&id).unwrap(); // FIXME no unwrap
            if let Some(value) = ozw_value {
                if value.is_set() && value.get_type() == ValueType::ValueType_Bool {
                    if let Some(value) = to_open_closed(&value) {
                        let sender = sender.lock().unwrap();
                        sender.send(
                            WatchEvent::Enter { id: id.clone(), value: value }
                        );
                    }
                }
            }

            (id, value_result)
        }).collect()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

<|endoftext|>


<commit_before>extern crate futures;
extern crate hyper;
extern crate log;
extern crate serde;
extern crate tokio_core;

use super::config;
use futures::Stream;
use hyper::{Client, Method, Request as HyperRequest, Response as HyperResponse, Uri};
use hyper::header::{ContentLength, ContentType};
use tokio_core::reactor::Core;

pub fn request() -> Request {
    let core = Core::new().unwrap();
    let client = Client::new(&core.handle());

    Request {
        addr: config::base_url().clone(),
        core,
        client,
        request: None,
    }
}

pub struct Request {
    addr: String,
    core: Core,
    client: Client<self::hyper::client::HttpConnector>,
    request: Option<HyperRequest>,
}

impl Request {
    pub fn post(mut self, path: &str, body: Option<String>) -> Request {
        let mut request = HyperRequest::new(Method::Post, self.uri(path));
        match body {
            Some(json) => {
                debug!("Request body: {}", json);
                request.headers_mut().set(ContentType::json());
                request.headers_mut().set(ContentLength(json.len() as u64));
                request.set_body(json);
            }
            _ => {
                debug!("Empty request body...");
            }
        }

        self.request = Some(request);
        self
    }

    pub fn response(self) -> Response {
        let req = self.request.unwrap();
        let mut core = self.core;
        let client = self.client;

        let res = core.run(client.request(req)).unwrap();
        Response {
            core: core,
            response: res,
        }
    }

    fn uri(&self, path: &str) -> Uri {
        let uri = format!("{}{}", self.addr, path).parse().unwrap();
        debug!("Request URL: {}", uri);
        uri
    }
}

pub struct Response {
    core: Core,
    response: HyperResponse,
}

impl Response {
    pub fn status(&self) -> u16 {
        debug!("Response status: {}", self.response.status());
        self.response.status().clone().into()
    }

    pub fn body(self) -> String {
        let mut core = self.core;
        let body = core.run(self.response.body().fold(Vec::new(), |mut body, chunk| {
            body.extend_from_slice(&chunk);
            Ok::<_, self::hyper::Error>(body)
        })).unwrap();

        let json = String::from_utf8(body).unwrap();
        debug!("Response body: {}", json);
        json
    }
}<commit_msg>fix linting<commit_after>extern crate futures;
extern crate hyper;
extern crate log;
extern crate serde;
extern crate tokio_core;

use super::config;
use futures::Stream;
use hyper::{Client, Method, Request as HyperRequest, Response as HyperResponse, Uri};
use hyper::header::{ContentLength, ContentType};
use tokio_core::reactor::Core;

pub fn request() -> Request {
    let core = Core::new().unwrap();
    let client = Client::new(&core.handle());

    Request {
        addr: config::base_url().clone(),
        core,
        client,
        request: None,
    }
}

pub struct Request {
    addr: String,
    core: Core,
    client: Client<self::hyper::client::HttpConnector>,
    request: Option<HyperRequest>,
}

impl Request {
    pub fn post(mut self, path: &str, body: Option<String>) -> Request {
        let mut request = HyperRequest::new(Method::Post, self.uri(path));
        match body {
            Some(json) => {
                debug!("Request body: {}", json);
                request.headers_mut().set(ContentType::json());
                request.headers_mut().set(ContentLength(json.len() as u64));
                request.set_body(json);
            }
            _ => {
                debug!("Empty request body...");
            }
        }

        self.request = Some(request);
        self
    }

    pub fn response(self) -> Response {
        let req = self.request.unwrap();
        let mut core = self.core;
        let client = self.client;

        let res = core.run(client.request(req)).unwrap();
        Response {
            core: core,
            response: res,
        }
    }

    fn uri(&self, path: &str) -> Uri {
        let uri = format!("{}{}", self.addr, path).parse().unwrap();
        debug!("Request URL: {}", uri);
        uri
    }
}

pub struct Response {
    core: Core,
    response: HyperResponse,
}

impl Response {
    pub fn status(&self) -> u16 {
        debug!("Response status: {}", self.response.status());
        self.response.status().clone().into()
    }

    pub fn body(self) -> String {
        let mut core = self.core;
        let body = core.run(self.response.body().fold(Vec::new(), |mut body, chunk| {
            body.extend_from_slice(&chunk);
            Ok::<_, self::hyper::Error>(body)
        })).unwrap();

        let json = String::from_utf8(body).unwrap();
        debug!("Response body: {}", json);
        json
    }
}<|endoftext|>


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

use std::{path::Path,
          time::Duration};

use habitat_core::{env,
                   package::PackageTarget,
                   util::sys};
use hyper::{client::{pool::{Config,
                            Pool},
                     Client as HyperClient,
                     IntoUrl,
                     RequestBuilder},
            header::UserAgent,
            http::h1::Http11Protocol,
            net::HttpsConnector};
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnector,
                   SslConnectorBuilder,
                   SslMethod,
                   SslOption,
                   SSL_OP_NO_COMPRESSION,
                   SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3,
                   SSL_VERIFY_NONE};
use url::Url;

use crate::{error::{Error,
                    Result},
            net::ProxyHttpsConnector,
            proxy::{proxy_unless_domain_exempted,
                    ProxyInfo},
            ssl};

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT_SEC: u64 = 120;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(endpoint: T,
                  product: &str,
                  version: &str,
                  fs_root_path: Option<&Path>)
                  -> Result<Self>
        where T: IntoUrl
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient { inner: new_hyper_client(&endpoint, fs_root_path)?,
                       proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
                       target_scheme: endpoint.scheme().to_string(),
                       endpoint,
                       user_agent_header: user_agent(product, version)? })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder { self.get_with_custom_url(path, |_| {}) }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder { self.head_with_custom_url(path, |_| {}) }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder { self.patch_with_custom_url(path, |_| {}) }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder { self.post_with_custom_url(path, |_| {}) }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder { self.put_with_custom_url(path, |_| {}) }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder { self.delete_with_custom_url(path, |_| {}) }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with('/') || path.starts_with('/') {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);

    let timeout_in_secs = match env::var("HAB_CLIENT_SOCKET_TIMEOUT") {
        Ok(t) => {
            match t.parse::<u64>() {
                Ok(n) => n,
                Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
            }
        }
        Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
    };
    debug!("Client socket timeout: {} secs", timeout_in_secs);

    let timeout = Some(Duration::from_secs(timeout_in_secs));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!("{}/{} ({}; {})",
                     product.trim(),
                     version.trim(),
                     PackageTarget::active_target(),
                     uname.release.trim().to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(&mut conn, fs_root_path)?;
    conn.set_options(options);
    conn.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH")?;

    if env::var("HAB_SSL_CERT_VERIFY_NONE").is_ok() {
        conn.set_verify(SSL_VERIFY_NONE);
    }

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

use std::{path::Path,
          time::Duration};

use habitat_core::{env,
                   package::PackageTarget,
                   util::sys};
use hyper::{client::{pool::{Config,
                            Pool},
                     Client as HyperClient,
                     IntoUrl,
                     RequestBuilder},
            header::UserAgent,
            http::h1::Http11Protocol,
            net::HttpsConnector};
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnector,
                   SslConnectorBuilder,
                   SslMethod,
                   SslOption,
                   SSL_OP_NO_COMPRESSION,
                   SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3,
                   SSL_VERIFY_NONE};
use url::Url;

use crate::{error::{Error,
                    Result},
            net::ProxyHttpsConnector,
            proxy::{proxy_unless_domain_exempted,
                    ProxyInfo},
            ssl};

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT_SEC: u64 = 300;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(endpoint: T,
                  product: &str,
                  version: &str,
                  fs_root_path: Option<&Path>)
                  -> Result<Self>
        where T: IntoUrl
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient { inner: new_hyper_client(&endpoint, fs_root_path)?,
                       proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
                       target_scheme: endpoint.scheme().to_string(),
                       endpoint,
                       user_agent_header: user_agent(product, version)? })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder { self.get_with_custom_url(path, |_| {}) }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder { self.head_with_custom_url(path, |_| {}) }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder { self.patch_with_custom_url(path, |_| {}) }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder { self.post_with_custom_url(path, |_| {}) }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder { self.put_with_custom_url(path, |_| {}) }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder { self.delete_with_custom_url(path, |_| {}) }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with('/') || path.starts_with('/') {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);

    let timeout_in_secs = match env::var("HAB_CLIENT_SOCKET_TIMEOUT") {
        Ok(t) => {
            match t.parse::<u64>() {
                Ok(n) => n,
                Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
            }
        }
        Err(_) => CLIENT_SOCKET_RW_TIMEOUT_SEC,
    };
    debug!("Client socket timeout: {} secs", timeout_in_secs);

    let timeout = Some(Duration::from_secs(timeout_in_secs));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!("{}/{} ({}; {})",
                     product.trim(),
                     version.trim(),
                     PackageTarget::active_target(),
                     uname.release.trim().to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(&mut conn, fs_root_path)?;
    conn.set_options(options);
    conn.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH")?;

    if env::var("HAB_SSL_CERT_VERIFY_NONE").is_ok() {
        conn.set_verify(SSL_VERIFY_NONE);
    }

    Ok(conn.build())
}
<|endoftext|>