<commit_before>// Copyright (c) 2017 The Noise-rs Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. All files in the
// project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

//! An ultra-light private math library to make our short lives easier as we
//! implement super-complex noise stuff.

use num_traits::{self, Float, NumCast};
use std::ops::{Add, Mul, Sub};

/// Cast a numeric type without having to unwrap - we don't expect any overflow
/// errors...
pub fn cast<T: NumCast, U: NumCast>(x: T) -> U {
    num_traits::cast(x).unwrap()
}

/// Raises the number to the power of `4`
pub fn pow4<T: Float>(x: T) -> T {
    x * x * x * x
}

/// A 2-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point2<T> = [T; 2];

/// A 3-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point3<T> = [T; 3];

/// A 4-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point4<T> = [T; 4];

/// A 2-dimensional vector, for internal use.
pub type Vector2<T> = [T; 2];
/// A 3-dimensional vector, for internal use.
pub type Vector3<T> = [T; 3];
/// A 4-dimensional vector, for internal use.
pub type Vector4<T> = [T; 4];

pub fn map2<T, U, F>(a: Vector2<T>, f: F) -> Vector2<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay) = (a[0], a[1]);
    [f(ax), f(ay)]
}
pub fn map3<T, U, F>(a: Vector3<T>, f: F) -> Vector3<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    [f(ax), f(ay), f(az)]
}
pub fn map4<T, U, F>(a: Vector4<T>, f: F) -> Vector4<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    [f(ax), f(ay), f(az), f(aw)]
}

pub fn zip_with2<T, U, V, F>(a: Vector2<T>, b: Vector2<U>, f: F) -> Vector2<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay) = (a[0], a[1]);
    let (bx, by) = (b[0], b[1]);
    [f(ax, bx), f(ay, by)]
}
pub fn zip_with3<T, U, V, F>(a: Vector3<T>, b: Vector3<U>, f: F) -> Vector3<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    let (bx, by, bz) = (b[0], b[1], b[2]);
    [f(ax, bx), f(ay, by), f(az, bz)]
}
pub fn zip_with4<T, U, V, F>(a: Vector4<T>, b: Vector4<U>, f: F) -> Vector4<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    let (bx, by, bz, bw) = (b[0], b[1], b[2], b[3]);
    [f(ax, bx), f(ay, by), f(az, bz), f(aw, bw)]
}

pub fn fold2<T, F>(a: Vector2<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay) = (a[0], a[1]);
    f(ax, ay)
}
pub fn fold3<T, F>(a: Vector3<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    f(f(ax, ay), az)
}
pub fn fold4<T, F>(a: Vector4<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    f(f(f(ax, ay), az), aw)
}

pub fn add2<T>(a: Point2<T>, b: Vector2<T>) -> Point2<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with2(a, b, Add::add)
}
pub fn add3<T>(a: Point3<T>, b: Vector3<T>) -> Point3<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with3(a, b, Add::add)
}
pub fn add4<T>(a: Point4<T>, b: Vector4<T>) -> Point4<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with4(a, b, Add::add)
}

pub fn sub2<T>(a: Point2<T>, b: Point2<T>) -> Vector2<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with2(a, b, Sub::sub)
}
pub fn sub3<T>(a: Point3<T>, b: Point3<T>) -> Vector3<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with3(a, b, Sub::sub)
}
pub fn sub4<T>(a: Point4<T>, b: Point4<T>) -> Vector4<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with4(a, b, Sub::sub)
}

pub fn mul2<T>(a: Vector2<T>, b: T) -> Vector2<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with2(a, const2(b), Mul::mul)
}
pub fn mul3<T>(a: Vector3<T>, b: T) -> Vector3<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with3(a, const3(b), Mul::mul)
}
pub fn mul4<T>(a: Vector4<T>, b: T) -> Vector4<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with4(a, const4(b), Mul::mul)
}

pub fn dot2<T: Float>(a: Vector2<T>, b: Vector2<T>) -> T {
    fold2(zip_with2(a, b, Mul::mul), Add::add)
}
pub fn dot3<T: Float>(a: Vector3<T>, b: Vector3<T>) -> T {
    fold3(zip_with3(a, b, Mul::mul), Add::add)
}
pub fn dot4<T: Float>(a: Vector4<T>, b: Vector4<T>) -> T {
    fold4(zip_with4(a, b, Mul::mul), Add::add)
}

pub fn const2<T: Copy>(x: T) -> Vector2<T> {
    [x, x]
}
pub fn const3<T: Copy>(x: T) -> Vector3<T> {
    [x, x, x]
}
pub fn const4<T: Copy>(x: T) -> Vector4<T> {
    [x, x, x, x]
}

pub fn one2<T: Copy + NumCast>() -> Vector2<T> {
    cast2(const2(1))
}
pub fn one3<T: Copy + NumCast>() -> Vector3<T> {
    cast3(const3(1))
}
pub fn one4<T: Copy + NumCast>() -> Vector4<T> {
    cast4(const4(1))
}

pub fn cast2<T, U>(x: Point2<T>) -> Point2<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map2(x, cast)
}
pub fn cast3<T, U>(x: Point3<T>) -> Point3<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map3(x, cast)
}
pub fn cast4<T, U>(x: Point4<T>) -> Point4<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map4(x, cast)
}

pub mod interp {
    use math;
    use num_traits::Float;

    /// Performs linear interploation between two values.
    pub fn linear<T: Float>(a: T, b: T, x: T) -> T {
        x.mul_add((b - a), a)
    }

    /// Performs cubic interpolation between two values bound between two other
    /// values.
    ///
    /// - n0 - The value before the first value.
    /// - n1 - The first value.
    /// - n2 - The second value.
    /// - n3 - The value after the second value.
    /// - x - The alpha value.
    ///
    /// The alpha value should range from 0.0 to 1.0. If the alpha value is
    /// 0.0, this function returns _n1_. If the alpha value is 1.0, this
    /// function returns _n2_.
    pub fn cubic<T: Float>(n0: T, n1: T, n2: T, n3: T, x: T) -> T {
        let p = (n3 - n2) - (n0 - n1);
        let q = (n0 - n1) - p;
        let r = n2 - n0;
        let s = n1;
        p * x * x * x + q * x * x + r * x + s
    }

    /// Maps a value onto a cubic S-curve.
    pub fn s_curve3<T: Float>(x: T) -> T {
        x * x * (math::cast::<_, T>(3.0) - (x * math::cast(2.0)))
    }

    /// Maps a value onto a quintic S-curve.
    pub fn s_curve5<T: Float>(x: T) -> T {
        x * x * x * (x * (x * math::cast(6.0) - math::cast(15.0) + math::cast(10.0)))
    }
}
<commit_msg>Add #[inline] to all math module functions<commit_after>// Copyright (c) 2017 The Noise-rs Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. All files in the
// project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

//! An ultra-light private math library to make our short lives easier as we
//! implement super-complex noise stuff.

use num_traits::{self, Float, NumCast};
use std::ops::{Add, Mul, Sub};

/// Cast a numeric type without having to unwrap - we don't expect any overflow
/// errors...
#[inline]
pub fn cast<T: NumCast, U: NumCast>(x: T) -> U {
    num_traits::cast(x).unwrap()
}

/// Raises the number to the power of `4`
#[inline]
pub fn pow4<T: Float>(x: T) -> T {
    x * x * x * x
}

/// A 2-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point2<T> = [T; 2];

/// A 3-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point3<T> = [T; 3];

