<commit_before>use hyper::Client as HyperClient;
use hyper::client::Response as HyperResponse;
use hyper::header::{Headers, Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use std::io::Read;
use serde_json;

use Stream;
use types::Result;
use error::HesError;
use error::UserErrorKind;
use connection::ConnectionInfo;
use api::ESResolveLinkTos;

pub struct Reader<'a> {
    connection_info: &'a ConnectionInfo,
    http_client: &'a HyperClient,
}

impl<'a> Reader<'a> {
    pub fn new(connection_info: &'a ConnectionInfo, http_client: &'a HyperClient) -> Reader<'a> {
        Reader { connection_info: connection_info, http_client: http_client }
    }

    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let response = try!(self.http_client.get(&self.url(stream_name, start, count))
            .headers(build_headers(resolve_link_tos))
            .send());

        to_result(response)
    }

    fn url(&self, stream_name: &str, start: u32, count: u32) -> String {
        format!("http://{}:{}/streams/{}/{}/forward/{}?embed=body",
                self.connection_info.host,
                self.connection_info.port,
                stream_name,
                start,
                count)
    }
}

fn build_headers(resolve_link_tos: bool) -> Headers {
    let mut headers = Headers::new();
    headers.set(
        Accept(vec![
        qitem(Mime(TopLevel::Application,
                   SubLevel::Ext("vnd.eventstore.atom+json".to_owned()), vec![]))]));
    headers.set(ESResolveLinkTos(resolve_link_tos));

    headers
}

fn to_result(mut response: HyperResponse) -> Result<Stream> {
    match response.status {
        StatusCode::Ok => {
            let mut body = String::new();
            try!(response.read_to_string(&mut body));
            let stream: Stream = try!(serde_json::from_str(&body));
            Ok(stream)
        },
        StatusCode::NotFound => {
            Err(HesError::UserError(UserErrorKind::StreamNotFound))
        },
        StatusCode::Gone => {
            Err(HesError::UserError(UserErrorKind::StreamDeleted))
        },
        _ => {
            Err(HesError::UserError(UserErrorKind::UnexpectedResponse(response)))
        }
    }
}
<commit_msg>Do refactoring<commit_after>use hyper::Client as HyperClient;
use hyper::client::Response as HyperResponse;
use hyper::header::{Headers, Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use std::io::Read;
use serde_json;

use Stream;
use types::Result;
use error::HesError;
use error::UserErrorKind;
use connection::ConnectionInfo;
use api::ESResolveLinkTos;

pub struct Reader<'a> {
    connection_info: &'a ConnectionInfo,
    http_client: &'a HyperClient,
}

impl<'a> Reader<'a> {
    pub fn new(connection_info: &'a ConnectionInfo, http_client: &'a HyperClient) -> Reader<'a> {
        Reader { connection_info: connection_info, http_client: http_client }
    }

    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let response = try!(self.http_client.get(&self.url(stream_name, start, count))
            .headers(build_headers(resolve_link_tos))
            .send());

        to_result(response)
    }

    fn url(&self, stream_name: &str, start: u32, count: u32) -> String {
        format!("http://{}:{}/streams/{}/{}/forward/{}?embed=body",
                self.connection_info.host,
                self.connection_info.port,
                stream_name,
                start,
                count)
    }
}

fn build_headers(resolve_link_tos: bool) -> Headers {
    let mut headers = Headers::new();
    headers.set(
        Accept(vec![
        qitem(Mime(TopLevel::Application,
                   SubLevel::Ext("vnd.eventstore.atom+json".to_owned()), vec![]))]));
    headers.set(ESResolveLinkTos(resolve_link_tos));

    headers
}

fn to_result(response: HyperResponse) -> Result<Stream> {
    match response.status {
        StatusCode::Ok => read_stream(response),
        StatusCode::NotFound => Err(HesError::UserError(UserErrorKind::StreamNotFound)),
        StatusCode::Gone => Err(HesError::UserError(UserErrorKind::StreamDeleted)),
        _ => Err(HesError::UserError(UserErrorKind::UnexpectedResponse(response)))
    }
}

fn read_stream(mut response: HyperResponse) -> Result<Stream> {
    let mut body = String::new();
    try!(response.read_to_string(&mut body));
    let stream: Stream = try!(serde_json::from_str(&body));

    Ok(stream)
}
<|endoftext|>


<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use super::CFReleaser;
use crate::sys::{ffi, utils};
use crate::utils::to_cpath;
use crate::{Disk, DiskType};

use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, CFRelease};
use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef};
use core_foundation_sys::string as cfs;

use libc::{c_char, c_int, c_void, statfs};

use std::ffi::{OsStr, OsString};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::ptr;

fn to_path(mount_path: &[c_char]) -> Option<PathBuf> {
    let mut tmp = Vec::with_capacity(mount_path.len());
    for &c in mount_path {
        if c == 0 {
            break;
        }
        tmp.push(c as u8);
    }
    if tmp.is_empty() {
        None
    } else {
        let path = OsStr::from_bytes(&tmp);
        Some(PathBuf::from(path))
    }
}

pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
    if session.is_null() {
        return Vec::new();
    }
    unsafe {
        let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        let bufsize = count * mem::size_of::<libc::statfs>() as c_int;
        let mut disks = Vec::with_capacity(count as _);
        let count = libc::getfsstat(disks.as_mut_ptr(), bufsize, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        disks.set_len(count as _);
        disks
            .into_iter()
            .filter_map(|c_disk| {
                let mount_point = to_path(&c_disk.f_mntonname)?;
                let disk = CFReleaser::new(ffi::DADiskCreateFromBSDName(
                    kCFAllocatorDefault as _,
                    session,
                    c_disk.f_mntfromname.as_ptr(),
                ))?;
                let dict = CFReleaser::new(ffi::DADiskCopyDescription(disk.inner()))?;
                // Keeping this around in case one might want the list of the available
                // keys in "dict".
                // core_foundation_sys::base::CFShow(dict as _);
                let name = match get_str_value(dict.inner(), b"DAMediaName\0").map(OsString::from) {
                    Some(n) => n,
                    None => return None,
                };
                let removable =
                    get_bool_value(dict.inner(), b"DAMediaRemovable\0").unwrap_or(false);
                let ejectable =
                    get_bool_value(dict.inner(), b"DAMediaEjectable\0").unwrap_or(false);
                // This is very hackish but still better than nothing...
                let type_ = if let Some(model) = get_str_value(dict.inner(), b"DADeviceModel\0") {
                    if model.contains("SSD") {
                        DiskType::SSD
                    } else {
                        // We just assume by default that this is a HDD
                        DiskType::HDD
                    }
                } else {
                    DiskType::Unknown(-1)
                };

                new_disk(name, mount_point, type_, removable || ejectable)
            })
            .collect::<Vec<_>>()
    }
}

unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
    dict: CFDictionaryRef,
    key: &[u8],
    callback: F,
) -> Option<T> {
    let key = ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        cfs::kCFStringEncodingUTF8,
        kCFAllocatorNull as _,
    );
    let mut value = std::ptr::null();
    let ret = if CFDictionaryGetValueIfPresent(dict, key as _, &mut value) != 0 {
        callback(value)
    } else {
        None
    };
    CFRelease(key as _);
    ret
}

