<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Miscellaneous utilities for low level networking

use bindings::libc;

use packet::PrimitiveValues;

use std::fmt;
use std::str::FromStr;
use std::mem;
use std::u8;
use std::net::IpAddr;

#[cfg(not(windows))] use internal;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2,
         self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MacAddr(a, b, c, d, e, f) =>
                write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
                       a, b, c, d, e, f)
        }
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        (self as &fmt::Display).fmt(fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation issue rather than
//       actually defined - is it useful to provide these errors, or would it be better to just
//       give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(), Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(), Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(), Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(), Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(), Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
}

/// Represents a network interface and its associated addresses
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NetworkInterface {
    /// The name of the interface
    pub name: String,
    /// The interface index (operating system specific)
    pub index: u32,
    /// A MAC address for the interface
    pub mac: Option<MacAddr>,
    /// An IP addresses for the interface
    pub ips: Option<Vec<IpAddr>>,
    /// Operating system specific flags for the interface
    pub flags: u32,
}

impl NetworkInterface {
    /// Retreive the MAC address associated with the interface
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (libc::IFF_LOOPBACK as u32) != 0
    }
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0], (*sll).sll_addr[1],
                              (*sll).sll_addr[2], (*sll).sll_addr[3],
                              (*sll).sll_addr[4], (*sll).sll_addr[5]);
            return (Some(mac), None);
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                        mem::size_of::<libc::sockaddr_storage>());
            return match addr {
                Ok(sa) => (None, Some(sa.ip())),
                Err(_) => (None, None)
            };
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen + 0] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8
                      );
            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                        mem::size_of::<libc::sockaddr_storage>());
            match addr {
                Ok(sa) => (None, Some(sa.ip())),
                Err(_) => (None, None)
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    get_network_interfaces_impl()
}

#[cfg(not(windows))]
fn get_network_interfaces_impl() -> Vec<NetworkInterface> {
    use std::ffi::{CStr, CString};
    use std::str::from_utf8_unchecked;

    let mut ifaces: Vec<NetworkInterface> = Vec::new();
    unsafe {
        let mut addrs: *mut libc::ifaddrs = mem::uninitialized();
        if libc::getifaddrs(&mut addrs) != 0 {
            return ifaces;
        }
        let mut addr = addrs;
        while !addr.is_null() {
            let c_str = (*addr).ifa_name as *const i8;
            let bytes = CStr::from_ptr(c_str).to_bytes();
            let name = from_utf8_unchecked(bytes).to_string();
            let (mac, ip) = sockaddr_to_network_addr((*addr).ifa_addr as *const libc::sockaddr);
            let ni = NetworkInterface {
                name: name.clone(),
                index: 0,
                mac: mac,
                ips: ip.map(|ip| [ip].to_vec()),
                flags: (*addr).ifa_flags
            };
            let mut found: bool = false;
            for iface in ifaces.iter_mut() {
                if name == iface.name {
                    merge(iface, &ni);
                    found = true;
                }
            }
            if !found {
                ifaces.push(ni);
            }

            addr = (*addr).ifa_next;
        }
        libc::freeifaddrs(addrs);

        for iface in ifaces.iter_mut() {
            let name = CString::new(iface.name.as_bytes());
            iface.index = libc::if_nametoindex(name.unwrap().as_ptr());
        }
        return ifaces;
    }

    fn merge(old: &mut NetworkInterface, new: &NetworkInterface) {
        old.mac = match new.mac {
            None => old.mac,
            _ => new.mac
        };
        match (&mut old.ips, &new.ips) {
            (&mut Some(ref mut old_ips), &Some(ref new_ips)) => old_ips.push_all(new_ips.as_slice()),
            _ => {}
        };
        old.flags = old.flags | new.flags;
    }

}

#[cfg(windows)]
fn get_network_interfaces_impl() -> Vec<NetworkInterface> {
    use std::ffi::c_str_to_bytes;
    use std::str::from_utf8;

    use bindings::winpcap;

    let mut adapters_size = 0u32;

    unsafe {
        let mut tmp: winpcap::IP_ADAPTER_INFO = mem::zeroed();
        // FIXME [windows] This only gets IPv4 addresses - should use GetAdaptersAddresses
        winpcap::GetAdaptersInfo(
            &mut tmp,
            &mut adapters_size
        );
    }

    let vec_size = adapters_size / mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32;

    let mut adapters = Vec::with_capacity(vec_size as usize);

    // FIXME [windows] Check return code
    unsafe {
        winpcap::GetAdaptersInfo(adapters.as_mut_ptr(), &mut adapters_size);
    }

    // Create a complete list of NetworkInterfaces for the machine
    let mut cursor = adapters.as_mut_ptr();
    let mut all_ifaces = Vec::with_capacity(vec_size as usize);
    while !cursor.is_null() {
        let mac = unsafe {
                    MacAddr((*cursor).Address[0],
                            (*cursor).Address[1],
                            (*cursor).Address[2],
                            (*cursor).Address[3],
                            (*cursor).Address[4],
                            (*cursor).Address[5])
                  };
        let mut ip_cursor = unsafe { &mut (*cursor).IpAddressList as winpcap::PIP_ADDR_STRING};
        let mut ips: Vec<IpAddr> = Vec::new();
        while !ip_cursor.is_null() {
            let ref ip_str_ptr = unsafe { &(*ip_cursor) }.IpAddress.String.as_ptr() as *const i8;
            let ip_str = from_utf8(unsafe { c_str_to_bytes(ip_str_ptr) }).unwrap();
            ips.push(ip_str.parse().unwrap());
            ip_cursor = unsafe { (*ip_cursor).Next };
        }

        unsafe {
            let ref name_str_ptr =(*cursor).AdapterName.as_ptr() as *const i8;

            all_ifaces.push(NetworkInterface {
                        name: String::from_utf8_unchecked(c_str_to_bytes(name_str_ptr).to_owned()),
                        index: (*cursor).Index,
                        mac: Some(mac),
                        ips: Some(ips),
                        //flags: (*cursor).Type, // FIXME [windows]
                        flags: 0,
                     });

            cursor = (*cursor).Next;
        }
    }

    let mut buf = [0u8; 4096];
    let mut buflen = buf.len() as u32;

    // Gets list of supported adapters in form:
    // adapter1\0adapter2\0\0desc1\0desc2\0\0
    if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 {
        // FIXME [windows] Should allocate a buffer big enough and try again
        //        - size should be buf.len() + buflen (buflen is overwritten)
        panic!("FIXME [windows] unable to get interface list");
    }

    let buf_str = from_utf8(&buf).unwrap();
    let iface_names = buf_str.split_str("\0\0").next();
    let mut vec = Vec::new();

    // Return only supported adapters
    match iface_names {
        Some(iface_names) => {
            for iface in iface_names.split('\0') {
                let name = iface.to_string();
                let next = all_ifaces.iter()
                                     .filter(|x| name.as_slice().ends_with(x.name.as_slice()))
                                     .next();
                if next.is_some() {
                    let mut iface = next.unwrap().clone();
                    iface.name = name;
                    vec.push(iface);
                }
            }
        },
        None => ()
    };

    vec
}