/// A 4-dimensional point. This is a fixed sized array, so should be compatible
/// with most linear algebra libraries.
pub type Point4<T> = [T; 4];

/// A 2-dimensional vector, for internal use.
pub type Vector2<T> = [T; 2];
/// A 3-dimensional vector, for internal use.
pub type Vector3<T> = [T; 3];
/// A 4-dimensional vector, for internal use.
pub type Vector4<T> = [T; 4];

#[inline]
pub fn map2<T, U, F>(a: Vector2<T>, f: F) -> Vector2<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay) = (a[0], a[1]);
    [f(ax), f(ay)]
}

#[inline]
pub fn map3<T, U, F>(a: Vector3<T>, f: F) -> Vector3<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    [f(ax), f(ay), f(az)]
}

#[inline]
pub fn map4<T, U, F>(a: Vector4<T>, f: F) -> Vector4<U>
    where T: Copy,
          F: Fn(T) -> U,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    [f(ax), f(ay), f(az), f(aw)]
}

#[inline]
pub fn zip_with2<T, U, V, F>(a: Vector2<T>, b: Vector2<U>, f: F) -> Vector2<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay) = (a[0], a[1]);
    let (bx, by) = (b[0], b[1]);
    [f(ax, bx), f(ay, by)]
}

#[inline]
pub fn zip_with3<T, U, V, F>(a: Vector3<T>, b: Vector3<U>, f: F) -> Vector3<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    let (bx, by, bz) = (b[0], b[1], b[2]);
    [f(ax, bx), f(ay, by), f(az, bz)]
}

#[inline]
pub fn zip_with4<T, U, V, F>(a: Vector4<T>, b: Vector4<U>, f: F) -> Vector4<V>
    where T: Copy,
          U: Copy,
          F: Fn(T, U) -> V,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    let (bx, by, bz, bw) = (b[0], b[1], b[2], b[3]);
    [f(ax, bx), f(ay, by), f(az, bz), f(aw, bw)]
}

#[inline]
pub fn fold2<T, F>(a: Vector2<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay) = (a[0], a[1]);
    f(ax, ay)
}

#[inline]
pub fn fold3<T, F>(a: Vector3<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay, az) = (a[0], a[1], a[2]);
    f(f(ax, ay), az)
}

#[inline]
pub fn fold4<T, F>(a: Vector4<T>, f: F) -> T
    where T: Copy,
          F: Fn(T, T) -> T,
{
    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
    f(f(f(ax, ay), az), aw)
}

#[inline]
pub fn add2<T>(a: Point2<T>, b: Vector2<T>) -> Point2<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with2(a, b, Add::add)
}

#[inline]
pub fn add3<T>(a: Point3<T>, b: Vector3<T>) -> Point3<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with3(a, b, Add::add)
}

#[inline]
pub fn add4<T>(a: Point4<T>, b: Vector4<T>) -> Point4<T>
    where T: Copy + Add<T, Output = T>,
{
    zip_with4(a, b, Add::add)
}

#[inline]
pub fn sub2<T>(a: Point2<T>, b: Point2<T>) -> Vector2<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with2(a, b, Sub::sub)
}

#[inline]
pub fn sub3<T>(a: Point3<T>, b: Point3<T>) -> Vector3<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with3(a, b, Sub::sub)
}

#[inline]
pub fn sub4<T>(a: Point4<T>, b: Point4<T>) -> Vector4<T>
    where T: Copy + Sub<T, Output = T>,
{
    zip_with4(a, b, Sub::sub)
}

#[inline]
pub fn mul2<T>(a: Vector2<T>, b: T) -> Vector2<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with2(a, const2(b), Mul::mul)
}

#[inline]
pub fn mul3<T>(a: Vector3<T>, b: T) -> Vector3<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with3(a, const3(b), Mul::mul)
}

#[inline]
pub fn mul4<T>(a: Vector4<T>, b: T) -> Vector4<T>
    where T: Copy + Mul<T, Output = T>,
{
    zip_with4(a, const4(b), Mul::mul)
}

#[inline]
pub fn dot2<T: Float>(a: Vector2<T>, b: Vector2<T>) -> T {
    fold2(zip_with2(a, b, Mul::mul), Add::add)
}

#[inline]
pub fn dot3<T: Float>(a: Vector3<T>, b: Vector3<T>) -> T {
    fold3(zip_with3(a, b, Mul::mul), Add::add)
}

#[inline]
pub fn dot4<T: Float>(a: Vector4<T>, b: Vector4<T>) -> T {
    fold4(zip_with4(a, b, Mul::mul), Add::add)
}

#[inline]
pub fn const2<T: Copy>(x: T) -> Vector2<T> {
    [x, x]
}

#[inline]
pub fn const3<T: Copy>(x: T) -> Vector3<T> {
    [x, x, x]
}

#[inline]
pub fn const4<T: Copy>(x: T) -> Vector4<T> {
    [x, x, x, x]
}

#[inline]
pub fn one2<T: Copy + NumCast>() -> Vector2<T> {
    cast2(const2(1))
}

#[inline]
pub fn one3<T: Copy + NumCast>() -> Vector3<T> {
    cast3(const3(1))
}

#[inline]
pub fn one4<T: Copy + NumCast>() -> Vector4<T> {
    cast4(const4(1))
}

#[inline]
pub fn cast2<T, U>(x: Point2<T>) -> Point2<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map2(x, cast)
}

#[inline]
pub fn cast3<T, U>(x: Point3<T>) -> Point3<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map3(x, cast)
}

#[inline]
pub fn cast4<T, U>(x: Point4<T>) -> Point4<U>
    where T: NumCast + Copy,
          U: NumCast + Copy,
{
    map4(x, cast)
}

pub mod interp {
    use math;
    use num_traits::Float;

    /// Performs linear interploation between two values.
    #[inline]
    pub fn linear<T: Float>(a: T, b: T, x: T) -> T {
        x.mul_add((b - a), a)
    }

    /// Performs cubic interpolation between two values bound between two other
    /// values.
    ///
    /// - n0 - The value before the first value.
    /// - n1 - The first value.
    /// - n2 - The second value.
    /// - n3 - The value after the second value.
    /// - x - The alpha value.
    ///
    /// The alpha value should range from 0.0 to 1.0. If the alpha value is
    /// 0.0, this function returns _n1_. If the alpha value is 1.0, this
    /// function returns _n2_.
    #[inline]
    pub fn cubic<T: Float>(n0: T, n1: T, n2: T, n3: T, x: T) -> T {
        let p = (n3 - n2) - (n0 - n1);
        let q = (n0 - n1) - p;
        let r = n2 - n0;
        let s = n1;
        p * x * x * x + q * x * x + r * x + s
    }

    /// Maps a value onto a cubic S-curve.
    #[inline]
    pub fn s_curve3<T: Float>(x: T) -> T {
        x * x * (math::cast::<_, T>(3.0) - (x * math::cast(2.0)))
    }