unsafe fn get_str_value(dict: CFDictionaryRef, key: &[u8]) -> Option<String> {
    get_dict_value(dict, key, |v| {
        let v = v as cfs::CFStringRef;

        let len_utf16 = cfs::CFStringGetLength(v);
        let len_bytes = len_utf16 as usize * 2; // Two bytes per UTF-16 codepoint.

        let v_ptr = cfs::CFStringGetCStringPtr(v, cfs::kCFStringEncodingUTF8);
        if v_ptr.is_null() {
            // Fallback on CFStringGetString to read the underlying bytes from the CFString.
            let mut buf = vec![0; len_bytes];
            let success = cfs::CFStringGetCString(
                v,
                buf.as_mut_ptr(),
                len_bytes as _,
                cfs::kCFStringEncodingUTF8,
            );

            if success != 0 {
                utils::vec_to_rust(buf)
            } else {
                None
            }
        } else {
            utils::cstr_to_rust_with_size(v_ptr, Some(len_bytes))
        }
    })
}

unsafe fn get_bool_value(dict: CFDictionaryRef, key: &[u8]) -> Option<bool> {
    get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
}

fn new_disk(
    name: OsString,
    mount_point: PathBuf,
    type_: DiskType,
    is_removable: bool,
) -> Option<Disk> {
    let mount_point_cpath = to_cpath(&mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            // APFS is "special" because its a snapshot-based filesystem, and modern
            // macOS devices take full advantage of this.
            //
            // By default, listing volumes with `statfs` can return both the root-level
            // "data" partition and any snapshots that exist. However, other than some flags and
            // reserved(undocumented) bytes, there is no difference between the OS boot snapshot
            // and the "data" partition.
            //
            // To avoid duplicating the number of disks (and therefore available space, etc), only return
            // a disk (which is really a partition with APFS) if it is the root of the filesystem.
            let is_root = stat.f_flags & libc::MNT_ROOTFS as u32 == 0;
            if !is_root {
                return None;
            }

            total_space = u64::from(stat.f_bsize).saturating_mul(stat.f_blocks);
            available_space = u64::from(stat.f_bsize).saturating_mul(stat.f_bavail);
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
        if total_space == 0 {
            return None;
        }
        Some(Disk {
            type_,
            name,
            file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
            mount_point,
            total_space,
            available_space,
            is_removable,
        })
    }
}
<commit_msg>Fix leak in apple disk retrieval<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use super::CFReleaser;
use crate::sys::{ffi, utils};
use crate::utils::to_cpath;
use crate::{Disk, DiskType};

use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, CFRelease};
use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef};
use core_foundation_sys::string as cfs;

use libc::{c_char, c_int, c_void, statfs};

use std::ffi::{OsStr, OsString};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::ptr;

fn to_path(mount_path: &[c_char]) -> Option<PathBuf> {
    let mut tmp = Vec::with_capacity(mount_path.len());
    for &c in mount_path {
        if c == 0 {
            break;
        }
        tmp.push(c as u8);
    }
    if tmp.is_empty() {
        None
    } else {
        let path = OsStr::from_bytes(&tmp);
        Some(PathBuf::from(path))
    }
}

pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
    if session.is_null() {
        return Vec::new();
    }
    unsafe {
        let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        let bufsize = count * mem::size_of::<libc::statfs>() as c_int;
        let mut disks = Vec::with_capacity(count as _);
        let count = libc::getfsstat(disks.as_mut_ptr(), bufsize, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        disks.set_len(count as _);
        disks
            .into_iter()
            .filter_map(|c_disk| {
                let mount_point = to_path(&c_disk.f_mntonname)?;
                let disk = CFReleaser::new(ffi::DADiskCreateFromBSDName(
                    kCFAllocatorDefault as _,
                    session,
                    c_disk.f_mntfromname.as_ptr(),
                ))?;
                let dict = CFReleaser::new(ffi::DADiskCopyDescription(disk.inner()))?;
                // Keeping this around in case one might want the list of the available
                // keys in "dict".
                // core_foundation_sys::base::CFShow(dict as _);
                let name = match get_str_value(dict.inner(), b"DAMediaName\0").map(OsString::from) {
                    Some(n) => n,
                    None => return None,
                };
                let removable =
                    get_bool_value(dict.inner(), b"DAMediaRemovable\0").unwrap_or(false);
                let ejectable =
                    get_bool_value(dict.inner(), b"DAMediaEjectable\0").unwrap_or(false);
                // This is very hackish but still better than nothing...
                let type_ = if let Some(model) = get_str_value(dict.inner(), b"DADeviceModel\0") {
                    if model.contains("SSD") {
                        DiskType::SSD
                    } else {
                        // We just assume by default that this is a HDD
                        DiskType::HDD
                    }
                } else {
                    DiskType::Unknown(-1)
                };

                new_disk(name, mount_point, type_, removable || ejectable)
            })
            .collect::<Vec<_>>()
    }
}

unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
    dict: CFDictionaryRef,
    key: &[u8],
    callback: F,
) -> Option<T> {
    match CFReleaser::new(ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        cfs::kCFStringEncodingUTF8,
        kCFAllocatorNull as _,
    )) {
        Some(c_key) => {
            let mut value = std::ptr::null();
            if CFDictionaryGetValueIfPresent(dict, c_key.inner() as _, &mut value) != 0 {
                callback(value)
            } else {
                None
            }
        }
        None => None,
    }
}

unsafe fn get_str_value(dict: CFDictionaryRef, key: &[u8]) -> Option<String> {
    get_dict_value(dict, key, |v| {
        let v = v as cfs::CFStringRef;

        let len_utf16 = cfs::CFStringGetLength(v);
        let len_bytes = len_utf16 as usize * 2; // Two bytes per UTF-16 codepoint.

        let v_ptr = cfs::CFStringGetCStringPtr(v, cfs::kCFStringEncodingUTF8);
        if v_ptr.is_null() {
            // Fallback on CFStringGetString to read the underlying bytes from the CFString.
            let mut buf = vec![0; len_bytes];
            let success = cfs::CFStringGetCString(
                v,
                buf.as_mut_ptr(),
                len_bytes as _,
                cfs::kCFStringEncodingUTF8,
            );

            if success != 0 {
                utils::vec_to_rust(buf)
            } else {
                None
            }
        } else {
            utils::cstr_to_rust_with_size(v_ptr, Some(len_bytes))
        }
    })
}

unsafe fn get_bool_value(dict: CFDictionaryRef, key: &[u8]) -> Option<bool> {
    get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
}