<commit_msg>Fixing bug in `get_network_interfaces_impl` interface merging.<commit_after>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Miscellaneous utilities for low level networking

use bindings::libc;

use packet::PrimitiveValues;

use std::fmt;
use std::str::FromStr;
use std::mem;
use std::u8;
use std::net::IpAddr;

#[cfg(not(windows))] use internal;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2,
         self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MacAddr(a, b, c, d, e, f) =>
                write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
                       a, b, c, d, e, f)
        }
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        (self as &fmt::Display).fmt(fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation issue rather than
//       actually defined - is it useful to provide these errors, or would it be better to just
//       give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(), Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(), Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(), Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(), Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(), Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(), Err(ParseMacAddrErr::InvalidComponent));
}

/// Represents a network interface and its associated addresses
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NetworkInterface {
    /// The name of the interface
    pub name: String,
    /// The interface index (operating system specific)
    pub index: u32,
    /// A MAC address for the interface
    pub mac: Option<MacAddr>,
    /// An IP addresses for the interface
    pub ips: Option<Vec<IpAddr>>,
    /// Operating system specific flags for the interface
    pub flags: u32,
}

impl NetworkInterface {
    /// Retreive the MAC address associated with the interface
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (libc::IFF_LOOPBACK as u32) != 0
    }
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0], (*sll).sll_addr[1],
                              (*sll).sll_addr[2], (*sll).sll_addr[3],
                              (*sll).sll_addr[4], (*sll).sll_addr[5]);
            return (Some(mac), None);
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                        mem::size_of::<libc::sockaddr_storage>());
            return match addr {
                Ok(sa) => (None, Some(sa.ip())),
                Err(_) => (None, None)
            };
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen + 0] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8
                      );
            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                        mem::size_of::<libc::sockaddr_storage>());
            match addr {
                Ok(sa) => (None, Some(sa.ip())),
                Err(_) => (None, None)
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    get_network_interfaces_impl()
}

#[cfg(not(windows))]
fn get_network_interfaces_impl() -> Vec<NetworkInterface> {
    use std::ffi::{CStr, CString};
    use std::str::from_utf8_unchecked;

    let mut ifaces: Vec<NetworkInterface> = Vec::new();
    unsafe {
        let mut addrs: *mut libc::ifaddrs = mem::uninitialized();
        if libc::getifaddrs(&mut addrs) != 0 {
            return ifaces;
        }
        let mut addr = addrs;
        while !addr.is_null() {
            let c_str = (*addr).ifa_name as *const i8;
            let bytes = CStr::from_ptr(c_str).to_bytes();
            let name = from_utf8_unchecked(bytes).to_string();
            let (mac, ip) = sockaddr_to_network_addr((*addr).ifa_addr as *const libc::sockaddr);
            let ni = NetworkInterface {
                name: name.clone(),
                index: 0,
                mac: mac,
                ips: ip.map(|ip| [ip].to_vec()),
                flags: (*addr).ifa_flags
            };
            let mut found: bool = false;
            for iface in ifaces.iter_mut() {
                if name == iface.name {
                    merge(iface, &ni);
                    found = true;
                }
            }
            if !found {
                ifaces.push(ni);
            }

            addr = (*addr).ifa_next;
        }
        libc::freeifaddrs(addrs);

        for iface in ifaces.iter_mut() {
            let name = CString::new(iface.name.as_bytes());
            iface.index = libc::if_nametoindex(name.unwrap().as_ptr());
        }
        return ifaces;
    }

    fn merge(old: &mut NetworkInterface, new: &NetworkInterface) {
        old.mac = match new.mac {
            None => old.mac,
            _ => new.mac
        };
        match (&mut old.ips, &new.ips) {
            (&mut Some(ref mut old_ips), &Some(ref new_ips)) => old_ips.push_all(new_ips.as_slice()),
            (&mut ref mut old_ips @ None, &Some(ref new_ips)) => *old_ips = Some(new_ips.clone()),
            _ => {}
        };
        old.flags = old.flags | new.flags;
    }

}

#[cfg(windows)]
fn get_network_interfaces_impl() -> Vec<NetworkInterface> {
    use std::ffi::c_str_to_bytes;
    use std::str::from_utf8;

    use bindings::winpcap;

    let mut adapters_size = 0u32;

    unsafe {
        let mut tmp: winpcap::IP_ADAPTER_INFO = mem::zeroed();
        // FIXME [windows] This only gets IPv4 addresses - should use GetAdaptersAddresses
        winpcap::GetAdaptersInfo(
            &mut tmp,
            &mut adapters_size
        );
    }

    let vec_size = adapters_size / mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32;

    let mut adapters = Vec::with_capacity(vec_size as usize);

    // FIXME [windows] Check return code
    unsafe {
        winpcap::GetAdaptersInfo(adapters.as_mut_ptr(), &mut adapters_size);
    }

    // Create a complete list of NetworkInterfaces for the machine
    let mut cursor = adapters.as_mut_ptr();
    let mut all_ifaces = Vec::with_capacity(vec_size as usize);
    while !cursor.is_null() {
        let mac = unsafe {
                    MacAddr((*cursor).Address[0],
                            (*cursor).Address[1],
                            (*cursor).Address[2],
                            (*cursor).Address[3],
                            (*cursor).Address[4],
                            (*cursor).Address[5])
                  };
        let mut ip_cursor = unsafe { &mut (*cursor).IpAddressList as winpcap::PIP_ADDR_STRING};
        let mut ips: Vec<IpAddr> = Vec::new();
        while !ip_cursor.is_null() {
            let ref ip_str_ptr = unsafe { &(*ip_cursor) }.IpAddress.String.as_ptr() as *const i8;
            let ip_str = from_utf8(unsafe { c_str_to_bytes(ip_str_ptr) }).unwrap();
            ips.push(ip_str.parse().unwrap());
            ip_cursor = unsafe { (*ip_cursor).Next };
        }

        unsafe {
            let ref name_str_ptr =(*cursor).AdapterName.as_ptr() as *const i8;

            all_ifaces.push(NetworkInterface {
                        name: String::from_utf8_unchecked(c_str_to_bytes(name_str_ptr).to_owned()),
                        index: (*cursor).Index,
                        mac: Some(mac),
                        ips: Some(ips),
                        //flags: (*cursor).Type, // FIXME [windows]
                        flags: 0,
                     });

            cursor = (*cursor).Next;
        }
    }

    let mut buf = [0u8; 4096];
    let mut buflen = buf.len() as u32;

    // Gets list of supported adapters in form:
    // adapter1\0adapter2\0\0desc1\0desc2\0\0
    if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 {
        // FIXME [windows] Should allocate a buffer big enough and try again
        //        - size should be buf.len() + buflen (buflen is overwritten)
        panic!("FIXME [windows] unable to get interface list");
    }

    let buf_str = from_utf8(&buf).unwrap();
    let iface_names = buf_str.split_str("\0\0").next();
    let mut vec = Vec::new();

    // Return only supported adapters
    match iface_names {
        Some(iface_names) => {
            for iface in iface_names.split('\0') {
                let name = iface.to_string();
                let next = all_ifaces.iter()
                                     .filter(|x| name.as_slice().ends_with(x.name.as_slice()))
                                     .next();
                if next.is_some() {
                    let mut iface = next.unwrap().clone();
                    iface.name = name;
                    vec.push(iface);
                }
            }
        },
        None => ()
    };

    vec
}