    /// Maps a value onto a quintic S-curve.
    #[inline]
    pub fn s_curve5<T: Float>(x: T) -> T {
        x * x * x * (x * (x * math::cast(6.0) - math::cast(15.0) + math::cast(10.0)))
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", deny(warnings))]
//#![cfg_attr(feature = "clippy", allow(needless_lifetimes))]

extern crate rustc_serialize;
#[macro_use] extern crate iron;
extern crate router;
extern crate hyper;
extern crate toml;
extern crate unicase;
extern crate atomicwrites;
extern crate url;
extern crate urlencoded;
extern crate clap;
extern crate crypto;
extern crate rand;
extern crate persistent;
extern crate iron_login;
extern crate handlebars;
extern crate handlebars_iron;
extern crate mount;
extern crate regex;
#[macro_use] extern crate quick_error;
extern crate time;
extern crate filetime;
extern crate chrono;
extern crate nix;
extern crate itertools;
extern crate uuid;
extern crate jsonwebtoken;
extern crate webicon;

#[cfg(test)]
extern crate tempdir;

pub mod cli;
mod web;
mod models;
mod utils;
mod config;
<commit_msg>Remove useless comment<commit_after>#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", deny(warnings))]

extern crate rustc_serialize;
#[macro_use] extern crate iron;
extern crate router;
extern crate hyper;
extern crate toml;
extern crate unicase;
extern crate atomicwrites;
extern crate url;
extern crate urlencoded;
extern crate clap;
extern crate crypto;
extern crate rand;
extern crate persistent;
extern crate iron_login;
extern crate handlebars;
extern crate handlebars_iron;
extern crate mount;
extern crate regex;
#[macro_use] extern crate quick_error;
extern crate time;
extern crate filetime;
extern crate chrono;
extern crate nix;
extern crate itertools;
extern crate uuid;
extern crate jsonwebtoken;
extern crate webicon;

#[cfg(test)]
extern crate tempdir;

pub mod cli;
mod web;
mod models;
mod utils;
mod config;
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

// TODO: Merge this with more general implementation in the `unix` module.
pub struct ExtAttrs<'p> {
    path: &'p Path,
    names: std::vec::IntoIter<std::ffi::OsString>,
}

impl<'p> Iterator for ExtAttrs<'p> {

    type Item = std::io::Result<super::unix::ExtAttr>;

    fn next(&mut self) -> Option<std::io::Result<super::unix::ExtAttr>> {
        let name = self.names.next()?;
        let value = match ext_attr_value(self.path, &name) {
            Ok(value) => value,
            Err(error) => return Some(Err(error)),
        };

        Some(Ok(super::unix::ExtAttr { name, value }))
    }
}

pub fn ext_attrs<'p>(path: &'p Path) -> std::io::Result<ExtAttrs<'p>> {
    let names = ext_attr_names(path)?;
    Ok(ExtAttrs {
        path,
        names: names.into_iter(),
    })
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/listxattr.2.html
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

pub fn ext_attr_value<P, S>(path: P, name: S) -> std::io::Result<Vec<u8>>
where
    P: AsRef<Path>,
    S: AsRef<std::ffi::OsStr>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/getxattr.2.html
        fn lgetxattr(
            path: *const libc::c_char,
            name: *const libc::c_char,
            value: *mut libc::c_void,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    let c_str_name = std::ffi::CString::new(name.as_ref().as_bytes())
        // While `name` as returned by the `ext_attr_names` function cannot have
        // null bytes inside, we cannot guarantee that the user doesn't supply
        // a bogus string here. Thus, we have to do proper error handling here.
        .map_err(|error| {
            std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
        })?;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. The rest is just is a FFI call respecting
    // the spec.
    let len = unsafe {
        // First we call `lgetxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];
    let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. For remaining parameters we provide a buffer
    // with length obtained through the previous call and we explicitly supply
    // its length as well. In case the result length increases between the calls
    // the system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `lgetxattr` with the actual buffer of the size we
        // determined by the previous call.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), buf_ptr, buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // In very rare cases (if the users tampers with the file inbetween calls to
    // the `lgetxattr`), the length value can decrease. To avoid returning any
    // garbage at the end, we have to truncate the buffer to its real length.
    buf.truncate(len as usize);
    Ok(buf)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attrs_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let mut ext_attrs = ext_attrs(tempfile.path()).unwrap();
        assert!(ext_attrs.next().is_none());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attrs_some() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.attr1", b"foo");
        setfattr(tempfile.path(), "user.attr2", b"bar");
        setfattr(tempfile.path(), "user.attr3", b"baz");

        let mut ext_attrs = ext_attrs(tempfile.path()).unwrap()
            .collect::<std::io::Result<Vec<_>>>().unwrap();
        ext_attrs.sort_by_cached_key(|attr| attr.name.clone());

        assert_eq!(ext_attrs[0].name, "user.attr1");
        assert_eq!(ext_attrs[0].value, b"foo");

        assert_eq!(ext_attrs[1].name, "user.attr2");
        assert_eq!(ext_attrs[1].value, b"bar");

        assert_eq!(ext_attrs[2].name, "user.attr3");
        assert_eq!(ext_attrs[2].value, b"baz");
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[test]
    fn ext_attr_value_not_existing() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let error = ext_attr_value(tempfile.path(), "user.foo").unwrap_err();
        assert_eq!(error.raw_os_error(), Some(libc::ENODATA));
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"bar");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"bar");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single_not_unicode() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"\xff\xfe\xff");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"\xff\xfe\xff");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"quux");
        setfattr(tempfile.path(), "user.bar", b"norf");

        let foo_value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(foo_value, b"quux");

        let bar_value = ext_attr_value(tempfile.path(), "user.bar").unwrap();
        assert_eq!(bar_value, b"norf");
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<commit_msg>Remove a no longer used Linux xattr iterator.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/listxattr.2.html
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

pub fn ext_attr_value<P, S>(path: P, name: S) -> std::io::Result<Vec<u8>>
where
    P: AsRef<Path>,
    S: AsRef<std::ffi::OsStr>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/getxattr.2.html
        fn lgetxattr(
            path: *const libc::c_char,
            name: *const libc::c_char,
            value: *mut libc::c_void,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    let c_str_name = std::ffi::CString::new(name.as_ref().as_bytes())
        // While `name` as returned by the `ext_attr_names` function cannot have
        // null bytes inside, we cannot guarantee that the user doesn't supply
        // a bogus string here. Thus, we have to do proper error handling here.
        .map_err(|error| {
            std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
        })?;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. The rest is just is a FFI call respecting
    // the spec.
    let len = unsafe {
        // First we call `lgetxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];
    let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. For remaining parameters we provide a buffer
    // with length obtained through the previous call and we explicitly supply
    // its length as well. In case the result length increases between the calls
    // the system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `lgetxattr` with the actual buffer of the size we
        // determined by the previous call.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), buf_ptr, buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // In very rare cases (if the users tampers with the file inbetween calls to
    // the `lgetxattr`), the length value can decrease. To avoid returning any
    // garbage at the end, we have to truncate the buffer to its real length.
    buf.truncate(len as usize);
    Ok(buf)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[test]
    fn ext_attr_value_not_existing() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let error = ext_attr_value(tempfile.path(), "user.foo").unwrap_err();
        assert_eq!(error.raw_os_error(), Some(libc::ENODATA));
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"bar");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"bar");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single_not_unicode() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"\xff\xfe\xff");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"\xff\xfe\xff");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"quux");
        setfattr(tempfile.path(), "user.bar", b"norf");

        let foo_value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(foo_value, b"quux");

        let bar_value = ext_attr_value(tempfile.path(), "user.bar").unwrap();
        assert_eq!(bar_value, b"norf");
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<|endoftext|>


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

use libc;
use std::{mem, ptr, io, any};
#[cfg(feature="nightly")] use std::panic;
use std::ffi::{CString, CStr};
use python::{Python, PythonObject, PyDrop};
use objects::{PyObject, PyTuple, PyDict, PyString, exc};
use conversion::ToPyObject;
use ffi;
use err::{self, PyResult};