fn new_disk(
    name: OsString,
    mount_point: PathBuf,
    type_: DiskType,
    is_removable: bool,
) -> Option<Disk> {
    let mount_point_cpath = to_cpath(&mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            // APFS is "special" because its a snapshot-based filesystem, and modern
            // macOS devices take full advantage of this.
            //
            // By default, listing volumes with `statfs` can return both the root-level
            // "data" partition and any snapshots that exist. However, other than some flags and
            // reserved(undocumented) bytes, there is no difference between the OS boot snapshot
            // and the "data" partition.
            //
            // To avoid duplicating the number of disks (and therefore available space, etc), only return
            // a disk (which is really a partition with APFS) if it is the root of the filesystem.
            let is_root = stat.f_flags & libc::MNT_ROOTFS as u32 == 0;
            if !is_root {
                return None;
            }

            total_space = u64::from(stat.f_bsize).saturating_mul(stat.f_blocks);
            available_space = u64::from(stat.f_bsize).saturating_mul(stat.f_bavail);
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
        if total_space == 0 {
            return None;
        }
        Some(Disk {
            type_,
            name,
            file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
            mount_point,
            total_space,
            available_space,
            is_removable,
        })
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;

use store::Result;
use error::StoreErrorKind as SEK;
use libimagerror::into::IntoError;

pub trait TomlValueExt {
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool>;
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>>;
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>>;
    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>>;

    #[inline]
    fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
        self.insert_with_sep(spec, '.', v)
    }

    #[inline]
    fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
        self.set_with_sep(spec, '.', v)
    }

    #[inline]
    fn read(&self, spec: &str) -> Result<Option<Value>> {
        self.read_with_sep(spec, '.')
    }

    #[inline]
    fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
        self.delete_with_sep(spec, '.')
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum Token {
    Key(String),
    Index(usize),
}

impl TomlValueExt for Value {
    /**
     * Insert a header field by a string-spec
     *
     * ```ignore
     *  insert("something.in.a.field", Boolean(true));
     * ```
     *
     * If an array field was accessed which is _out of bounds_ of the array available, the element
     * is appended to the array.
     *
     * Inserts a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * Returns true if header field was set, false if there is already a value
     */
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool> {
        let (destination, value) = try!(setup(self, spec, sep));

        // There is already an value at this place
        if extract(value, &destination).is_ok() {
            return Ok(false);
        }

        match destination {
            // if the destination shall be an map key
            Token::Key(ref s) => match *value {
                /*
                 * Put it in there if we have a map
                 */
                Value::Table(ref mut t) => { t.insert(s.clone(), v); },

                /*
                 * Fail if there is no map here
                 */
                _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                /*
                 * Put it in there if we have an array
                 */
                Value::Array(ref mut a) => {
                    a.push(v); // push to the end of the array

                    // if the index is inside the array, we swap-remove the element at this
                    // index
                    if a.len() < i {
                        a.swap_remove(i);
                    }
                },

                /*
                 * Fail if there is no array here
                 */
                _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
            },
        }

        Ok(true)
    }

    /**
     * Set a header field by a string-spec
     *
     * ```ignore
     *  set("something.in.a.field", Boolean(true));
     * ```
     *
     * Sets a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is already a value at this place, this value will be overridden and the old value
     * will be returned
     */
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
        let (destination, value) = try!(setup(self, spec, sep));

        match destination {
            // if the destination shall be an map key->value
            Token::Key(ref s) => match *value {
                /*
                 * Put it in there if we have a map
                 */
                Value::Table(ref mut t) => {
                    debug!("Matched Key->Table");
                    return Ok(t.insert(s.clone(), v));
                }

                /*
                 * Fail if there is no map here
                 */
                _ => {
                    debug!("Matched Key->NON-Table");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                }
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                /*
                 * Put it in there if we have an array
                 */
                Value::Array(ref mut a) => {
                    debug!("Matched Index->Array");
                    a.push(v); // push to the end of the array

                    // if the index is inside the array, we swap-remove the element at this
                    // index
                    if a.len() > i {
                        debug!("Swap-Removing in Array {:?}[{:?}] <- {:?}", a, i, a[a.len()-1]);
                        return Ok(Some(a.swap_remove(i)));
                    }

                    debug!("Appended");
                    return Ok(None);
                },

                /*
                 * Fail if there is no array here
                 */
                _ => {
                    debug!("Matched Index->NON-Array");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                },
            },
        }
    }

    /**
     * Read a header field by a string-spec
     *
     * ```ignore
     *  let value = read("something.in.a.field");
     * ```
     *
     * Reads a Value in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is no a value at this place, None will be returned. This also holds true for Arrays
     * which are accessed at an index which is not yet there, even if the accessed index is much
     * larger than the array length.
     */
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = try!(tokenize(spec, splitchr));

        let mut header_clone = self.clone(); // we clone as READing is simpler this way
        // walk N-1 tokens
        match walk_header(&mut header_clone, tokens) {
            Err(e) => match e.err_type() {
                // We cannot find the header key, as there is no path to it
                SEK::HeaderKeyNotFound => Ok(None),
                _ => Err(e),
            },
            Ok(v) => Ok(Some(v.clone())),
        }
    }

    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let (destination, value) = try!(setup(self, spec, splitchr));

        match destination {
            // if the destination shall be an map key->value
            Token::Key(ref s) => match *value {
                Value::Table(ref mut t) => {
                    debug!("Matched Key->Table, removing {:?}", s);
                    return Ok(t.remove(s));
                },
                _ => {
                    debug!("Matched Key->NON-Table");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                }
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                // if the index is inside the array, we swap-remove the element at this
                // index
                Value::Array(ref mut a) => if a.len() > i {
                    debug!("Removing in Array {:?}[{:?}]", a, i);
                    return Ok(Some(a.remove(i)));
                } else {
                    return Ok(None);
                },
                _ => {
                    debug!("Matched Index->NON-Array");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                },
            },
        }
    }

}

fn setup<'a>(v: &'a mut Value, spec: &str, sep: char)
    -> Result<(Token, &'a mut Value)>
{
    let tokens       = try!(tokenize(spec, sep));
    debug!("tokens = {:?}", tokens);

    let destination  = try!(tokens.iter().last().cloned().ok_or(SEK::HeaderPathSyntaxError.into_error()));
    debug!("destination = {:?}", destination);

    let path_to_dest : Vec<Token> = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
    let value        = try!(walk_header(v, path_to_dest.clone())); // walk N-1 tokens

    debug!("walked value = {:?}", value);

    Ok((destination, value))
}

fn tokenize(spec: &str, splitchr: char) -> Result<Vec<Token>> {
    use std::str::FromStr;

    spec.split(splitchr)
        .map(|s| usize::from_str(s).map(Token::Index).or_else(|_| Ok(Token::Key(String::from(s)))))
        .collect()
}

fn walk_header(v: &mut Value, tokens: Vec<Token>) -> Result<&mut Value> {
    use std::vec::IntoIter;

    fn walk_iter<'a>(v: Result<&'a mut Value>, i: &mut IntoIter<Token>) -> Result<&'a mut Value> {
        let next = i.next();
        v.and_then(move |value| if let Some(token) = next {
            walk_iter(extract(value, &token), i)
        } else {
            Ok(value)
        })
    }

    walk_iter(Ok(v), &mut tokens.into_iter())
}

fn extract_from_table<'a>(v: &'a mut Value, s: &str) -> Result<&'a mut Value> {
    match *v {
        Value::Table(ref mut t) => t.get_mut(&s[..]).ok_or(SEK::HeaderKeyNotFound.into_error()),
        _ => Err(SEK::HeaderPathTypeFailure.into_error()),
    }
}

fn extract_from_array(v: &mut Value, i: usize) -> Result<&mut Value> {
    match *v {
        Value::Array(ref mut a) => if a.len() < i {
            Err(SEK::HeaderKeyNotFound.into_error())
        } else {
            Ok(&mut a[i])
        },
        _ => Err(SEK::HeaderPathTypeFailure.into_error()),
    }
}

fn extract<'a>(v: &'a mut Value, token: &Token) -> Result<&'a mut Value> {
    match *token {
        Token::Key(ref s)  => extract_from_table(v, s),
        Token::Index(i)    => extract_from_array(v, i),
    }
}

<commit_msg>Add helper trait for extracting from Array/Table<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;

use store::Result;
use error::StoreErrorKind as SEK;
use libimagerror::into::IntoError;

pub trait TomlValueExt {
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool>;
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>>;
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>>;
    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>>;

    #[inline]
    fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
        self.insert_with_sep(spec, '.', v)
    }

    #[inline]
    fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
        self.set_with_sep(spec, '.', v)
    }

    #[inline]
    fn read(&self, spec: &str) -> Result<Option<Value>> {
        self.read_with_sep(spec, '.')
    }

    #[inline]
    fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
        self.delete_with_sep(spec, '.')
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum Token {
    Key(String),
    Index(usize),
}