<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

pub fn parse_update_count(tag: String) -> u64 {
    tag.split(' ').last().unwrap().parse().unwrap_or(0)
}

pub fn read_all<R: Read>(r: &mut R, mut buf: &mut [u8]) -> io::Result<()> {
    while !buf.is_empty() {
        match r.read(buf) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "unexpected EOF")),
            Ok(len) => buf = &mut {buf}[len..],
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<commit_msg>Disable rustfmt on a line we don't want changed<commit_after>use std::io;
use std::io::prelude::*;

pub fn parse_update_count(tag: String) -> u64 {
    tag.split(' ').last().unwrap().parse().unwrap_or(0)
}

pub fn read_all<R: Read>(r: &mut R, mut buf: &mut [u8]) -> io::Result<()> {
    while !buf.is_empty() {
        match r.read(buf) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "unexpected EOF")),
            #[cfg_attr(rustfmt, rustfmt_skip)]
            Ok(len) => buf = &mut {buf}[len..],
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::ptr;
use glib::Error;
use glib::ffi::{GList};
use glib::object::{Wrapper};
use glib::translate::{ToGlibPtr, FromGlibPtrContainer};
use glib::glib_container::GlibContainer;
use glib::types::StaticType;
use secret_item::SecretItem;
use secret_collection::SecretCollection;
use ffi;
use SecretResult;

pub fn lock_object<W: Wrapper>(obj: &W) -> SecretResult<Vec<W>>{
    let my_type = W::static_type();
    assert!(my_type == SecretItem::static_type() || my_type == SecretCollection::static_type(), "Can only lock items or collections");
    let mut err = ptr::null_mut();
    let mut res = ptr::null_mut();
    let arr = [obj];
    let slice = (&arr[..]).to_glib_none();
    unsafe {
        ffi::secret_service_lock_sync(ptr::null_mut(), slice.0 as *mut GList, ptr::null_mut(), &mut res, &mut err);
        if err.is_null() {
            Ok(Vec::from_glib_full(res))
        } else {
            Err(Error::wrap(err))
        }
    }
}

pub fn unlock_object<W: Wrapper>(obj: &W) -> SecretResult<Vec<W>>{
    let my_type = W::static_type();
    assert!(my_type == SecretItem::static_type() || my_type == SecretCollection::static_type(), "Can only unlock items or collections");
    let mut err = ptr::null_mut();
    let mut res = ptr::null_mut();
    let arr = [obj];
    let slice = (&arr[..]).to_glib_none();
    unsafe {
        ffi::secret_service_unlock_sync(ptr::null_mut(), slice.0 as *mut GList, ptr::null_mut(), &mut res, &mut err);
        if err.is_null() {
            Ok(Vec::from_glib_full(res))
        } else {
            Err(Error::wrap(err))
        }
    }
}
<commit_msg>Only check type in debug mode<commit_after>use std::ptr;
use glib::Error;
use glib::ffi::{GList};
use glib::object::{Wrapper};
use glib::translate::{ToGlibPtr, FromGlibPtrContainer};
use glib::glib_container::GlibContainer;
use glib::types::StaticType;
use secret_item::SecretItem;
use secret_collection::SecretCollection;
use ffi;
use SecretResult;

pub fn lock_object<W: Wrapper>(obj: &W) -> SecretResult<Vec<W>>{
    debug_assert!(W::static_type() == SecretItem::static_type() || W::static_type() == SecretCollection::static_type(), "Can only lock items or collections");
    let mut err = ptr::null_mut();
    let mut res = ptr::null_mut();
    let arr = [obj];
    let slice = (&arr[..]).to_glib_none();
    unsafe {
        ffi::secret_service_lock_sync(ptr::null_mut(), slice.0 as *mut GList, ptr::null_mut(), &mut res, &mut err);
        if err.is_null() {
            Ok(Vec::from_glib_full(res))
        } else {
            Err(Error::wrap(err))
        }
    }
}

pub fn unlock_object<W: Wrapper>(obj: &W) -> SecretResult<Vec<W>>{
    debug_assert!(W::static_type() == SecretItem::static_type() || W::static_type() == SecretCollection::static_type(), "Can only unlock items or collections");
    let mut err = ptr::null_mut();
    let mut res = ptr::null_mut();
    let arr = [obj];
    let slice = (&arr[..]).to_glib_none();
    unsafe {
        ffi::secret_service_unlock_sync(ptr::null_mut(), slice.0 as *mut GList, ptr::null_mut(), &mut res, &mut err);
        if err.is_null() {
            Ok(Vec::from_glib_full(res))
        } else {
            Err(Error::wrap(err))
        }
    }
}
<|endoftext|>


<commit_before>use super::{Features, Mesh, Region};
use crate::mesh::allocate_state;
use crate::shapes::{geo_class_and_kind, GeoKind, Shape, StateOfShape};
use crate::StrError;
use plotpy::{Canvas, Curve, Plot, PolyCode, Text};
use russell_lab::Vector;
use std::collections::HashMap;

// Returns the first point on GeoClass:Lin; Bezier t=0(ξ=-1)
macro_rules! pa {
    ($state:expr,$i:expr) => {
        $state.coords_transp[$i][0]
    };
}

// Returns the last point on GeoClass:Lin; Bezier t=1(ξ=1)
macro_rules! pb {
    ($state:expr,$i:expr) => {
        $state.coords_transp[$i][1]
    };
}

pub struct Draw {
    pub canvas_edges: Canvas,
    pub canvas_nodes: Curve,
    pub canvas_nodes_labels: Text,
    pub nodes: bool,
    pub nodes_labels: bool,
    pub set_range: bool,
}

impl Draw {
    pub fn new() -> Self {
        let mut canvas_edges = Canvas::new();
        let mut canvas_nodes = Curve::new();
        let mut canvas_nodes_labels = Text::new();
        canvas_edges
            .set_stop_clip(true)
            .set_face_color("None")
            .set_line_width(2.0)
            .set_edge_color("#2440cd");
        canvas_nodes
            .set_stop_clip(true)
            .set_marker_color("black")
            .set_marker_line_color("white")
            .set_marker_style("o")
            .set_line_style("None");
        canvas_nodes_labels.set_color("red").set_fontsize(6.0);
        Draw {
            canvas_edges,
            canvas_nodes,
            canvas_nodes_labels,
            nodes: true,
            nodes_labels: false,
            set_range: true,
        }
    }