#[macro_export]
#[doc(hidden)]
macro_rules! py_method_def {
    ($name: expr, $flags: expr, $wrap: expr) => {{
        static mut method_def: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
            //ml_name: bytes!(stringify!($name), "\0"),
            ml_name: 0 as *const $crate::_detail::libc::c_char,
            ml_meth: None,
            ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags,
            ml_doc: 0 as *const $crate::_detail::libc::c_char
        };
        method_def.ml_name = concat!($name, "\0").as_ptr() as *const _;
        method_def.ml_meth = Some(
            ::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
                                  $crate::_detail::ffi::PyCFunction>($wrap)
        );
        &mut method_def
    }}
}

/// Creates a Python callable object that invokes a Rust function.
///
/// There are two forms of this macro:
/// 1) `py_fn!(py, f(parameter_list))`
/// 2) `py_fn!(py, f(parameter_list) -> PyResult<T> { body })`
///
/// All three forms return a value of type `PyObject`.
/// This python object is a callable object that invokes
/// the Rust function when called.
///
/// When called, the arguments are converted into
/// the Rust types specified in the parameter list.
/// See `py_argparse!()` for details on argument parsing.
///
/// Form 1:
/// * `py` must be an expression of type `Python`
/// * `f` must be the name of a function that is compatible with the specified
///    parameter list, except that a single parameter of type `Python` is prepended.
///    The function must return `PyResult<T>` for some `T` that implements `ToPyObject`.
///
/// Form 2:
/// * `py` must be an identifier refers to a `Python` value.
///   The function body will also have access to a `Python` variable of this name.
/// * `f` must be an identifier.
/// * The function return type must be `PyResult<T>` for some `T` that
///   implements `ToPyObject`.
///
/// # Example
/// ```
/// #[macro_use] extern crate cpython;
/// use cpython::{Python, PyResult, PyErr, PyDict};
/// use cpython::{exc};
///
/// fn multiply(py: Python, lhs: i32, rhs: i32) -> PyResult<i32> {
///     match lhs.checked_mul(rhs) {
///         Some(val) => Ok(val),
///         None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
///     }
/// }
///
/// fn main() {
///     let gil = Python::acquire_gil();
///     let py = gil.python();
///     let dict = PyDict::new(py);
///     dict.set_item(py, "multiply", py_fn!(py, multiply(lhs: i32, rhs: i32))).unwrap();
///     py.run("print(multiply(6, 7))", None, Some(&dict)).unwrap();
/// }
/// ```
#[macro_export]
macro_rules! py_fn {
    ($py:expr, $f:ident $plist:tt ) => {
        py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
    };
    ($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
        py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! py_fn_impl {
    // Form 1: reference existing function
    { $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        // <DUMMY> is workaround for rust issue #26201
        unsafe extern "C" fn wrap<DUMMY>(
            _slf: *mut $crate::_detail::ffi::PyObject,
            args: *mut $crate::_detail::ffi::PyObject,
            kwargs: *mut $crate::_detail::ffi::PyObject)
        -> *mut $crate::_detail::ffi::PyObject
        {
            $crate::_detail::handle_callback(
                stringify!($f), $crate::_detail::PyObjectCallbackConverter,
                |py| {
                    py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
                        [ $( { $pname : $ptype = $detail } )* ]
                        {
                            $f(py $(, $pname )* )
                        })
                })
        }
        unsafe {
            $crate::_detail::py_fn_impl($py,
                py_method_def!(stringify!($f), 0, wrap::<()>))
        }
    }};
    // Form 2: inline function definition
    { $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
        py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
    }}
}

pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
    err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}

pub trait CallbackConverter<T, R> {
    fn convert(T, Python) -> R;
    fn error_value() -> R;
}

pub struct PyObjectCallbackConverter;

impl <T> CallbackConverter<T, *mut ffi::PyObject> for PyObjectCallbackConverter
    where T: ToPyObject
{
    fn convert(val: T, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

#[cfg(feature="nightly")]
pub unsafe fn handle_callback<F, T, R, C>(location: &str, _c: C, f: F) -> R
    where F: FnOnce(Python) -> PyResult<T>,
          F: panic::UnwindSafe,
          C: CallbackConverter<T, R>
{
    let guard = AbortOnDrop(location);
    let ret = panic::catch_unwind(|| {
        let py = Python::assume_gil_acquired();
        match f(py) {
            Ok(val) => {
                C::convert(val, py)
            }
            Err(e) => {
                e.restore(py);
                C::error_value()
            }
        }
    });
    let ret = match ret {
        Ok(r) => r,
        Err(ref err) => {
            handle_panic(Python::assume_gil_acquired(), err);
            C::error_value()
        }
    };
    mem::forget(guard);
    ret
}

#[cfg(feature="nightly")]
fn handle_panic(_py: Python, _panic: &any::Any) {
    let msg = cstr!("Rust panic");
    unsafe {
        ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
    }
}

#[cfg(not(feature="nightly"))]
pub unsafe fn handle_callback<F, T, R>(location: &str, f: F) -> R
    where F: FnOnce(Python) -> PyResult<T>,
          T: CallbackConvertTo<R>
{
    let guard = AbortOnDrop(location);
    let py = Python::assume_gil_acquired();
    let ret = match f(py) {
        Ok(val) => {
            val.convert(py)
        }
        Err(e) => {
            e.restore(py);
            R::error_value()
        }
    };
    mem::forget(guard);
    ret
}

pub struct AbortOnDrop<'a>(pub &'a str);

impl <'a> Drop for AbortOnDrop<'a> {
    fn drop(&mut self) {
        use std::io::Write;
        let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
        unsafe { libc::abort() }
    }
}

// Tests for this file are in tests/test_function.rs

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

use libc;
use std::{mem, ptr, io, any};
#[cfg(feature="nightly")] use std::panic;
use std::ffi::{CString, CStr};
use python::{Python, PythonObject, PyDrop};
use objects::{PyObject, PyTuple, PyDict, PyString, exc};
use conversion::ToPyObject;
use ffi;
use err::{self, PyResult};

#[macro_export]
#[doc(hidden)]
macro_rules! py_method_def {
    ($name: expr, $flags: expr, $wrap: expr) => {{
        static mut method_def: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
            //ml_name: bytes!(stringify!($name), "\0"),
            ml_name: 0 as *const $crate::_detail::libc::c_char,
            ml_meth: None,
            ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags,
            ml_doc: 0 as *const $crate::_detail::libc::c_char
        };
        method_def.ml_name = concat!($name, "\0").as_ptr() as *const _;
        method_def.ml_meth = Some(
            ::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
                                  $crate::_detail::ffi::PyCFunction>($wrap)
        );
        &mut method_def
    }}
}