impl TomlValueExt for Value {
    /**
     * Insert a header field by a string-spec
     *
     * ```ignore
     *  insert("something.in.a.field", Boolean(true));
     * ```
     *
     * If an array field was accessed which is _out of bounds_ of the array available, the element
     * is appended to the array.
     *
     * Inserts a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * Returns true if header field was set, false if there is already a value
     */
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool> {
        let (destination, value) = try!(setup(self, spec, sep));

        // There is already an value at this place
        if value.extract(&destination).is_ok() {
            return Ok(false);
        }

        match destination {
            // if the destination shall be an map key
            Token::Key(ref s) => match *value {
                /*
                 * Put it in there if we have a map
                 */
                Value::Table(ref mut t) => { t.insert(s.clone(), v); },

                /*
                 * Fail if there is no map here
                 */
                _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                /*
                 * Put it in there if we have an array
                 */
                Value::Array(ref mut a) => {
                    a.push(v); // push to the end of the array

                    // if the index is inside the array, we swap-remove the element at this
                    // index
                    if a.len() < i {
                        a.swap_remove(i);
                    }
                },

                /*
                 * Fail if there is no array here
                 */
                _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
            },
        }

        Ok(true)
    }

    /**
     * Set a header field by a string-spec
     *
     * ```ignore
     *  set("something.in.a.field", Boolean(true));
     * ```
     *
     * Sets a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is already a value at this place, this value will be overridden and the old value
     * will be returned
     */
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
        let (destination, value) = try!(setup(self, spec, sep));

        match destination {
            // if the destination shall be an map key->value
            Token::Key(ref s) => match *value {
                /*
                 * Put it in there if we have a map
                 */
                Value::Table(ref mut t) => {
                    debug!("Matched Key->Table");
                    return Ok(t.insert(s.clone(), v));
                }

                /*
                 * Fail if there is no map here
                 */
                _ => {
                    debug!("Matched Key->NON-Table");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                }
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                /*
                 * Put it in there if we have an array
                 */
                Value::Array(ref mut a) => {
                    debug!("Matched Index->Array");
                    a.push(v); // push to the end of the array

                    // if the index is inside the array, we swap-remove the element at this
                    // index
                    if a.len() > i {
                        debug!("Swap-Removing in Array {:?}[{:?}] <- {:?}", a, i, a[a.len()-1]);
                        return Ok(Some(a.swap_remove(i)));
                    }

                    debug!("Appended");
                    return Ok(None);
                },

                /*
                 * Fail if there is no array here
                 */
                _ => {
                    debug!("Matched Index->NON-Array");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                },
            },
        }
    }

    /**
     * Read a header field by a string-spec
     *
     * ```ignore
     *  let value = read("something.in.a.field");
     * ```
     *
     * Reads a Value in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is no a value at this place, None will be returned. This also holds true for Arrays
     * which are accessed at an index which is not yet there, even if the accessed index is much
     * larger than the array length.
     */
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = try!(tokenize(spec, splitchr));

        let mut header_clone = self.clone(); // we clone as READing is simpler this way
        // walk N-1 tokens
        match walk_header(&mut header_clone, tokens) {
            Err(e) => match e.err_type() {
                // We cannot find the header key, as there is no path to it
                SEK::HeaderKeyNotFound => Ok(None),
                _ => Err(e),
            },
            Ok(v) => Ok(Some(v.clone())),
        }
    }

    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let (destination, value) = try!(setup(self, spec, splitchr));

        match destination {
            // if the destination shall be an map key->value
            Token::Key(ref s) => match *value {
                Value::Table(ref mut t) => {
                    debug!("Matched Key->Table, removing {:?}", s);
                    return Ok(t.remove(s));
                },
                _ => {
                    debug!("Matched Key->NON-Table");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                }
            },

            // if the destination shall be an array
            Token::Index(i) => match *value {

                // if the index is inside the array, we swap-remove the element at this
                // index
                Value::Array(ref mut a) => if a.len() > i {
                    debug!("Removing in Array {:?}[{:?}]", a, i);
                    return Ok(Some(a.remove(i)));
                } else {
                    return Ok(None);
                },
                _ => {
                    debug!("Matched Index->NON-Array");
                    return Err(SEK::HeaderPathTypeFailure.into_error());
                },
            },
        }
    }

}

fn setup<'a>(v: &'a mut Value, spec: &str, sep: char)
    -> Result<(Token, &'a mut Value)>
{
    let tokens       = try!(tokenize(spec, sep));
    debug!("tokens = {:?}", tokens);

    let destination  = try!(tokens.iter().last().cloned().ok_or(SEK::HeaderPathSyntaxError.into_error()));
    debug!("destination = {:?}", destination);

    let path_to_dest : Vec<Token> = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
    let value        = try!(walk_header(v, path_to_dest.clone())); // walk N-1 tokens

    debug!("walked value = {:?}", value);

    Ok((destination, value))
}

fn tokenize(spec: &str, splitchr: char) -> Result<Vec<Token>> {
    use std::str::FromStr;

    spec.split(splitchr)
        .map(|s| usize::from_str(s).map(Token::Index).or_else(|_| Ok(Token::Key(String::from(s)))))
        .collect()
}

fn walk_header(v: &mut Value, tokens: Vec<Token>) -> Result<&mut Value> {
    use std::vec::IntoIter;

    fn walk_iter<'a>(v: Result<&'a mut Value>, i: &mut IntoIter<Token>) -> Result<&'a mut Value> {
        let next = i.next();
        v.and_then(move |value| if let Some(token) = next {
            walk_iter(value.extract(&token), i)
        } else {
            Ok(value)
        })
    }

    walk_iter(Ok(v), &mut tokens.into_iter())
}

trait Extract {
    fn extract<'a>(&'a mut self, &Token) -> Result<&'a mut Self>;
}