    pub fn edges(&mut self, plot: &mut Plot, region: &Region) -> Result<(), StrError> {
        if region.mesh.space_ndim == 2 {
            self.edges_2d(plot, &region.mesh, &region.features)
        } else {
            self.edges_3d(plot, &region.mesh, &region.features)
        }
    }

    fn edges_2d(&mut self, plot: &mut Plot, mesh: &Mesh, features: &Features) -> Result<(), StrError> {
        // space dimension
        let space_ndim = mesh.space_ndim;
        assert_eq!(space_ndim, 2);

        // middle points (p) on Bezier curve and control points (q)
        let mut pc = Vector::new(space_ndim); // first middle point on Bezier curve with t=⅓(ξ=-⅓) or t=½(ξ=0)
        let mut pd = Vector::new(space_ndim); // second middle point on Bezier curve with t=⅔(ξ=+⅓)
        let mut qc = Vector::new(space_ndim); // control point corresponding to pc
        let mut qd = Vector::new(space_ndim); // control point corresponding to pd

        // reference coordinate
        let mut ksi = vec![0.0; space_ndim];

        // memoization of shapes and states
        let mut shapes_memo: HashMap<GeoKind, Shape> = HashMap::new();
        let mut states_memo: HashMap<GeoKind, StateOfShape> = HashMap::new();

        // begin poly-curve
        self.canvas_edges.polycurve_begin();

        // loop over edges (GeoKind::Lin)
        const EDGE_GEO_NDIM: usize = 1;
        for (_, edge) in &features.edges {
            // number of nodes on edge and kind
            let nnode = edge.points.len();
            let (_, kind) = geo_class_and_kind(EDGE_GEO_NDIM, nnode)?;

            // retrieve shape or allocate new
            let shape = shapes_memo
                .entry(kind)
                .or_insert(Shape::new(space_ndim, EDGE_GEO_NDIM, nnode)?);

            // retrieve state or allocate new
            let mut state = states_memo
                .entry(kind)
                .or_insert(allocate_state(&mesh, EDGE_GEO_NDIM, &edge.points)?);

            // set coordinates of GeoKind::Lin
            for m in 0..nnode {
                for i in 0..space_ndim {
                    state.coords_transp[i][m] = mesh.points[edge.points[m]].coords[i];
                }
            }

            // add poly-curve points (or control points for quadratic/cubic Bezier)
            match nnode {
                2 => {
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::LineTo);
                }
                3 => {
                    ksi[0] = 0.0; // middle
                    shape.calc_coords(&mut pc, &mut state, &ksi)?;
                    qc[0] = (-pa!(state, 0) - pb!(state, 0) + 4.0 * pc[0]) / 2.0;
                    qc[1] = (-pa!(state, 1) - pb!(state, 1) + 4.0 * pc[1]) / 2.0;
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(/*   */ qc[0], /*   */ qc[1], PolyCode::Curve3)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::Curve3);
                }
                _ => {
                    ksi[0] = -1.0 / 3.0; // => t=⅓(ξ=-⅓)
                    shape.calc_coords(&mut pc, &mut state, &ksi)?;
                    ksi[0] = 1.0 / 3.0; // => t=⅔(ξ=+⅓)
                    shape.calc_coords(&mut pd, &mut state, &ksi)?;
                    qc[0] = (-5.0 * pa!(state, 0) + 2.0 * pb!(state, 0) + 18.0 * pc[0] - 9.0 * pd[0]) / 6.0;
                    qc[1] = (-5.0 * pa!(state, 1) + 2.0 * pb!(state, 1) + 18.0 * pc[1] - 9.0 * pd[1]) / 6.0;
                    qd[0] = (2.0 * pa!(state, 0) - 5.0 * pb!(state, 0) - 9.0 * pc[0] + 18.0 * pd[0]) / 6.0;
                    qd[1] = (2.0 * pa!(state, 1) - 5.0 * pb!(state, 1) - 9.0 * pc[1] + 18.0 * pd[1]) / 6.0;
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(/*   */ qc[0], /*   */ qc[1], PolyCode::Curve4)
                        .polycurve_add(/*   */ qd[0], /*   */ qd[1], PolyCode::Curve4)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::Curve4);
                }
            }
        }

        // end polycurve and add to plot
        self.canvas_edges.polycurve_end(false);
        plot.add(&self.canvas_edges);
        if self.set_range {
            plot.set_range(features.min[0], features.max[0], features.min[1], features.max[1]);
        }

        // add nodes
        if self.nodes {
            // plt.nodes.begin();
            // for p in &region.features.points {
            //     plt.nodes
            //         .add(region.mesh.points[*p].coords[0], region.mesh.points[*p].coords[1])
            // }
            // plt.nodes.end();
            // plot.add(&plt.nodes);
        }
        Ok(())
    }

    fn edges_3d(&mut self, _plot: &mut Plot, _mesh: &Mesh, _features: &Features) -> Result<(), StrError> {
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::Draw;
    use crate::mesh::{Extract, Region, Samples};
    use crate::StrError;
    use plotpy::{Canvas, Plot};

    #[test]
    fn draw_edges_works() -> Result<(), StrError> {
        // draw reference circles
        let mut plot = Plot::new();
        let mut circle_in = Canvas::new();
        let mut circle_mi = Canvas::new();
        let mut circle_ou = Canvas::new();
        circle_in
            .set_face_color("None")
            .set_edge_color("magenta")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 1.0);
        circle_mi
            .set_face_color("None")
            .set_edge_color("cyan")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 1.5);
        circle_ou
            .set_face_color("None")
            .set_edge_color("orange")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 2.0);
        plot.add(&circle_in);
        plot.add(&circle_mi);
        plot.add(&circle_ou);

        // draw edges
        let mesh = Samples::ring_eight_qua8_rad1_thick1();
        let region = Region::with(mesh, Extract::All)?;
        let mut draw = Draw::new();
        draw.edges(&mut plot, &region)?;

        // save figure
        plot.set_figure_size_points(800.0, 800.0)
            .set_equal_axes(true)
            .save("/tmp/gemlab/draw_edges_works.svg")?;
        Ok(())
    }
}
<commit_msg>Improve Draw<commit_after>use super::{Features, Mesh, Region};
use crate::mesh::allocate_state;
use crate::shapes::{geo_class_and_kind, GeoKind, Shape, StateOfShape};
use crate::StrError;
use plotpy::{Canvas, Curve, Plot, PolyCode, Text};
use russell_lab::Vector;
use std::collections::HashMap;

// Returns the first point on GeoClass:Lin; Bezier t=0(ξ=-1)
macro_rules! pa {
    ($state:expr,$i:expr) => {
        $state.coords_transp[$i][0]
    };
}