/// Creates a Python callable object that invokes a Rust function.
///
/// There are two forms of this macro:
/// 1) `py_fn!(py, f(parameter_list))`
/// 2) `py_fn!(py, f(parameter_list) -> PyResult<T> { body })`
///
/// All three forms return a value of type `PyObject`.
/// This python object is a callable object that invokes
/// the Rust function when called.
///
/// When called, the arguments are converted into
/// the Rust types specified in the parameter list.
/// See `py_argparse!()` for details on argument parsing.
///
/// Form 1:
/// * `py` must be an expression of type `Python`
/// * `f` must be the name of a function that is compatible with the specified
///    parameter list, except that a single parameter of type `Python` is prepended.
///    The function must return `PyResult<T>` for some `T` that implements `ToPyObject`.
///
/// Form 2:
/// * `py` must be an identifier refers to a `Python` value.
///   The function body will also have access to a `Python` variable of this name.
/// * `f` must be an identifier.
/// * The function return type must be `PyResult<T>` for some `T` that
///   implements `ToPyObject`.
///
/// # Example
/// ```
/// #[macro_use] extern crate cpython;
/// use cpython::{Python, PyResult, PyErr, PyDict};
/// use cpython::{exc};
///
/// fn multiply(py: Python, lhs: i32, rhs: i32) -> PyResult<i32> {
///     match lhs.checked_mul(rhs) {
///         Some(val) => Ok(val),
///         None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
///     }
/// }
///
/// fn main() {
///     let gil = Python::acquire_gil();
///     let py = gil.python();
///     let dict = PyDict::new(py);
///     dict.set_item(py, "multiply", py_fn!(py, multiply(lhs: i32, rhs: i32))).unwrap();
///     py.run("print(multiply(6, 7))", None, Some(&dict)).unwrap();
/// }
/// ```
#[macro_export]
macro_rules! py_fn {
    ($py:expr, $f:ident $plist:tt ) => {
        py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
    };
    ($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
        py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! py_fn_impl {
    // Form 1: reference existing function
    { $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        // <DUMMY> is workaround for rust issue #26201
        unsafe extern "C" fn wrap<DUMMY>(
            _slf: *mut $crate::_detail::ffi::PyObject,
            args: *mut $crate::_detail::ffi::PyObject,
            kwargs: *mut $crate::_detail::ffi::PyObject)
        -> *mut $crate::_detail::ffi::PyObject
        {
            $crate::_detail::handle_callback(
                stringify!($f), $crate::_detail::PyObjectCallbackConverter,
                |py| {
                    py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
                        [ $( { $pname : $ptype = $detail } )* ]
                        {
                            $f(py $(, $pname )* )
                        })
                })
        }
        unsafe {
            $crate::_detail::py_fn_impl($py,
                py_method_def!(stringify!($f), 0, wrap::<()>))
        }
    }};
    // Form 2: inline function definition
    { $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
        py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
    }}
}

pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
    err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}

pub trait CallbackConverter<T, R> {
    fn convert(T, Python) -> R;
    fn error_value() -> R;
}

pub struct PyObjectCallbackConverter;

impl <T> CallbackConverter<T, *mut ffi::PyObject> for PyObjectCallbackConverter
    where T: ToPyObject
{
    fn convert(val: T, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

#[cfg(feature="nightly")]
pub unsafe fn handle_callback<F, T, R, C>(location: &str, _c: C, f: F) -> R
    where F: FnOnce(Python) -> PyResult<T>,
          F: panic::UnwindSafe,
          C: CallbackConverter<T, R>
{
    let guard = AbortOnDrop(location);
    let ret = panic::catch_unwind(|| {
        let py = Python::assume_gil_acquired();
        match f(py) {
            Ok(val) => {
                C::convert(val, py)
            }
            Err(e) => {
                e.restore(py);
                C::error_value()
            }
        }
    });
    let ret = match ret {
        Ok(r) => r,
        Err(ref err) => {
            handle_panic(Python::assume_gil_acquired(), err);
            C::error_value()
        }
    };
    mem::forget(guard);
    ret
}

#[cfg(feature="nightly")]
fn handle_panic(_py: Python, _panic: &any::Any) {
    let msg = cstr!("Rust panic");
    unsafe {
        ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
    }
}

#[cfg(not(feature="nightly"))]
pub unsafe fn handle_callback<F, T, R, C>(location: &str, _c: C, f: F) -> R
    where F: FnOnce(Python) -> PyResult<T>,
          C: CallbackConverter<T, R>
{
    let guard = AbortOnDrop(location);
    let py = Python::assume_gil_acquired();
    let ret = match f(py) {
        Ok(val) => {
            C::convert(val, py)
        }
        Err(e) => {
            e.restore(py);
            C::error_value()
        }
    };
    mem::forget(guard);
    ret
}

pub struct AbortOnDrop<'a>(pub &'a str);

impl <'a> Drop for AbortOnDrop<'a> {
    fn drop(&mut self) {
        use std::io::Write;
        let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
        unsafe { libc::abort() }
    }
}

// Tests for this file are in tests/test_function.rs

<|endoftext|>


<commit_before>use std::fmt;
use std::net::Ipv6Addr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};

use simple_kbuckets::Key;

pub const PUBLIC_KEY_LENGTH: usize = 32;

pub type Path = [u8; 8];

/// Rotates an IPv6 address 64 bits, which is a required preprocessing
/// for computing the XOR metric.
/// See https://github.com/cjdelisle/cjdns/blob/cjdns-v18/doc/Whitepaper.md#the-router
fn rotate_64(i: &[u8; 16]) -> [u8; 16] {
    [
        i[ 8], i[ 9], i[10], i[11], i[12], i[13], i[14], i[15],
        i[ 0], i[ 1], i[ 2], i[ 3], i[ 4], i[ 5], i[ 6], i[ 7],
    ]
}

pub const ADDRESS_BITS: usize = 16*8;

/// Wrapper of `Ipv6Addr` that implements `simple_kbuckets::Key`
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Address {
    bytes: [u8; 16],
}

impl Address {
    pub fn new(bytes: &[u8; 16]) -> Address {
        Address { bytes: rotate_64(bytes) }
    }
    pub fn bytes(&self) -> [u8; 16] {
        rotate_64(&self.bytes)
    }
}

impl fmt::Debug for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let ipv6addr = Ipv6Addr::from(self);
        write!(f, "Address::from(Ipv6Addr::from_str(\"{}\"))", ipv6addr)
    }
}

impl<'a> From<&'a Ipv6Addr> for Address {
    fn from(ipv6addr: &Ipv6Addr) -> Address {
        Address::new(&ipv6addr.octets())
    }
}
impl From<Ipv6Addr> for Address {
    fn from(ipv6addr: Ipv6Addr) -> Address {
        Address::from(&ipv6addr)
    }
}
impl<'a> From<&'a Address> for Ipv6Addr {
    fn from(addr: &Address) -> Ipv6Addr {
        Ipv6Addr::from(addr.bytes())
    }
}
impl From<Address> for Ipv6Addr {
    fn from(addr: Address) -> Ipv6Addr {
        Ipv6Addr::from(&addr)
    }
}

impl Key for Address {
    fn bitxor(&self, other: &Self) -> Self {
        let vec = self.bytes.to_vec().bitxor(&other.bytes.to_vec());
        assert_eq!(vec.len(), 16);
        let mut bytes = [0u8; 16];
        bytes.copy_from_slice(&vec);
        Address { bytes: bytes }
    }
    fn bits(&self) -> usize {
        self.bytes.to_vec().bits()
    }
}

/// Data of the hash table
#[derive(Clone, Debug)]
pub struct Node {
    public_key: [u8; PUBLIC_KEY_LENGTH],
    path: Path,
    version: u64,
}

impl Node {
    pub fn new(pk: [u8; PUBLIC_KEY_LENGTH], path: Path, version: u64) -> Node {
        Node {
            public_key: pk,
            path: path,
            version: version,
        }
    }
    pub fn public_key(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
        &self.public_key
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn version(&self) -> u64 {
        self.version
    }
}

impl Eq for Node {
}
impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.public_key == other.public_key
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Node) -> Ordering {
        self.public_key.cmp(&other.public_key)
    }
}
impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Node) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Hash for Node {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.public_key.hash(state);
    }
}
<commit_msg>Don't do useless copies to a vec; compute on array directly.<commit_after>use std::fmt;
use std::net::Ipv6Addr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};