impl Extract for Value {
    fn extract<'a>(&'a mut self, token: &Token) -> Result<&'a mut Value> {
        match *token {
            // on Token::Key extract from Value::Table
            Token::Key(ref s) => match *self {
                Value::Table(ref mut t) =>
                    t.get_mut(&s[..]).ok_or(SEK::HeaderKeyNotFound.into_error()),

                _ => Err(SEK::HeaderPathTypeFailure.into_error()),
            },

            // on Token::Index extract from Value::Array
            Token::Index(i) => match *self {
                Value::Array(ref mut a) => if a.len() < i {
                    Err(SEK::HeaderKeyNotFound.into_error())
                } else {
                    Ok(&mut a[i])
                },

                _ => Err(SEK::HeaderPathTypeFailure.into_error()),
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::ffi::OsStr;
use walkdir::WalkDir;

use std::fs::File;
use std::io::prelude::*;

// The maximum length allowed for stderr files.
//
// We limit this because small files are easier to deal with than bigger files.
const LIMIT: usize = 220;

pub fn check() {
    let stderr_files = stderr_files();
    let exceeding_files = exceeding_stderr_files(stderr_files).collect::<Vec<String>>();

    if !exceeding_files.is_empty() {
        eprintln!("Error: stderr files exceeding limit of {} lines:", LIMIT);
        for path in exceeding_files {
            println!("{}", path);
        }
        std::process::exit(1);
    }
}

fn exceeding_stderr_files(files: impl Iterator<Item = walkdir::DirEntry>) -> impl Iterator<Item = String> {
    files.filter_map(|file| {
        let path = file.path().to_str().expect("Could not convert path to str").to_string();
        let linecount = count_linenumbers(&path);
        if linecount > LIMIT {
            Some(path)
        } else {
            None
        }
    })
}

fn stderr_files() -> impl Iterator<Item = walkdir::DirEntry> {
    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
    WalkDir::new("../tests/ui")
        .into_iter()
        .filter_map(std::result::Result::ok)
        .filter(|f| f.path().extension() == Some(OsStr::new("stderr")))
}

#[must_use]
fn count_linenumbers(filepath: &str) -> usize {
    if let Ok(mut file) = File::open(filepath) {
        let mut content = String::new();
        file.read_to_string(&mut content).expect("Failed to read file?");
        content.lines().count()
    } else {
        0
    }
}
<commit_msg>Decrease line length limit for stderrs<commit_after>use std::ffi::OsStr;
use walkdir::WalkDir;

use std::fs::File;
use std::io::prelude::*;

// The maximum length allowed for stderr files.
//
// We limit this because small files are easier to deal with than bigger files.
const LIMIT: usize = 200;

pub fn check() {
    let stderr_files = stderr_files();
    let exceeding_files = exceeding_stderr_files(stderr_files).collect::<Vec<String>>();

    if !exceeding_files.is_empty() {
        eprintln!("Error: stderr files exceeding limit of {} lines:", LIMIT);
        for path in exceeding_files {
            println!("{}", path);
        }
        std::process::exit(1);
    }
}

fn exceeding_stderr_files(files: impl Iterator<Item = walkdir::DirEntry>) -> impl Iterator<Item = String> {
    files.filter_map(|file| {
        let path = file.path().to_str().expect("Could not convert path to str").to_string();
        let linecount = count_linenumbers(&path);
        if linecount > LIMIT {
            Some(path)
        } else {
            None
        }
    })
}

fn stderr_files() -> impl Iterator<Item = walkdir::DirEntry> {
    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
    WalkDir::new("../tests/ui")
        .into_iter()
        .filter_map(std::result::Result::ok)
        .filter(|f| f.path().extension() == Some(OsStr::new("stderr")))
}

#[must_use]
fn count_linenumbers(filepath: &str) -> usize {
    if let Ok(mut file) = File::open(filepath) {
        let mut content = String::new();
        file.read_to_string(&mut content).expect("Failed to read file?");
        content.lines().count()
    } else {
        0
    }
}
<|endoftext|>


<commit_before>//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
#![allow(nonstandard_style)]

use std::io;
use std::path::Path;

cfg_if! {
    if #[cfg(unix)] {
        use std::ffi::{CString, OsStr};
        use std::mem;
        use std::os::unix::prelude::*;

        #[derive(Debug)]
        pub struct Lock {
            fd: libc::c_int,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                let os: &OsStr = p.as_ref();
                let buf = CString::new(os.as_bytes()).unwrap();
                let open_flags = if create {
                    libc::O_RDWR | libc::O_CREAT
                } else {
                    libc::O_RDWR
                };

                let fd = unsafe {
                    libc::open(buf.as_ptr(), open_flags,
                               libc::S_IRWXU as libc::c_int)
                };

                if fd < 0 {
                    return Err(io::Error::last_os_error());
                }

                let lock_type = if exclusive {
                    libc::F_WRLCK
                } else {
                    libc::F_RDLCK
                };

                let mut flock: libc::flock = unsafe { mem::zeroed() };
                flock.l_type = lock_type as libc::c_short;
                flock.l_whence = libc::SEEK_SET as libc::c_short;
                flock.l_start = 0;
                flock.l_len = 0;

                let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK };
                let ret = unsafe {
                    libc::fcntl(fd, cmd, &flock)
                };
                if ret == -1 {
                    let err = io::Error::last_os_error();
                    unsafe { libc::close(fd); }
                    Err(err)
                } else {
                    Ok(Lock { fd })
                }
            }
        }

        impl Drop for Lock {
            fn drop(&mut self) {
                let mut flock: libc::flock = unsafe { mem::zeroed() };
                flock.l_type = libc::F_UNLCK as libc::c_short;
                flock.l_whence = libc::SEEK_SET as libc::c_short;
                flock.l_start = 0;
                flock.l_len = 0;

                unsafe {
                    libc::fcntl(self.fd, libc::F_SETLK, &flock);
                    libc::close(self.fd);
                }
            }
        }
    } else if #[cfg(windows)] {
        use std::mem;
        use std::os::windows::prelude::*;
        use std::fs::{File, OpenOptions};

        use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
        use winapi::um::fileapi::LockFileEx;
        use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                assert!(p.parent().unwrap().exists(),
                    "Parent directory of lock-file must exist: {}",
                    p.display());

                let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

                let mut open_options = OpenOptions::new();
                open_options.read(true)
                            .share_mode(share_mode);

                if create {
                    open_options.create(true)
                                .write(true);
                }

                debug!("attempting to open lock file `{}`", p.display());
                let file = match open_options.open(p) {
                    Ok(file) => {
                        debug!("lock file opened successfully");
                        file
                    }
                    Err(err) => {
                        debug!("error opening lock file: {}", err);
                        return Err(err)
                    }
                };

                let ret = unsafe {
                    let mut overlapped: OVERLAPPED = mem::zeroed();

                    let mut dwFlags = 0;
                    if !wait {
                        dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                    }

                    if exclusive {
                        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                    }

                    debug!("attempting to acquire lock on lock file `{}`",
                           p.display());
                    LockFileEx(file.as_raw_handle(),
                               dwFlags,
                               0,
                               0xFFFF_FFFF,
                               0xFFFF_FFFF,
                               &mut overlapped)
                };
                if ret == 0 {
                    let err = io::Error::last_os_error();
                    debug!("failed acquiring file lock: {}", err);
                    Err(err)
                } else {
                    debug!("successfully acquired lock");
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl on the Windows: The file is unlocked
        // automatically when it's closed.
    } else {
        #[derive(Debug)]
        pub struct Lock(());

        impl Lock {
            pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool)
                -> io::Result<Lock>
            {
                let msg = "file locks not supported on this platform";
                Err(io::Error::new(io::ErrorKind::Other, msg))
            }
        }
    }
}
<commit_msg>Replace fcntl-based file lock with flock<commit_after>//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
#![allow(nonstandard_style)]

use std::io;
use std::path::Path;

cfg_if! {
    if #[cfg(unix)] {
        use std::os::unix::prelude::*;
        use std::fs::{File, OpenOptions};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                let file = OpenOptions::new()
                           .read(true)
                           .write(true)
                           .create(create)
                           .mode(libc::S_IRWXU as u32)
                           .open(p)?;

                let mut operation = if exclusive {
                    libc::LOCK_EX
                } else {
                    libc::LOCK_SH
                };
                if !wait {
                    operation |= libc::LOCK_NB
                }

                let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };
                if ret == -1 {
                    let err = io::Error::last_os_error();
                    Err(err)
                } else {
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by
        // `flock` is associated with the file descriptor and closing the file release it
        // automatically.
    } else if #[cfg(windows)] {
        use std::mem;
        use std::os::windows::prelude::*;
        use std::fs::{File, OpenOptions};

        use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
        use winapi::um::fileapi::LockFileEx;
        use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                assert!(p.parent().unwrap().exists(),
                    "Parent directory of lock-file must exist: {}",
                    p.display());

                let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

                let mut open_options = OpenOptions::new();
                open_options.read(true)
                            .share_mode(share_mode);

                if create {
                    open_options.create(true)
                                .write(true);
                }

                debug!("attempting to open lock file `{}`", p.display());
                let file = match open_options.open(p) {
                    Ok(file) => {
                        debug!("lock file opened successfully");
                        file
                    }
                    Err(err) => {
                        debug!("error opening lock file: {}", err);
                        return Err(err)
                    }
                };

                let ret = unsafe {
                    let mut overlapped: OVERLAPPED = mem::zeroed();

                    let mut dwFlags = 0;
                    if !wait {
                        dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                    }

                    if exclusive {
                        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                    }

                    debug!("attempting to acquire lock on lock file `{}`",
                           p.display());
                    LockFileEx(file.as_raw_handle(),
                               dwFlags,
                               0,
                               0xFFFF_FFFF,
                               0xFFFF_FFFF,
                               &mut overlapped)
                };
                if ret == 0 {
                    let err = io::Error::last_os_error();
                    debug!("failed acquiring file lock: {}", err);
                    Err(err)
                } else {
                    debug!("successfully acquired lock");
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl on the Windows: The file is unlocked
        // automatically when it's closed.
    } else {
        #[derive(Debug)]
        pub struct Lock(());

        impl Lock {
            pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool)
                -> io::Result<Lock>
            {
                let msg = "file locks not supported on this platform";
                Err(io::Error::new(io::ErrorKind::Other, msg))
            }
        }
    }
}
<|endoftext|>