// Returns the last point on GeoClass:Lin; Bezier t=1(ξ=1)
macro_rules! pb {
    ($state:expr,$i:expr) => {
        $state.coords_transp[$i][1]
    };
}

pub struct Draw {
    pub canvas_edges: Canvas,
    pub canvas_nodes: Curve,
    pub canvas_nodes_labels: Text,
    pub nodes: bool,
    pub nodes_labels: bool,
    pub set_range: bool,
}

impl Draw {
    pub fn new() -> Self {
        let mut canvas_edges = Canvas::new();
        let mut canvas_nodes = Curve::new();
        let mut canvas_nodes_labels = Text::new();
        canvas_edges
            .set_stop_clip(true)
            .set_face_color("None")
            .set_line_width(2.0)
            .set_edge_color("#2440cd");
        canvas_nodes
            .set_stop_clip(true)
            .set_marker_color("black")
            .set_marker_line_color("white")
            .set_marker_style("o")
            .set_line_style("None");
        canvas_nodes_labels.set_color("red").set_fontsize(6.0);
        Draw {
            canvas_edges,
            canvas_nodes,
            canvas_nodes_labels,
            nodes: true,
            nodes_labels: false,
            set_range: true,
        }
    }

    pub fn edges(&mut self, plot: &mut Plot, region: &Region) -> Result<(), StrError> {
        if region.mesh.space_ndim == 2 {
            self.edges_2d(plot, &region.mesh, &region.features)
        } else {
            self.edges_3d(plot, &region.mesh, &region.features)
        }
    }

    fn edges_2d(&mut self, plot: &mut Plot, mesh: &Mesh, features: &Features) -> Result<(), StrError> {
        // space dimension
        let space_ndim = mesh.space_ndim;
        assert_eq!(space_ndim, 2);

        // middle points (p) on Bezier curve and control points (q)
        let mut pc = Vector::new(space_ndim); // first middle point on Bezier curve with t=⅓(ξ=-⅓) or t=½(ξ=0)
        let mut pd = Vector::new(space_ndim); // second middle point on Bezier curve with t=⅔(ξ=+⅓)
        let mut qc = Vector::new(space_ndim); // control point corresponding to pc
        let mut qd = Vector::new(space_ndim); // control point corresponding to pd

        // reference coordinate
        let mut ksi = vec![0.0; space_ndim];

        // memoization of shapes and states
        let mut shapes_memo: HashMap<GeoKind, Shape> = HashMap::new();
        let mut states_memo: HashMap<GeoKind, StateOfShape> = HashMap::new();

        // begin poly-curve
        self.canvas_edges.polycurve_begin();

        // loop over edges (GeoKind::Lin)
        const EDGE_GEO_NDIM: usize = 1;
        for (_, edge) in &features.edges {
            // number of nodes on edge and kind
            let nnode = edge.points.len();
            let (_, kind) = geo_class_and_kind(EDGE_GEO_NDIM, nnode)?;

            // retrieve shape or allocate new
            let shape = shapes_memo
                .entry(kind)
                .or_insert(Shape::new(space_ndim, EDGE_GEO_NDIM, nnode)?);

            // retrieve state or allocate new
            let mut state = states_memo
                .entry(kind)
                .or_insert(allocate_state(&mesh, EDGE_GEO_NDIM, &edge.points)?);

            // set coordinates of GeoKind::Lin
            for m in 0..nnode {
                for i in 0..space_ndim {
                    state.coords_transp[i][m] = mesh.points[edge.points[m]].coords[i];
                }
            }

            // add poly-curve points (or control points for quadratic/cubic Bezier)
            match nnode {
                2 => {
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::LineTo);
                }
                3 => {
                    ksi[0] = 0.0; // middle
                    shape.calc_coords(&mut pc, &mut state, &ksi)?;
                    qc[0] = (-pa!(state, 0) - pb!(state, 0) + 4.0 * pc[0]) / 2.0;
                    qc[1] = (-pa!(state, 1) - pb!(state, 1) + 4.0 * pc[1]) / 2.0;
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(/*   */ qc[0], /*   */ qc[1], PolyCode::Curve3)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::Curve3);
                }
                _ => {
                    ksi[0] = -1.0 / 3.0; // => t=⅓(ξ=-⅓)
                    shape.calc_coords(&mut pc, &mut state, &ksi)?;
                    ksi[0] = 1.0 / 3.0; // => t=⅔(ξ=+⅓)
                    shape.calc_coords(&mut pd, &mut state, &ksi)?;
                    qc[0] = (-5.0 * pa!(state, 0) + 2.0 * pb!(state, 0) + 18.0 * pc[0] - 9.0 * pd[0]) / 6.0;
                    qc[1] = (-5.0 * pa!(state, 1) + 2.0 * pb!(state, 1) + 18.0 * pc[1] - 9.0 * pd[1]) / 6.0;
                    qd[0] = (2.0 * pa!(state, 0) - 5.0 * pb!(state, 0) - 9.0 * pc[0] + 18.0 * pd[0]) / 6.0;
                    qd[1] = (2.0 * pa!(state, 1) - 5.0 * pb!(state, 1) - 9.0 * pc[1] + 18.0 * pd[1]) / 6.0;
                    self.canvas_edges
                        .polycurve_add(pa!(state, 0), pa!(state, 1), PolyCode::MoveTo)
                        .polycurve_add(/*   */ qc[0], /*   */ qc[1], PolyCode::Curve4)
                        .polycurve_add(/*   */ qd[0], /*   */ qd[1], PolyCode::Curve4)
                        .polycurve_add(pb!(state, 0), pb!(state, 1), PolyCode::Curve4);
                }
            }
        }

        // end polycurve and add to plot
        self.canvas_edges.polycurve_end(false);
        plot.add(&self.canvas_edges);
        if self.set_range {
            plot.set_range(features.min[0], features.max[0], features.min[1], features.max[1]);
        }

        // add nodes
        if self.nodes {
            self.canvas_nodes.points_begin();
            for p in &features.points {
                self.canvas_nodes
                    .points_add(mesh.points[*p].coords[0], mesh.points[*p].coords[1]);
            }
            self.canvas_nodes.points_end();
            plot.add(&self.canvas_nodes);
        }
        Ok(())
    }

    fn edges_3d(&mut self, _plot: &mut Plot, _mesh: &Mesh, _features: &Features) -> Result<(), StrError> {
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::Draw;
    use crate::mesh::{Extract, Region, Samples};
    use crate::StrError;
    use plotpy::{Canvas, Plot};

    #[test]
    fn draw_edges_works() -> Result<(), StrError> {
        // draw reference circles
        let mut plot = Plot::new();
        let mut circle_in = Canvas::new();
        let mut circle_mi = Canvas::new();
        let mut circle_ou = Canvas::new();
        circle_in
            .set_face_color("None")
            .set_edge_color("magenta")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 1.0);
        circle_mi
            .set_face_color("None")
            .set_edge_color("cyan")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 1.5);
        circle_ou
            .set_face_color("None")
            .set_edge_color("orange")
            .set_line_width(7.0)
            .draw_circle(0.0, 0.0, 2.0);
        plot.add(&circle_in);
        plot.add(&circle_mi);
        plot.add(&circle_ou);

        // draw edges
        let mesh = Samples::ring_eight_qua8_rad1_thick1();
        let region = Region::with(mesh, Extract::All)?;
        let mut draw = Draw::new();
        draw.edges(&mut plot, &region)?;

        // save figure
        plot.set_figure_size_points(800.0, 800.0)
            .set_equal_axes(true)
            .save("/tmp/gemlab/draw_edges_works.svg")?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use yew::prelude::*;