use simple_kbuckets::Key;

pub const PUBLIC_KEY_LENGTH: usize = 32;

pub type Path = [u8; 8];

/// Rotates an IPv6 address 64 bits, which is a required preprocessing
/// for computing the XOR metric.
/// See https://github.com/cjdelisle/cjdns/blob/cjdns-v18/doc/Whitepaper.md#the-router
fn rotate_64(i: &[u8; 16]) -> [u8; 16] {
    [
        i[ 8], i[ 9], i[10], i[11], i[12], i[13], i[14], i[15],
        i[ 0], i[ 1], i[ 2], i[ 3], i[ 4], i[ 5], i[ 6], i[ 7],
    ]
}

pub const ADDRESS_BITS: usize = 16*8;

/// Wrapper of `Ipv6Addr` that implements `simple_kbuckets::Key`
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Address {
    bytes: [u8; 16],
}

impl Address {
    pub fn new(bytes: &[u8; 16]) -> Address {
        Address { bytes: rotate_64(bytes) }
    }
    pub fn bytes(&self) -> [u8; 16] {
        rotate_64(&self.bytes)
    }
}

impl fmt::Debug for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let ipv6addr = Ipv6Addr::from(self);
        write!(f, "Address::from(Ipv6Addr::from_str(\"{}\"))", ipv6addr)
    }
}

impl<'a> From<&'a Ipv6Addr> for Address {
    fn from(ipv6addr: &Ipv6Addr) -> Address {
        Address::new(&ipv6addr.octets())
    }
}
impl From<Ipv6Addr> for Address {
    fn from(ipv6addr: Ipv6Addr) -> Address {
        Address::from(&ipv6addr)
    }
}
impl<'a> From<&'a Address> for Ipv6Addr {
    fn from(addr: &Address) -> Ipv6Addr {
        Ipv6Addr::from(addr.bytes())
    }
}
impl From<Address> for Ipv6Addr {
    fn from(addr: Address) -> Ipv6Addr {
        Ipv6Addr::from(&addr)
    }
}

impl Key for Address {
    fn bitxor(&self, other: &Self) -> Self {
        let mut bytes = [0; 16];
        for i in 0..bytes.len() {
            bytes[i] = self.bytes[i] ^ other.bytes[i];
        }
        Address { bytes: bytes }
    }
    fn bits(&self) -> usize {
        self.bytes.to_vec().bits()
    }
}

/// Data of the hash table
#[derive(Clone, Debug)]
pub struct Node {
    public_key: [u8; PUBLIC_KEY_LENGTH],
    path: Path,
    version: u64,
}

impl Node {
    pub fn new(pk: [u8; PUBLIC_KEY_LENGTH], path: Path, version: u64) -> Node {
        Node {
            public_key: pk,
            path: path,
            version: version,
        }
    }
    pub fn public_key(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
        &self.public_key
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn version(&self) -> u64 {
        self.version
    }
}

impl Eq for Node {
}
impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.public_key == other.public_key
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Node) -> Ordering {
        self.public_key.cmp(&other.public_key)
    }
}
impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Node) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Hash for Node {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.public_key.hash(state);
    }
}
<|endoftext|>