<commit_before>use anyhow::{anyhow, Result};
use byteorder::{ByteOrder, LittleEndian};
use homectl_types::device::{Device, DeviceColor, DeviceId, DeviceState, Light};
use homectl_types::integration::IntegrationId;
use num_traits::pow::Pow;
use palette::Hsv;
use std::net::SocketAddr;

#[derive(Clone)]
pub struct LifxState {
    pub hue: u16,
    pub sat: u16,
    pub bri: u16,
    pub power: u16,
    pub label: String,
    pub addr: SocketAddr,
    pub transition: Option<u16>,
}

#[derive(Clone)]
pub enum LifxMsg {
    Get(SocketAddr),
    SetColor(LifxState),
    State(LifxState),
    SetPower(LifxState),
    Unknown,
}

pub fn lifx_msg_type_to_u16(msg_type: LifxMsg) -> u16 {
    match msg_type {
        LifxMsg::Get(_) => 101,
        LifxMsg::SetColor(_) => 102,
        LifxMsg::State(_) => 107,
        LifxMsg::SetPower(_) => 117,
        LifxMsg::Unknown => panic!("Cannot convert LifxMsg::Unknown to u16"),
    }
}

fn mk_lifx_msg_payload(lifx_msg: LifxMsg) -> Option<Vec<u8>> {
    match lifx_msg {
        LifxMsg::SetPower(state) => {
            let mut buf: [u8; 16 + 32] = [0; 16 + 32];

            LittleEndian::write_u16(&mut buf, state.power);

            if let Some(t) = state.transition {
                LittleEndian::write_u16(&mut buf[2..], t)
            }

            Some(buf.to_vec())
        }
        LifxMsg::SetColor(state) => {
            let mut buf: [u8; 8 + 16 * 4 + 32] = [0; 8 + 16 * 4 + 32];

            LittleEndian::write_u16(&mut buf[1..], state.hue);
            LittleEndian::write_u16(&mut buf[3..], state.sat);
            LittleEndian::write_u16(&mut buf[5..], state.bri);
            LittleEndian::write_u16(&mut buf[7..], 6500); // lifx requires this weird color temperature parameter?

            if let Some(t) = state.transition {
                LittleEndian::write_u16(&mut buf[9..], t)
            }

            Some(buf.to_vec())
        }
        _ => None,
    }
}

pub fn mk_lifx_udp_msg(lifx_msg: LifxMsg) -> Vec<u8> {
    // frame
    // https://lan.developer.lifx.com/docs/header-description#frame
    let mut frame: [u8; 8] = [0; 8];
    let protocol = 1024;
    let origin = 0;
    let tagged = 1;
    let addressable = 1;

    LittleEndian::write_u16(&mut frame, 0); // size to be filled in later
    LittleEndian::write_u16(
        &mut frame[2..],
        protocol | (origin << 14) | (tagged << 13) | (addressable << 12),
    );
    LittleEndian::write_u16(&mut frame[1..], 4);

    // frame address
    // https://lan.developer.lifx.com/docs/header-description#frame-address
    let mut frame_address: [u8; 16] = [0; 16];
    let ack_required = 0;
    let res_required = match lifx_msg {
        LifxMsg::Get(_) => 1,
        _ => 0,
    };

    frame_address[14] = (ack_required << 1) | res_required;

    // protocol header
    // https://lan.developer.lifx.com/docs/header-description#protocol-header
    let mut protocol_header: [u8; 12] = [0; 12];
    let msg_type = lifx_msg_type_to_u16(lifx_msg.clone());
    LittleEndian::write_u16(&mut protocol_header[8..], msg_type);

    let payload = mk_lifx_msg_payload(lifx_msg);
    let payload_size = payload.clone().map(|p| p.len()).unwrap_or(0);
    let msg_size = frame.len() + frame_address.len() + protocol_header.len() + payload_size;

    // we now know the total size - write it into the beginning of the frame header
    LittleEndian::write_u16(&mut frame, msg_size as u16);

    let mut msg: Vec<u8> = vec![];
    msg.append(&mut frame.to_vec());
    msg.append(&mut frame_address.to_vec());
    msg.append(&mut protocol_header.to_vec());

    if let Some(payload) = payload {
        msg.append(&mut payload.to_vec());
    };

    msg
}

pub fn read_lifx_msg(buf: &[u8], addr: SocketAddr) -> LifxMsg {
    let msg_type = LittleEndian::read_u16(&buf[32..]);
    let payload = &buf[36..];

    match msg_type {
        107 => {
            // State (107) message, response to Get (101)
            // https://lan.developer.lifx.com/docs/light-messages#section-state-107

            let hue = LittleEndian::read_u16(payload);
            let sat = LittleEndian::read_u16(&payload[2..]);
            let bri = LittleEndian::read_u16(&payload[4..]);

            let power = LittleEndian::read_u16(&payload[10..]);

            let label = std::str::from_utf8(&payload[12..(12 + 32)])
                .unwrap_or("Unknown")
                .to_owned()
                .replace("\0", "");

            let state = LifxState {
                hue,
                sat,
                bri,
                power,
                label,
                addr,
                transition: None,
            };

            LifxMsg::State(state)
        }
        _ => LifxMsg::Unknown,
    }
}

pub fn from_lifx_state(lifx_state: LifxState, integration_id: IntegrationId) -> Device {
    let hue = from_lifx_hue((f32::from(lifx_state.hue) / 65535.0) * 360.0);
    let sat = f32::from(lifx_state.sat) / 65535.0;
    let bri = f32::from(lifx_state.bri) / 65535.0;

    let power = lifx_state.power == 65535;

    let color = Hsv::new(hue, sat, bri);

    let transition_ms = lifx_state.transition.map(|transition| transition as u64);

    let state = DeviceState::Light(Light::new(
        power,
        None,
        Some(DeviceColor::Color(color)),
        transition_ms,
    ));

    Device {
        id: DeviceId::new(&lifx_state.addr.to_string()),
        name: lifx_state.label,
        integration_id,
        scene: None,
        state,
    }
}