use crate::{
    schema::{Field, Multiplicity, SCHEMA},
    types::*,
};

impl Model {
    pub fn view_actions(&self) -> Html {
        let actions = vec![
            Action {
                image: None,
                text: "store".to_string(),
                msg: Msg::Store,
            },
            Action {
                image: None,
                text: "load".to_string(),
                msg: Msg::Load,
            },
            Action {
                image: None,
                text: "Normal mode".to_string(),
                msg: Msg::SetMode(Mode::Normal),
            },
            Action {
                image: None,
                text: "Edit mode".to_string(),
                msg: Msg::SetMode(Mode::Edit),
            },
            Action {
                image: Some("gg-arrow-left".to_string()),
                text: "prev".to_string(),
                msg: Msg::Prev,
            },
            Action {
                image: Some("gg-arrow-right".to_string()),
                text: "next".to_string(),
                msg: Msg::Next,
            },
            Action {
                image: Some("gg-corner-right-up".to_string()),
                text: "parent".to_string(),
                msg: Msg::Parent,
            },
            Action {
                image: Some("gg-corner-double-up-right".to_string()),
                text: "+item".to_string(),
                msg: Msg::AddItem,
            },
            Action {
                image: Some("gg-close".to_string()),
                text: "delete".to_string(),
                msg: Msg::DeleteItem,
            },
        ];
        let actions = actions
            .iter()
            // .filter(|a| self.is_valid_action(a))
            .map(|a| self.view_action(a));

        html! {

                { for actions }

        }
    }

    pub fn current_field(&self) -> Option<Field> {
        self.field(&self.cursor)
    }

    pub fn field(&self, path: &Path) -> Option<Field> {
        let parent_path = parent(&self.cursor);

        match path.back() {
            Some(selector) => {
                let parent_ref = self.lookup_path(&self.file.root, &parent_path).unwrap();
                let parent = self.lookup(&parent_ref).unwrap();
                SCHEMA
                    .get_kind(&parent.kind)
                    .unwrap()
                    .get_field(&selector.field)
            }
            None => None,
        }
    }

    fn view_action(&self, action: &Action) -> Html {
        let msg = action.msg.clone();
        let callback = self.link.callback(move |_: MouseEvent| msg.clone());
        let img = match &action.image {
            Some(image) => html! {

            },
            None => html! {},
        };
        html! {
            <button
              class="action hover:bg-blue-200 hover:text-blue-800 group flex items-center rounded-md bg-blue-100 text-blue-600 text-sm font-medium px-4 py-2"
              onclick=callback>
                { img }
                { &action.text }
            </button>
        }
    }

    pub fn view_file(&self, file: &File) -> Html {
        let node = self.view_node(Some(file.root.clone()), &Path::new());
        html! {

{ node }

        }
    }

    pub fn view_file_json(&self, file: &File) -> Html {
        let serialized = serde_json::to_string_pretty(file).expect("could not serialize to JSON");
        html! {

{ serialized }

        }
    }

    pub fn traverse_fields(node: &Node) -> Vec<Field> {
        let kind = &node.kind;
        match SCHEMA.get_kind(kind) {
            Some(kind) => kind.get_fields(),
            None => vec![],
        }
    }

    pub fn flatten_paths(&self, reference: &Ref, base: Path) -> Vec<Path> {
        log::info!("flatten: {:?} {:?}", reference, base);
        match &self.lookup(reference) {
            Some(node) => {
                let mut paths = vec![];
                for field in Model::traverse_fields(&node) {
                    let mut children = node.children.get(field.name).cloned().unwrap_or_default();
                    match field.multiplicity {
                        Multiplicity::Single => {
                            if children.is_empty() {
                                children.push("dummy".to_string());
                            }
                        }
                        Multiplicity::Repeated => {}
                    };
                    for (n, child) in children.iter().enumerate() {
                        let new_base = append(
                            &base,
                            Selector {
                                field: field.name.to_string(),
                                index: n,
                            },
                        );
                        paths.push(new_base.clone());
                        log::info!("child: {:?}[{:?}]->{:?}", reference, field.name, child);
                        paths.extend(self.flatten_paths(child, new_base));
                    }
                    match field.multiplicity {
                        // If repeated field, stay on the parent.
                        Multiplicity::Repeated => {
                            let new_base = append(
                                &base,
                                Selector {
                                    field: field.name.to_string(),
                                    index: children.len(),
                                },
                            );
                            paths.push(new_base.clone());
                        }
                        // If single field, skip directly to the first (and only) child.
                        Multiplicity::Single => {}
                    }
                }
                paths
            }
            None => vec![],
        }
    }

    fn view_node(&self, reference: Option<Ref>, path: &Path) -> Html {
        self.view_node_with_placeholder(reference, path, "◆")
    }

    fn view_node_with_placeholder(
        &self,
        reference: Option<Ref>,
        path: &Path,
        placeholder: &str,
    ) -> Html {
        let mut classes = vec!["node".to_string()];
        if path == &self.cursor {
            classes.push("selected".to_string());
        }
        if path == &self.hover {
            classes.push("hover".to_string());
        }

        let path_clone = path.clone();
        let onclick = self.link.callback(move |e: MouseEvent| {
            e.stop_propagation();
            Msg::Select(path_clone.clone())
        });

        let path_clone = path.clone();
        let onmouseover = self.link.callback(move |e: MouseEvent| {
            e.stop_propagation();
            Msg::Hover(path_clone.clone())
        });

        let path_clone = path.clone();
        let oninput = self.link.callback(move |e: InputData| {
            crate::types::Msg::SetNodeCommand(path_clone.clone(), e.value.clone())
        });

        let node_state = self.node_state.get(&path);

        let suggestions: Vec<_> = node_state
            .map(|v| v.parsed_commands.clone())
            .unwrap_or_default()
            .iter()
            .map(|v| {
                let path_clone = path.clone();
                let value_string = v.value.clone().unwrap_or_default();
                let node = v.to_node().unwrap();
                let onclick = self.link.callback(move |e: MouseEvent| {
                    Msg::ReplaceNode(path_clone.clone(), node.clone())
                });
                let classes_item = vec!["block", "border"];
                html! {
                    {value_string}
                }
            })
            .collect();

        let value = match reference {
            Some(reference) => match self.lookup(&reference) {
                Some(node) => self.view_value(&node, &path),
                None => {
                    html! {
                        { format!("invalid: {}", reference) }
                    }
                }
            },
            None => {
                let classes_dropdown = vec!["absolute", "z-10", "bg-white"];
                html! {

{ placeholder }

                            {""}

                                {for suggestions}
                                // {"link 1"}
                                // {"link 2"}
                                // {"link 3"}

                }
            }
        };
        html! {

                { value }

        }
    }