<commit_before>use glob::glob;
use reqwest;
use reqwest::header::Authorization;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use structs::{CliError, Config, Member, PublicKey, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    client: reqwest::Client,
    conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { client: match config.proxy_url {
                           Some(ref proxy_url) => {
                               let p = reqwest::Proxy::all(proxy_url).unwrap();
                               reqwest::Client::builder().proxy(p).build().unwrap()
                           }
                           None => reqwest::Client::new(),
                       },
                       conf: config.clone(), }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push("sectora-cache");
        path.push(url);
        path
    }

    fn load_content_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata, content))
    }

    fn store_content_to_cache(&self, url: &str, content: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap())?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url: &str) -> Result<String, CliError> {
        let token = String::from("token ") + &self.conf.token;
        let res = self.client.get(url).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url, &content)?;
        Ok(content)
    }

    fn get_content(&self, url: &str) -> Result<String, CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata, cache_content)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_content_from_url(url) {
                            Ok(content_from_url) => Ok(content_from_url),
                            Err(_) => Ok(cache_content),
                        }
                    } else {
                        Ok(cache_content)
                    }
                }
                Err(_) => Ok(cache_content),
            },
            Err(_) => self.get_content_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&content)?;
        Ok(keys.iter().map(|k| k.key.clone())
               .collect::<Vec<String>>()
               .join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let sectors = self.get_sectors();
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Vec<SectorGroup> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result().unwrap_or(Vec::new());
        sectors.append(&mut self.get_repos_result().unwrap_or(Vec::new()));
        sectors
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_teams = self.get_team_map()?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let &Some(gh_team) = &gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid.clone(),
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)?, });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&content)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        Ok(members.iter()
                  .map(|m| (m.login.clone(), m.clone()))
                  .collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_repos = self.get_repo_map()?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let &Some(gh_repo) = &gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid.clone(),
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)?, });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self) -> Result<HashMap<String, Repo>, CliError> {
        let url = format!("{}/orgs/{}/repos?per_page=100",
                          self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&content)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        Ok(members.iter()
                  .map(|m| (m.login.clone(), m.clone()))
                  .collect())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("sectora-cache/**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Improve to get all paginated list of api result<commit_after>use glob::glob;
use reqwest;
use reqwest::header::Authorization;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use structs::{CliError, Config, Member, PublicKey, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    client: reqwest::Client,
    conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { client: match config.proxy_url {
                           Some(ref proxy_url) => {
                               let p = reqwest::Proxy::all(proxy_url).unwrap();
                               reqwest::Client::builder().proxy(p).build().unwrap()
                           }
                           None => reqwest::Client::new(),
                       },
                       conf: config.clone(), }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push("sectora-cache");
        path.push(url);
        path
    }

    fn load_content_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata, content))
    }

    fn store_content_to_cache(&self, url: &str, content: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap())?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url: &str) -> Result<String, CliError> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_content_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let content = serde_json::ser::to_string(&all_contents)?;
        self.store_content_to_cache(url, &content)?;
        Ok(content)
    }

    fn get_content_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::value::Value>, CliError> {
        let token = String::from("token ") + &self.conf.token;
        let sep = if url.contains("?") { "&" } else { "?" };
        let url_p = format!("{}{}page={}", url, sep, page);
        let res = self.client.get(&url_p).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        Ok(serde_json::from_str::<Vec<serde_json::value::Value>>(&content)?)
    }

    fn get_content(&self, url: &str) -> Result<String, CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata, cache_content)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_content_from_url(url) {
                            Ok(content_from_url) => Ok(content_from_url),
                            Err(_) => Ok(cache_content),
                        }
                    } else {
                        Ok(cache_content)
                    }
                }
                Err(_) => Ok(cache_content),
            },
            Err(_) => self.get_content_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&content)?;
        Ok(keys.iter().map(|k| k.key.clone())
               .collect::<Vec<String>>()
               .join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let sectors = self.get_sectors();
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Vec<SectorGroup> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result().unwrap_or(Vec::new());
        sectors.append(&mut self.get_repos_result().unwrap_or(Vec::new()));
        sectors
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_teams = self.get_team_map()?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let &Some(gh_team) = &gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid.clone(),
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)?, });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&content)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        Ok(members.iter()
                  .map(|m| (m.login.clone(), m.clone()))
                  .collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_repos = self.get_repo_map()?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let &Some(gh_repo) = &gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid.clone(),
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)?, });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self) -> Result<HashMap<String, Repo>, CliError> {
        let url = format!("{}/orgs/{}/repos?per_page=100",
                          self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&content)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        Ok(members.iter()
                  .map(|m| (m.login.clone(), m.clone()))
                  .collect())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("sectora-cache/**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::io;
use average::Average;
use circular_buffer::CircularBuffer;
use std::fmt::Display;
use json_display::JsonDisplay;

pub struct Historic<T> {
    circular_buffer : CircularBuffer<T>,
    limit : usize
}

impl<T : JsonDisplay + Display> Historic<T> {
    pub fn new(size : usize, limit : usize) -> Historic<T> {
        Historic::<T> {
            circular_buffer : CircularBuffer::<T>::new(size),
            limit
        }
    }

    pub fn add(&mut self, element : T) {
        self.circular_buffer.put_item(element);
    }

    pub fn get_nb_items(&self) -> usize {
        self.circular_buffer.get_nb_items()
    }

    pub fn is_empty(&self) -> bool {
        self.circular_buffer.is_empty()
    }
}

impl<T : JsonDisplay + Display + Average<T>> Historic<T> {
    pub fn reduce(historics : &mut [Historic<T>]) {
        //let mut i = 0;
        let mut average_data = None;
        for historic in historics {
            // look if the previous historic produce an average data to add to the next historic
            match average_data {
                Some(data) => {historic.circular_buffer.put_item(data);}
                None => {},
            }
            if historic.circular_buffer.get_nb_items() > historic.limit {
                let nb_elements_to_sum = historic.limit / 2;
                //println!("Reduction queue {} nbElementsToSum = {}\n", i, nb_elements_to_sum);
                // Accumulate on first nb_elements_to_sum element of the historic
                let mut j = 0;
                let mut accumulator_data = T::empty_cumulator();
                while j < nb_elements_to_sum {
                    match historic.circular_buffer.peek_item(j) {
                            Some(data) => {data.cumulate(&mut accumulator_data);},
                            None => panic!("Problem of implementation in historic reduce method")
                        }
                        j += 1;
                }
                //remove elements used for accumulation from the historic
                j = 0;
                while j < nb_elements_to_sum {
                    historic.circular_buffer.get_item();
                    j += 1;
                }
                // The average_data will be add to the next historic (if historic exists, otherwise it will be lost)
                average_data = Some(T::divide(&accumulator_data, nb_elements_to_sum));
                //i += 1;
            }
            else {
                break;
            }
        }
    }

    pub fn write_json_historics(historics : &[Historic<T>], w: &mut io::Write) {
        w.write(b"[").unwrap();
        let mut first = true;
        for historic in historics {
            if !historic.is_empty() {
                if !first {
                    w.write(b",\n").unwrap();
                } else {
                    first = false;
                }
                historic.circular_buffer.write_json_chunk(w).unwrap();
            }
        }
        w.write(b"]\n").unwrap();
    }
}

<commit_msg>Transform while to for<commit_after>use std::io;
use average::Average;
use circular_buffer::CircularBuffer;
use std::fmt::Display;
use json_display::JsonDisplay;

pub struct Historic<T> {
    circular_buffer : CircularBuffer<T>,
    limit : usize
}

impl<T : JsonDisplay + Display> Historic<T> {
    pub fn new(size : usize, limit : usize) -> Historic<T> {
        Historic::<T> {
            circular_buffer : CircularBuffer::<T>::new(size),
            limit
        }
    }

    pub fn add(&mut self, element : T) {
        self.circular_buffer.put_item(element);
    }

    pub fn get_nb_items(&self) -> usize {
        self.circular_buffer.get_nb_items()
    }

    pub fn is_empty(&self) -> bool {
        self.circular_buffer.is_empty()
    }
}

impl<T : JsonDisplay + Display + Average<T>> Historic<T> {
    pub fn reduce(historics : &mut [Historic<T>]) {
        //let mut i = 0;
        let mut average_data = None;
        for historic in historics {
            // look if the previous historic produce an average data to add to the next historic
            match average_data {
                Some(data) => {historic.circular_buffer.put_item(data);}
                None => {},
            }
            if historic.circular_buffer.get_nb_items() > historic.limit {
                let nb_elements_to_sum = historic.limit / 2;
                //println!("Reduction queue {} nbElementsToSum = {}\n", i, nb_elements_to_sum);
                // Accumulate on first nb_elements_to_sum element of the historic
                let mut accumulator_data = T::empty_cumulator();
                for j in 0..nb_elements_to_sum {
                    match historic.circular_buffer.peek_item(j) {
                            Some(data) => {data.cumulate(&mut accumulator_data);},
                            None => panic!("Problem of implementation in historic reduce method")
                        }
                }
                //remove elements used for accumulation from the historic
                for _ in 0..nb_elements_to_sum {
                    historic.circular_buffer.get_item();
                }
                // The average_data will be add to the next historic (if historic exists, otherwise it will be lost)
                average_data = Some(T::divide(&accumulator_data, nb_elements_to_sum));
                //i += 1;
            }
            else {
                break;
            }
        }
    }

    pub fn write_json_historics(historics : &[Historic<T>], w: &mut io::Write) {
        w.write(b"[").unwrap();
        let mut first = true;
        for historic in historics {
            if !historic.is_empty() {
                if !first {
                    w.write(b",\n").unwrap();
                } else {
                    first = false;
                }
                historic.circular_buffer.write_json_chunk(w).unwrap();
            }
        }
        w.write(b"]\n").unwrap();
    }
}

<|endoftext|>


<commit_before>use std::error::Error;
use postgres::types::{FromSql, IsNull, ToSql, Type, CIDR};
use postgres::{accepts, to_sql_checked};
use crate::{IpNetwork, Ipv4Network, Ipv6Network};
use crate::postgres_common;

type PostgresResult<T> = Result<T, Box<Error + Sync + Send>>;

impl FromSql for Ipv4Network {
    fn from_sql(_: &Type, raw: &[u8]) -> PostgresResult<Ipv4Network> {
        postgres_common::from_sql_ipv4_network(raw)
    }

    accepts!(CIDR);
}

impl FromSql for Ipv6Network {
    fn from_sql(_: &Type, raw: &[u8]) -> PostgresResult<Ipv6Network> {
        postgres_common::from_sql_ipv6_network(raw)
    }

    accepts!(CIDR);
}

impl FromSql for IpNetwork {
    fn from_sql(t: &Type, raw: &[u8]) -> PostgresResult<IpNetwork> {
        match raw[0] {
            postgres_common::IPV4_TYPE => Ok(IpNetwork::V4(Ipv4Network::from_sql(t, raw)?)),
            postgres_common::IPV6_TYPE => Ok(IpNetwork::V6(Ipv6Network::from_sql(t, raw)?)),
            _ => Err("CIDR is not IP version 4 or 6".into()),
        }
    }

    accepts!(CIDR);
}

impl ToSql for Ipv4Network {
    fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        let bytes = postgres_common::to_sql_ipv4_network(*self);
        w.extend_from_slice(&bytes);

        Ok(IsNull::No)
    }

    accepts!(CIDR);
    to_sql_checked!();
}

impl ToSql for Ipv6Network {
    fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        let bytes = postgres_common::to_sql_ipv6_network(*self);
        w.extend_from_slice(&bytes);

        Ok(IsNull::No)
    }

    accepts!(CIDR);
    to_sql_checked!();
}