pub fn to_lifx_state(device: &Device) -> Result<LifxState> {
    let light_state = match device.state.clone() {
        DeviceState::Light(Light {
            brightness,
            color,
            power,
            transition_ms,
        }) => Ok(Light {
            power,
            brightness,
            color,
            transition_ms,
        }),
        _ => Err(anyhow!("Unsupported device state")),
    }?;

    match light_state.color {
        Some(DeviceColor::Color(color)) => {
            let hue =
                ((to_lifx_hue(color.hue.to_positive_degrees()) / 360.0) * 65535.0).floor() as u16;
            let sat = (color.saturation * 65535.0).floor() as u16;
            let bri =
                (light_state.brightness.unwrap_or(1.0) * color.value * 65535.0).floor() as u16;
            let transition = light_state
                .transition_ms
                .map(|transition_ms| transition_ms as u16);

            let power = if light_state.power { 65535 } else { 0 };

            Ok(LifxState {
                hue,
                sat,
                bri,
                power,
                label: device.name.clone(),
                addr: device.id.to_string().parse()?,
                transition,
            })
        }
        Some(DeviceColor::Cct(_)) => todo!(),
        None => todo!(),
    }
}

// NOTE: this is complete trial-and-error, but seems to produce a wider range of
// yellow hues which matches my other HA systems better.
pub fn to_lifx_hue(h: f32) -> f32 {
    if h > 0.0 && h < 60.0 {
        let p = h / 60.0;
        f32::pow(p, 1.0 / 2.0) * 60.0
    } else {
        h
    }
}

pub fn from_lifx_hue(h: f32) -> f32 {
    if h > 0.0 && h < 60.0 {
        let p = h / 60.0;
        f32::pow(p, 2.0 / 1.0) * 60.0
    } else {
        h
    }
}
<commit_msg>Don't panic if encountering color temp values in Lifx integration<commit_after>use anyhow::{anyhow, Result};
use byteorder::{ByteOrder, LittleEndian};
use homectl_types::device::{Device, DeviceColor, DeviceId, DeviceState, Light};
use homectl_types::integration::IntegrationId;
use num_traits::pow::Pow;
use palette::Hsv;
use std::net::SocketAddr;

#[derive(Clone)]
pub struct LifxState {
    pub hue: u16,
    pub sat: u16,
    pub bri: u16,
    pub power: u16,
    pub label: String,
    pub addr: SocketAddr,
    pub transition: Option<u16>,
}

#[derive(Clone)]
pub enum LifxMsg {
    Get(SocketAddr),
    SetColor(LifxState),
    State(LifxState),
    SetPower(LifxState),
    Unknown,
}

pub fn lifx_msg_type_to_u16(msg_type: LifxMsg) -> u16 {
    match msg_type {
        LifxMsg::Get(_) => 101,
        LifxMsg::SetColor(_) => 102,
        LifxMsg::State(_) => 107,
        LifxMsg::SetPower(_) => 117,
        LifxMsg::Unknown => panic!("Cannot convert LifxMsg::Unknown to u16"),
    }
}

fn mk_lifx_msg_payload(lifx_msg: LifxMsg) -> Option<Vec<u8>> {
    match lifx_msg {
        LifxMsg::SetPower(state) => {
            let mut buf: [u8; 16 + 32] = [0; 16 + 32];

            LittleEndian::write_u16(&mut buf, state.power);

            if let Some(t) = state.transition {
                LittleEndian::write_u16(&mut buf[2..], t)
            }

            Some(buf.to_vec())
        }
        LifxMsg::SetColor(state) => {
            let mut buf: [u8; 8 + 16 * 4 + 32] = [0; 8 + 16 * 4 + 32];

            LittleEndian::write_u16(&mut buf[1..], state.hue);
            LittleEndian::write_u16(&mut buf[3..], state.sat);
            LittleEndian::write_u16(&mut buf[5..], state.bri);
            LittleEndian::write_u16(&mut buf[7..], 6500); // lifx requires this weird color temperature parameter?

            if let Some(t) = state.transition {
                LittleEndian::write_u16(&mut buf[9..], t)
            }

            Some(buf.to_vec())
        }
        _ => None,
    }
}

pub fn mk_lifx_udp_msg(lifx_msg: LifxMsg) -> Vec<u8> {
    // frame
    // https://lan.developer.lifx.com/docs/header-description#frame
    let mut frame: [u8; 8] = [0; 8];
    let protocol = 1024;
    let origin = 0;
    let tagged = 1;
    let addressable = 1;

    LittleEndian::write_u16(&mut frame, 0); // size to be filled in later
    LittleEndian::write_u16(
        &mut frame[2..],
        protocol | (origin << 14) | (tagged << 13) | (addressable << 12),
    );
    LittleEndian::write_u16(&mut frame[1..], 4);

    // frame address
    // https://lan.developer.lifx.com/docs/header-description#frame-address
    let mut frame_address: [u8; 16] = [0; 16];
    let ack_required = 0;
    let res_required = match lifx_msg {
        LifxMsg::Get(_) => 1,
        _ => 0,
    };

    frame_address[14] = (ack_required << 1) | res_required;

    // protocol header
    // https://lan.developer.lifx.com/docs/header-description#protocol-header
    let mut protocol_header: [u8; 12] = [0; 12];
    let msg_type = lifx_msg_type_to_u16(lifx_msg.clone());
    LittleEndian::write_u16(&mut protocol_header[8..], msg_type);

    let payload = mk_lifx_msg_payload(lifx_msg);
    let payload_size = payload.clone().map(|p| p.len()).unwrap_or(0);
    let msg_size = frame.len() + frame_address.len() + protocol_header.len() + payload_size;

    // we now know the total size - write it into the beginning of the frame header
    LittleEndian::write_u16(&mut frame, msg_size as u16);

    let mut msg: Vec<u8> = vec![];
    msg.append(&mut frame.to_vec());
    msg.append(&mut frame_address.to_vec());
    msg.append(&mut protocol_header.to_vec());

    if let Some(payload) = payload {
        msg.append(&mut payload.to_vec());
    };

    msg
}

pub fn read_lifx_msg(buf: &[u8], addr: SocketAddr) -> LifxMsg {
    let msg_type = LittleEndian::read_u16(&buf[32..]);
    let payload = &buf[36..];

    match msg_type {
        107 => {
            // State (107) message, response to Get (101)
            // https://lan.developer.lifx.com/docs/light-messages#section-state-107

            let hue = LittleEndian::read_u16(payload);
            let sat = LittleEndian::read_u16(&payload[2..]);
            let bri = LittleEndian::read_u16(&payload[4..]);

            let power = LittleEndian::read_u16(&payload[10..]);

            let label = std::str::from_utf8(&payload[12..(12 + 32)])
                .unwrap_or("Unknown")
                .to_owned()
                .replace("\0", "");

            let state = LifxState {
                hue,
                sat,
                bri,
                power,
                label,
                addr,
                transition: None,
            };

            LifxMsg::State(state)
        }
        _ => LifxMsg::Unknown,
    }
}

pub fn from_lifx_state(lifx_state: LifxState, integration_id: IntegrationId) -> Device {
    let hue = from_lifx_hue((f32::from(lifx_state.hue) / 65535.0) * 360.0);
    let sat = f32::from(lifx_state.sat) / 65535.0;
    let bri = f32::from(lifx_state.bri) / 65535.0;

    let power = lifx_state.power == 65535;

    let color = Hsv::new(hue, sat, bri);

    let transition_ms = lifx_state.transition.map(|transition| transition as u64);

    let state = DeviceState::Light(Light::new(
        power,
        None,
        Some(DeviceColor::Color(color)),
        transition_ms,
    ));

    Device {
        id: DeviceId::new(&lifx_state.addr.to_string()),
        name: lifx_state.label,
        integration_id,
        scene: None,
        state,
    }
}