    pub fn view_child(&self, node: &Node, field_name: &str, path: &Path) -> Html {
        self.view_child_with_placeholder(node, field_name, path, field_name)
    }

    pub fn view_child_with_placeholder(
        &self,
        node: &Node,
        field_name: &str,
        path: &Path,
        placeholder: &str,
    ) -> Html {
        let path = append(
            &path,
            Selector {
                field: field_name.to_string(),
                index: 0,
            },
        );
        let reference = node
            .children
            .get(field_name)
            .and_then(|v| v.get(0))
            .cloned();
        // Empty list vs hole vs special value?
        // TODO: How to traverse nested lists in preorder?
        self.view_node_with_placeholder(reference, &path, placeholder)
    }

    /// Returns the head and children, separately.
    pub fn view_children(&self, node: &Node, field_name: &str, path: &Path) -> (Html, Vec) {
        // let path = append(&path, field(field_name));
        // let cursor = sub_cursor(&cursor, field(field_name));
        let empty = vec![];
        let children = node.children.get(field_name).unwrap_or(&empty);
        let head = {
            let path = append(
                &path,
                Selector {
                    field: field_name.to_string(),
                    index: children.len(),
                },
            );
            let mut classes = vec!["node".to_string(), "placeholder".to_string()];
            if path == self.cursor {
                classes.push("selected".to_string());
            }
            if path == self.hover {
                classes.push("hover".to_string());
            }

            let path_clone = path.clone();
            let onclick = self.link.callback(move |e: MouseEvent| {
                e.stop_propagation();
                Msg::Select(path_clone.clone())
            });

            let path_clone = path.clone();
            let onmouseover = self.link.callback(move |e: MouseEvent| {
                e.stop_propagation();
                Msg::Hover(path_clone.clone())
            });

            html! {

{ field_name }{ "▷" }

            }
        };

        let nodes = children
            .iter()
            .enumerate()
            .map(|(i, n)| {
                let path = append(
                    &path,
                    Selector {
                        field: field_name.to_string(),
                        index: i,
                    },
                );
                self.view_node(Some(n.clone()), &path)
            })
            .collect::<Vec<_>>();
        (head, nodes)
    }

    fn view_value(&self, node: &Node, path: &Path) -> Html {
        match SCHEMA.get_kind(&node.kind) {
            Some(kind) => kind.render(self, node, path),
            None => html! { { "unknown kind: " }{ node.kind.clone() } },
        }
    }
}
<commit_msg>Remove list head<commit_after>use yew::prelude::*;

use crate::{
    schema::{Field, Multiplicity, SCHEMA},
    types::*,
};

impl Model {
    pub fn view_actions(&self) -> Html {
        let actions = vec![
            Action {
                image: None,
                text: "store".to_string(),
                msg: Msg::Store,
            },
            Action {
                image: None,
                text: "load".to_string(),
                msg: Msg::Load,
            },
            Action {
                image: None,
                text: "Normal mode".to_string(),
                msg: Msg::SetMode(Mode::Normal),
            },
            Action {
                image: None,
                text: "Edit mode".to_string(),
                msg: Msg::SetMode(Mode::Edit),
            },
            Action {
                image: Some("gg-arrow-left".to_string()),
                text: "prev".to_string(),
                msg: Msg::Prev,
            },
            Action {
                image: Some("gg-arrow-right".to_string()),
                text: "next".to_string(),
                msg: Msg::Next,
            },
            Action {
                image: Some("gg-corner-right-up".to_string()),
                text: "parent".to_string(),
                msg: Msg::Parent,
            },
            Action {
                image: Some("gg-corner-double-up-right".to_string()),
                text: "+item".to_string(),
                msg: Msg::AddItem,
            },
            Action {
                image: Some("gg-close".to_string()),
                text: "delete".to_string(),
                msg: Msg::DeleteItem,
            },
        ];
        let actions = actions
            .iter()
            // .filter(|a| self.is_valid_action(a))
            .map(|a| self.view_action(a));

        html! {

                { for actions }

        }
    }

    pub fn current_field(&self) -> Option<Field> {
        self.field(&self.cursor)
    }

    pub fn field(&self, path: &Path) -> Option<Field> {
        let parent_path = parent(&self.cursor);

        match path.back() {
            Some(selector) => {
                let parent_ref = self.lookup_path(&self.file.root, &parent_path).unwrap();
                let parent = self.lookup(&parent_ref).unwrap();
                SCHEMA
                    .get_kind(&parent.kind)
                    .unwrap()
                    .get_field(&selector.field)
            }
            None => None,
        }
    }

    fn view_action(&self, action: &Action) -> Html {
        let msg = action.msg.clone();
        let callback = self.link.callback(move |_: MouseEvent| msg.clone());
        let img = match &action.image {
            Some(image) => html! {

            },
            None => html! {},
        };
        html! {
            <button
              class="action hover:bg-blue-200 hover:text-blue-800 group flex items-center rounded-md bg-blue-100 text-blue-600 text-sm font-medium px-4 py-2"
              onclick=callback>
                { img }
                { &action.text }
            </button>
        }
    }

    pub fn view_file(&self, file: &File) -> Html {
        let node = self.view_node(Some(file.root.clone()), &Path::new());
        html! {

{ node }

        }
    }

    pub fn view_file_json(&self, file: &File) -> Html {
        let serialized = serde_json::to_string_pretty(file).expect("could not serialize to JSON");
        html! {

{ serialized }

        }
    }

    pub fn traverse_fields(node: &Node) -> Vec<Field> {
        let kind = &node.kind;
        match SCHEMA.get_kind(kind) {
            Some(kind) => kind.get_fields(),
            None => vec![],
        }
    }