impl ToSql for IpNetwork {
    fn to_sql(&self, t: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        match *self {
            IpNetwork::V4(ref network) => network.to_sql(t, w),
            IpNetwork::V6(ref network) => network.to_sql(t, w),
        }
    }

    accepts!(CIDR);
    to_sql_checked!();
}

#[cfg(test)]
mod tests {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use postgres::types::{FromSql, ToSql, CIDR};
    use crate::{IpNetwork, Ipv4Network, Ipv6Network};

    fn return_test_ipv4_network() -> Ipv4Network {
        Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 16).unwrap()
    }

    fn return_test_ipv6_network() -> Ipv6Network {
        Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap()
    }

    #[test]
    fn ivp4_to_sql() {
        let ip_network = return_test_ipv4_network();
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
        assert_eq!(2, output[0]);
        assert_eq!(16, output[1]);
        assert_eq!(1, output[2]);
        assert_eq!(4, output[3]);
        assert_eq!(192, output[4]);
        assert_eq!(168, output[5]);
        assert_eq!(0, output[6]);
        assert_eq!(0, output[7]);
    }

    #[test]
    fn ivp4_both_direction() {
        let ip_network = return_test_ipv4_network();
        let mut output = vec![];

        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());

        let result = Ipv4Network::from_sql(&CIDR, &output);
        assert!(result.is_ok());

        let ip_network_converted = result.unwrap();
        assert_eq!(ip_network, ip_network_converted);
    }

    #[test]
    fn ivp6_to_sql() {
        let ip_network = return_test_ipv6_network();
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
        assert_eq!(3, output[0]);
        assert_eq!(32, output[1]);
        assert_eq!(1, output[2]);
        assert_eq!(16, output[3]);
        // TODO: IPv6 comparsion
    }

    #[test]
    fn ivp6_both_direction() {
        let ip_network = return_test_ipv6_network();
        let mut output = vec![];

        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());

        let result = Ipv6Network::from_sql(&CIDR, &output);
        assert!(result.is_ok());

        let ip_network_converted = result.unwrap();
        assert_eq!(ip_network, ip_network_converted);
    }

    #[test]
    fn ipnetwork_to_sql_v4() {
        let ip_network = IpNetwork::V4(return_test_ipv4_network());
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
    }

    #[test]
    fn ipnetwork_to_sql_v6() {
        let ip_network = IpNetwork::V6(return_test_ipv6_network());
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
    }
}
<commit_msg>Complete test for postgres_support IPv6 conversion<commit_after>use std::error::Error;
use postgres::types::{FromSql, IsNull, ToSql, Type, CIDR};
use postgres::{accepts, to_sql_checked};
use crate::{IpNetwork, Ipv4Network, Ipv6Network};
use crate::postgres_common;

type PostgresResult<T> = Result<T, Box<Error + Sync + Send>>;

impl FromSql for Ipv4Network {
    fn from_sql(_: &Type, raw: &[u8]) -> PostgresResult<Ipv4Network> {
        postgres_common::from_sql_ipv4_network(raw)
    }

    accepts!(CIDR);
}

impl FromSql for Ipv6Network {
    fn from_sql(_: &Type, raw: &[u8]) -> PostgresResult<Ipv6Network> {
        postgres_common::from_sql_ipv6_network(raw)
    }

    accepts!(CIDR);
}

impl FromSql for IpNetwork {
    fn from_sql(t: &Type, raw: &[u8]) -> PostgresResult<IpNetwork> {
        match raw[0] {
            postgres_common::IPV4_TYPE => Ok(IpNetwork::V4(Ipv4Network::from_sql(t, raw)?)),
            postgres_common::IPV6_TYPE => Ok(IpNetwork::V6(Ipv6Network::from_sql(t, raw)?)),
            _ => Err("CIDR is not IP version 4 or 6".into()),
        }
    }

    accepts!(CIDR);
}

impl ToSql for Ipv4Network {
    fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        let bytes = postgres_common::to_sql_ipv4_network(*self);
        w.extend_from_slice(&bytes);

        Ok(IsNull::No)
    }

    accepts!(CIDR);
    to_sql_checked!();
}

impl ToSql for Ipv6Network {
    fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        let bytes = postgres_common::to_sql_ipv6_network(*self);
        w.extend_from_slice(&bytes);

        Ok(IsNull::No)
    }

    accepts!(CIDR);
    to_sql_checked!();
}

impl ToSql for IpNetwork {
    fn to_sql(&self, t: &Type, w: &mut Vec<u8>) -> PostgresResult<IsNull> {
        match *self {
            IpNetwork::V4(ref network) => network.to_sql(t, w),
            IpNetwork::V6(ref network) => network.to_sql(t, w),
        }
    }

    accepts!(CIDR);
    to_sql_checked!();
}

#[cfg(test)]
mod tests {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use postgres::types::{FromSql, ToSql, CIDR};
    use crate::{IpNetwork, Ipv4Network, Ipv6Network};

    fn return_test_ipv4_network() -> Ipv4Network {
        Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 16).unwrap()
    }

    fn return_test_ipv6_network() -> Ipv6Network {
        Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap()
    }

    #[test]
    fn ivp4_to_sql() {
        let ip_network = return_test_ipv4_network();
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
        assert_eq!(2, output[0]);
        assert_eq!(16, output[1]);
        assert_eq!(1, output[2]);
        assert_eq!(4, output[3]);
        assert_eq!(192, output[4]);
        assert_eq!(168, output[5]);
        assert_eq!(0, output[6]);
        assert_eq!(0, output[7]);
    }

    #[test]
    fn ivp4_both_direction() {
        let ip_network = return_test_ipv4_network();
        let mut output = vec![];

        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());

        let result = Ipv4Network::from_sql(&CIDR, &output);
        assert!(result.is_ok());

        let ip_network_converted = result.unwrap();
        assert_eq!(ip_network, ip_network_converted);
    }

    #[test]
    fn ivp6_to_sql() {
        let ip_network = return_test_ipv6_network();
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
        assert_eq!(3, output[0]);
        assert_eq!(32, output[1]);
        assert_eq!(1, output[2]);
        assert_eq!(16, output[3]);
        assert_eq!(0x20, output[4]);
        assert_eq!(0x01, output[5]);
        assert_eq!(0x0d, output[6]);
        assert_eq!(0xb8, output[7]);
        for i in 8..20 {
            assert_eq!(0, output[i]);
        }
    }

    #[test]
    fn ivp6_both_direction() {
        let ip_network = return_test_ipv6_network();
        let mut output = vec![];

        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());

        let result = Ipv6Network::from_sql(&CIDR, &output);
        assert!(result.is_ok());

        let ip_network_converted = result.unwrap();
        assert_eq!(ip_network, ip_network_converted);
    }

    #[test]
    fn ipnetwork_to_sql_v4() {
        let ip_network = IpNetwork::V4(return_test_ipv4_network());
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
    }

    #[test]
    fn ipnetwork_to_sql_v6() {
        let ip_network = IpNetwork::V6(return_test_ipv6_network());
        let mut output = vec![];
        assert!(ip_network.to_sql(&CIDR, &mut output).is_ok());
    }
}
<|endoftext|>