pub fn to_lifx_state(device: &Device) -> Result<LifxState> {
    let light_state = match device.state.clone() {
        DeviceState::Light(Light {
            brightness,
            color,
            power,
            transition_ms,
        }) => Ok(Light {
            power,
            brightness,
            color,
            transition_ms,
        }),
        _ => Err(anyhow!("Unsupported device state")),
    }?;

    match light_state.color {
        Some(DeviceColor::Color(color)) => {
            let hue =
                ((to_lifx_hue(color.hue.to_positive_degrees()) / 360.0) * 65535.0).floor() as u16;
            let sat = (color.saturation * 65535.0).floor() as u16;
            let bri =
                (light_state.brightness.unwrap_or(1.0) * color.value * 65535.0).floor() as u16;
            let transition = light_state
                .transition_ms
                .map(|transition_ms| transition_ms as u16);

            let power = if light_state.power { 65535 } else { 0 };

            Ok(LifxState {
                hue,
                sat,
                bri,
                power,
                label: device.name.clone(),
                addr: device.id.to_string().parse()?,
                transition,
            })
        }
        Some(DeviceColor::Cct(_)) => Err(anyhow!("Support for Lifx color temperature mode not implemented")),
        None => Err(anyhow!("Support for Lifx devices without color not implemented")),
    }
}

// NOTE: this is complete trial-and-error, but seems to produce a wider range of
// yellow hues which matches my other HA systems better.
pub fn to_lifx_hue(h: f32) -> f32 {
    if h > 0.0 && h < 60.0 {
        let p = h / 60.0;
        f32::pow(p, 1.0 / 2.0) * 60.0
    } else {
        h
    }
}

pub fn from_lifx_hue(h: f32) -> f32 {
    if h > 0.0 && h < 60.0 {
        let p = h / 60.0;
        f32::pow(p, 2.0 / 1.0) * 60.0
    } else {
        h
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub struct Symbol {
    ns: Option<String>,
    name: String,
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref ns) = self.ns {
            write!(f, "{}/{}", ns, self.name)
        } else {
            write!(f, "{}", self.name)
        }
    }
}
<commit_msg>Add constructor and accessor methods to Symbol struct<commit_after>use std::fmt;

#[derive(Debug, PartialEq, Clone)]
pub struct Symbol {
    ns: Option<String>,
    name: String,
}

impl Symbol {
    fn new(ns: Option<String>, name: String) -> Symbol {
        Symbol {
            ns: ns,
            name: name,
        }
    }

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

    fn ns(&self) -> Option<&String> {
        self.ns.as_ref()
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref ns) = self.ns {
            write!(f, "{}/{}", ns, self.name)
        } else {
            write!(f, "{}", self.name)
        }
    }
}
<|endoftext|>


<commit_before>use bit_vec::BitVec;
use num::pow;
use std::slice::Iter;
use lfsr;

#[derive(PartialEq, Debug)]
pub struct GaloisLFSR {
    state: BitVec,
    mask: BitVec,
}

impl GaloisLFSR {
    pub fn from_iter(iter: Iter<usize>) -> Self {
        let &len = iter.clone().max().unwrap();

        let state = BitVec::from_elem(len, true);
        let mut mask = BitVec::from_elem(len, false);

        for &t in iter {
            if t < len {
                // Set the _counterpart_ mask index
                mask.set(len - t, true);
            }
        }
        mask.set(0, true);

        GaloisLFSR { state: state, mask: mask, }
    }

    fn shift(&mut self) {
        lfsr::shift(&mut self.state);
    }

    fn feedback(&mut self) {
        for (i, mb) in self.mask.iter().enumerate() {
            let sb = self.state[i];
            self.state.set(i, mb ^ sb);
        }
    }

    pub fn iter(&mut self) -> LFSRIter {
        LFSRIter { lfsr: self }
    }

    fn lsbyte(&self) -> u8 {
        self.state
            .iter()
            .take(8)
            .enumerate()
            .fold(0, |acc, (i, b)| acc + (b as u8) * pow(2, i))
    }

    pub fn bytes(&mut self) -> LFSRByteIter {
        if self.state.len() < 8 {
            panic!("LFSR must have length at least 8 to iterate bytes");
        }

        LFSRByteIter { lfsr: self }
    }
}

impl lfsr::LFSR for GaloisLFSR {
    fn output(&self) -> bool {
        let len = self.state.len();
        self.state[len - 1]
    }

    fn step(&mut self) {
        let output = self.output();

        self.shift();

        if output {
            self.feedback();
        }
    }
}

pub struct LFSRIter<'a> {
    lfsr: &'a mut GaloisLFSR,
}

impl<'a> Iterator for LFSRIter<'a> {
    type Item = bool;

    fn next(&mut self) -> Option<bool> {
        use lfsr::LFSR;

        let o = self.lfsr.output();
        self.lfsr.step();
        Some(o)
    }
}

pub struct LFSRByteIter<'a> {
    lfsr: &'a mut GaloisLFSR,
}

impl<'a> Iterator for LFSRByteIter<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        use lfsr::LFSR;

        let byte = self.lfsr.lsbyte();
        self.lfsr.step();
        Some(byte)
    }
}
<commit_msg>Newline nit<commit_after>use bit_vec::BitVec;
use num::pow;
use std::slice::Iter;
use lfsr;

#[derive(PartialEq, Debug)]
pub struct GaloisLFSR {
    state: BitVec,
    mask: BitVec,
}

impl GaloisLFSR {
    pub fn from_iter(iter: Iter<usize>) -> Self {
        let &len = iter.clone().max().unwrap();

        let state = BitVec::from_elem(len, true);
        let mut mask = BitVec::from_elem(len, false);

        for &t in iter {
            if t < len {
                // Set the _counterpart_ mask index
                mask.set(len - t, true);
            }
        }
        mask.set(0, true);

        GaloisLFSR { state: state, mask: mask, }
    }

    fn shift(&mut self) {
        lfsr::shift(&mut self.state);
    }

    fn feedback(&mut self) {
        for (i, mb) in self.mask.iter().enumerate() {
            let sb = self.state[i];
            self.state.set(i, mb ^ sb);
        }
    }

    pub fn iter(&mut self) -> LFSRIter {
        LFSRIter { lfsr: self }
    }

    fn lsbyte(&self) -> u8 {
        self.state
            .iter()
            .take(8)
            .enumerate()
            .fold(0, |acc, (i, b)| acc + (b as u8) * pow(2, i))
    }

    pub fn bytes(&mut self) -> LFSRByteIter {
        if self.state.len() < 8 {
            panic!("LFSR must have length at least 8 to iterate bytes");
        }

        LFSRByteIter { lfsr: self }
    }
}

impl lfsr::LFSR for GaloisLFSR {
    fn output(&self) -> bool {
        let len = self.state.len();
        self.state[len - 1]
    }

    fn step(&mut self) {
        let output = self.output();

        self.shift();

        if output {
            self.feedback();
        }
    }
}

pub struct LFSRIter<'a> {
    lfsr: &'a mut GaloisLFSR,
}

impl<'a> Iterator for LFSRIter<'a> {
    type Item = bool;

    fn next(&mut self) -> Option<bool> {
        use lfsr::LFSR;

        let o = self.lfsr.output();
        self.lfsr.step();
        Some(o)
    }
}

pub struct LFSRByteIter<'a> {
    lfsr: &'a mut GaloisLFSR,
}

impl<'a> Iterator for LFSRByteIter<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        use lfsr::LFSR;

        let byte = self.lfsr.lsbyte();
        self.lfsr.step();
        Some(byte)
    }
}
<|endoftext|>