    pub fn flatten_paths(&self, reference: &Ref, base: Path) -> Vec<Path> {
        log::info!("flatten: {:?} {:?}", reference, base);
        match &self.lookup(reference) {
            Some(node) => {
                let mut paths = vec![];
                for field in Model::traverse_fields(&node) {
                    let mut children = node.children.get(field.name).cloned().unwrap_or_default();
                    match field.multiplicity {
                        Multiplicity::Single => {
                            if children.is_empty() {
                                children.push("dummy".to_string());
                            }
                        }
                        Multiplicity::Repeated => {}
                    };
                    for (n, child) in children.iter().enumerate() {
                        let new_base = append(
                            &base,
                            Selector {
                                field: field.name.to_string(),
                                index: n,
                            },
                        );
                        paths.push(new_base.clone());
                        log::info!("child: {:?}[{:?}]->{:?}", reference, field.name, child);
                        paths.extend(self.flatten_paths(child, new_base));
                    }
                    match field.multiplicity {
                        // If repeated field, stay on the parent.
                        Multiplicity::Repeated => {
                            let new_base = append(
                                &base,
                                Selector {
                                    field: field.name.to_string(),
                                    index: children.len(),
                                },
                            );
                            paths.push(new_base.clone());
                        }
                        // If single field, skip directly to the first (and only) child.
                        Multiplicity::Single => {}
                    }
                }
                paths
            }
            None => vec![],
        }
    }

    fn view_node(&self, reference: Option<Ref>, path: &Path) -> Html {
        self.view_node_with_placeholder(reference, path, "◆")
    }

    fn view_node_with_placeholder(
        &self,
        reference: Option<Ref>,
        path: &Path,
        placeholder: &str,
    ) -> Html {
        let mut classes = vec!["node".to_string()];
        if path == &self.cursor {
            classes.push("selected".to_string());
        }
        if path == &self.hover {
            classes.push("hover".to_string());
        }

        let path_clone = path.clone();
        let onclick = self.link.callback(move |e: MouseEvent| {
            e.stop_propagation();
            Msg::Select(path_clone.clone())
        });

        let path_clone = path.clone();
        let onmouseover = self.link.callback(move |e: MouseEvent| {
            e.stop_propagation();
            Msg::Hover(path_clone.clone())
        });

        let path_clone = path.clone();
        let oninput = self.link.callback(move |e: InputData| {
            crate::types::Msg::SetNodeCommand(path_clone.clone(), e.value.clone())
        });

        let node_state = self.node_state.get(&path);

        let value = match reference {
            Some(reference) => match self.lookup(&reference) {
                Some(node) => self.view_value(&node, &path),
                None => {
                    html! {
                        { format!("invalid: {}", reference) }
                    }
                }
            },
            None => {
                let suggestions: Vec<_> = node_state
                    .map(|v| v.parsed_commands.clone())
                    .unwrap_or_default()
                    .iter()
                    .map(|v| {
                        let path_clone = path.clone();
                        let value_string = v.value.clone().unwrap_or_default();
                        let node = v.to_node();
                        let onclick = self.link.callback(move |e: MouseEvent| match node.clone() {
                            Some(node) => Msg::ReplaceNode(path_clone.clone(), node.clone()),
                            None => Msg::Noop,
                        });
                        let classes_item = vec!["block", "border"];
                        html! {
                            {value_string}
                        }
                    })
                    .collect();
                let classes_dropdown = vec!["absolute", "z-10", "bg-white"];
                html! {

{ placeholder }

                            {""}

                                { for suggestions }

                }
            }
        };
        html! {

                { value }

        }
    }

    pub fn view_child(&self, node: &Node, field_name: &str, path: &Path) -> Html {
        self.view_child_with_placeholder(node, field_name, path, field_name)
    }

    pub fn view_child_with_placeholder(
        &self,
        node: &Node,
        field_name: &str,
        path: &Path,
        placeholder: &str,
    ) -> Html {
        let path = append(
            &path,
            Selector {
                field: field_name.to_string(),
                index: 0,
            },
        );
        let reference = node
            .children
            .get(field_name)
            .and_then(|v| v.get(0))
            .cloned();
        // Empty list vs hole vs special value?
        // TODO: How to traverse nested lists in preorder?
        self.view_node_with_placeholder(reference, &path, placeholder)
    }

    /// Returns the head and children, separately.
    pub fn view_children(&self, node: &Node, field_name: &str, path: &Path) -> (Html, Vec) {
        // let path = append(&path, field(field_name));
        // let cursor = sub_cursor(&cursor, field(field_name));
        let empty = vec![];
        let children = node.children.get(field_name).unwrap_or(&empty);
        let head = {
            let path = append(
                &path,
                Selector {
                    field: field_name.to_string(),
                    index: children.len(),
                },
            );
            let mut classes = vec!["node".to_string(), "placeholder".to_string()];
            if path == self.cursor {
                classes.push("selected".to_string());
            }
            if path == self.hover {
                classes.push("hover".to_string());
            }

            let path_clone = path.clone();
            let onclick = self.link.callback(move |e: MouseEvent| {
                e.stop_propagation();
                Msg::Select(path_clone.clone())
            });

            let path_clone = path.clone();
            let onmouseover = self.link.callback(move |e: MouseEvent| {
                e.stop_propagation();
                Msg::Hover(path_clone.clone())
            });

            // html! {
            //
{ field_name
            // }{ "▷" }
 }
            html! {

            }
        };

        let nodes = children
            .iter()
            .enumerate()
            .map(|(i, n)| {
                let path = append(
                    &path,
                    Selector {
                        field: field_name.to_string(),
                        index: i,
                    },
                );
                self.view_node(Some(n.clone()), &path)
            })
            .chain(std::iter::once({
                let path = append(
                    &path,
                    Selector {
                        field: field_name.to_string(),
                        index: children.len(),
                    },
                );
                self.view_node_with_placeholder(None, &path, &format!("{}▷", field_name))
            }))
            .collect::<Vec<_>>();
        (head, nodes)
    }

    fn view_value(&self, node: &Node, path: &Path) -> Html {
        match SCHEMA.get_kind(&node.kind) {
            Some(kind) => kind.render(self, node, path),
            None => html! { { "unknown kind: " }{ node.kind.clone() } },
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

use FsStats;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly",
          target_os = "solaris"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

pub fn statvfs(path: &Path) -> Result<FsStats> {
    let cstr = match CString::new(path.as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) != 0 {
            Err(Error::last_os_error())
        } else {
            Ok(FsStats {
                free_space: stat.f_frsize as u64 * stat.f_bfree as u64,
                available_space: stat.f_frsize as u64 * stat.f_bavail as u64,
                total_space: stat.f_frsize as u64 * stat.f_blocks as u64,
                allocation_granularity: stat.f_frsize,
            })
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<commit_msg>Cast statvfs.f_frsize to u64<commit_after>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

use FsStats;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly",
          target_os = "solaris"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

pub fn statvfs(path: &Path) -> Result<FsStats> {
    let cstr = match CString::new(path.as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) != 0 {
            Err(Error::last_os_error())
        } else {
            Ok(FsStats {
                free_space: stat.f_frsize as u64 * stat.f_bfree as u64,
                available_space: stat.f_frsize as u64 * stat.f_bavail as u64,
                total_space: stat.f_frsize as u64 * stat.f_blocks as u64,
                allocation_granularity: stat.f_frsize as u64,
            })
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<|endoftext|>