<commit_before>use sodiumoxide::crypto::sign::Signature;
use crypto::{SigPair, PublicKey};
use asset::{Asset, AssetSymbol};
use std::io::{Read, Cursor};

pub trait BufWrite {
    fn push_u16(&mut self, num: u16);
    fn push_u32(&mut self, num: u32);
    fn push_i64(&mut self, num: i64);
    fn push_u64(&mut self, num: u64);
    fn push_bytes(&mut self, slice: &[u8]);
    fn push_pub_key(&mut self, key: &PublicKey);
    fn push_sig_pair(&mut self, pair: &SigPair);
    fn push_asset(&mut self, asset: &Asset);
}

impl BufWrite for Vec<u8> {
    fn push_u16(&mut self, num: u16) {
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_u32(&mut self, num: u32) {
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_i64(&mut self, num: i64) {
        self.push((num >> 56) as u8);
        self.push((num >> 48) as u8);
        self.push((num >> 40) as u8);
        self.push((num >> 32) as u8);
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_u64(&mut self, num: u64) {
        self.push((num >> 56) as u8);
        self.push((num >> 48) as u8);
        self.push((num >> 40) as u8);
        self.push((num >> 32) as u8);
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_bytes(&mut self, other: &[u8]) {
        if other.is_empty() {
            self.push_u32(0);
            return
        }
        self.push_u32(other.len() as u32);
        self.extend_from_slice(other);
    }

    fn push_pub_key(&mut self, key: &PublicKey) {
        self.push_bytes(key.as_bytes());
    }

    fn push_sig_pair(&mut self, pair: &SigPair) {
        self.push_pub_key(&pair.pub_key);
        self.push_bytes(pair.signature.as_ref());
    }

    fn push_asset(&mut self, asset: &Asset) {
        self.push_i64(asset.amount);
        self.push(asset.decimals);
        self.push(asset.symbol as u8);
    }
}

pub trait BufRead {
    fn take_u8(&mut self) -> Option<u8>;
    fn take_u16(&mut self) -> Option<u16>;
    fn take_u32(&mut self) -> Option<u32>;
    fn take_i64(&mut self) -> Option<i64>;
    fn take_u64(&mut self) -> Option<u64>;
    fn take_bytes(&mut self) -> Option<Vec<u8>>;
    fn take_pub_key(&mut self) -> Option<PublicKey>;
    fn take_sig_pair(&mut self) -> Option<SigPair>;
    fn take_asset(&mut self) -> Option<Asset>;
}

impl<'a, T: AsRef<[u8]> + Read> BufRead for Cursor<T> {
    fn take_u8(&mut self) -> Option<u8> {
        let mut buf = [0u8;1];
        self.read_exact(&mut buf).ok()?;
        Some(buf[0])
    }

    fn take_u16(&mut self) -> Option<u16> {
        let mut buf = [0u8;2];
        self.read_exact(&mut buf).ok()?;
        Some((u16::from(buf[0]) << 8) | u16::from(buf[1]))
    }

    fn take_u32(&mut self) -> Option<u32> {
        let mut buf = [0u8;4];
        self.read_exact(&mut buf).ok()?;
        Some(u32_from_buf!(buf))
    }

    fn take_i64(&mut self) -> Option<i64> {
        let mut buf = [0u8;8];
        self.read_exact(&mut buf).ok()?;
        Some((i64::from(buf[0]) << 56)
                | (i64::from(buf[1]) << 48)
                | (i64::from(buf[2]) << 40)
                | (i64::from(buf[3]) << 32)
                | (i64::from(buf[4]) << 24)
                | (i64::from(buf[5]) << 16)
                | (i64::from(buf[6]) << 8)
                | i64::from(buf[7]))
    }

    fn take_u64(&mut self) -> Option<u64> {
        let mut buf = [0u8;8];
        self.read_exact(&mut buf).ok()?;
        Some(u64_from_buf!(buf))
    }

    fn take_bytes(&mut self) -> Option<Vec<u8>> {
        let len = self.take_u32()? as usize;
        let mut buf = Vec::with_capacity(len);
        unsafe { buf.set_len(len); }
        self.read_exact(&mut buf).ok()?;
        Some(buf)
    }

    fn take_pub_key(&mut self) -> Option<PublicKey> {
        let buf = self.take_bytes()?;
        PublicKey::from_bytes(&buf)
    }

    fn take_sig_pair(&mut self) -> Option<SigPair> {
        let pub_key = self.take_pub_key()?;
        let signature = Signature::from_slice(&self.take_bytes()?)?;
        Some(SigPair {
            pub_key,
            signature
        })
    }

    fn take_asset(&mut self) -> Option<Asset> {
        let amount = self.take_i64()?;
        let decimals = self.take_u8()?;
        let symbol = match self.take_u8()? {
            0 => AssetSymbol::GOLD,
            1 => AssetSymbol::SILVER,
            _ => return None
        };
        Asset::new(amount, decimals, symbol)
    }
}

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

    #[test]
    fn test_u32_serialization() {
        let num: u32 = 0x0A0B0C0D;
        let mut v = Vec::with_capacity(4);
        v.push_u32(num);
        assert_eq!(v, [0x0A, 0x0B, 0x0C, 0x0D]);
    }

    #[test]
    fn test_u64_serialization() {
        let num: u64 = 0x0A0B0C0D_0A0B0C0D;
        let mut v = Vec::with_capacity(8);
        v.push_u64(num);

        assert_eq!(v, [0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0C, 0x0D]);
        let dec = Cursor::<&[u8]>::new(&v).take_u64().unwrap();
        assert_eq!(num, dec);
    }

    #[test]
    fn test_asset_serialization() {
        {
            let a = Asset::from_str("12.34 GOLD").unwrap();
            let mut v = vec![];
            v.push_asset(&a);

            let mut c = Cursor::<&[u8]>::new(&v);
            let b = c.take_asset().unwrap();
            assert_eq!(a.to_string(), b.to_string());
        }
        {
            let a = Asset {
                amount: 1,
                decimals: ::asset::MAX_PRECISION + 1,
                symbol: ::asset::AssetSymbol::GOLD
            };
            let mut v = vec![];
            v.push_asset(&a);

            let mut c = Cursor::<&[u8]>::new(&v);
            let b = c.take_asset();
            assert!(b.is_none());
        }
    }
}
<commit_msg>Remove unnecessary lifetime annotation<commit_after>use sodiumoxide::crypto::sign::Signature;
use crypto::{SigPair, PublicKey};
use asset::{Asset, AssetSymbol};
use std::io::{Read, Cursor};

pub trait BufWrite {
    fn push_u16(&mut self, num: u16);
    fn push_u32(&mut self, num: u32);
    fn push_i64(&mut self, num: i64);
    fn push_u64(&mut self, num: u64);
    fn push_bytes(&mut self, slice: &[u8]);
    fn push_pub_key(&mut self, key: &PublicKey);
    fn push_sig_pair(&mut self, pair: &SigPair);
    fn push_asset(&mut self, asset: &Asset);
}

impl BufWrite for Vec<u8> {
    fn push_u16(&mut self, num: u16) {
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_u32(&mut self, num: u32) {
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_i64(&mut self, num: i64) {
        self.push((num >> 56) as u8);
        self.push((num >> 48) as u8);
        self.push((num >> 40) as u8);
        self.push((num >> 32) as u8);
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_u64(&mut self, num: u64) {
        self.push((num >> 56) as u8);
        self.push((num >> 48) as u8);
        self.push((num >> 40) as u8);
        self.push((num >> 32) as u8);
        self.push((num >> 24) as u8);
        self.push((num >> 16) as u8);
        self.push((num >> 8) as u8);
        self.push(num as u8);
    }

    fn push_bytes(&mut self, other: &[u8]) {
        if other.is_empty() {
            self.push_u32(0);
            return
        }
        self.push_u32(other.len() as u32);
        self.extend_from_slice(other);
    }

    fn push_pub_key(&mut self, key: &PublicKey) {
        self.push_bytes(key.as_bytes());
    }

    fn push_sig_pair(&mut self, pair: &SigPair) {
        self.push_pub_key(&pair.pub_key);
        self.push_bytes(pair.signature.as_ref());
    }

    fn push_asset(&mut self, asset: &Asset) {
        self.push_i64(asset.amount);
        self.push(asset.decimals);
        self.push(asset.symbol as u8);
    }
}

pub trait BufRead {
    fn take_u8(&mut self) -> Option<u8>;
    fn take_u16(&mut self) -> Option<u16>;
    fn take_u32(&mut self) -> Option<u32>;
    fn take_i64(&mut self) -> Option<i64>;
    fn take_u64(&mut self) -> Option<u64>;
    fn take_bytes(&mut self) -> Option<Vec<u8>>;
    fn take_pub_key(&mut self) -> Option<PublicKey>;
    fn take_sig_pair(&mut self) -> Option<SigPair>;
    fn take_asset(&mut self) -> Option<Asset>;
}

impl<T: AsRef<[u8]> + Read> BufRead for Cursor<T> {
    fn take_u8(&mut self) -> Option<u8> {
        let mut buf = [0u8;1];
        self.read_exact(&mut buf).ok()?;
        Some(buf[0])
    }

    fn take_u16(&mut self) -> Option<u16> {
        let mut buf = [0u8;2];
        self.read_exact(&mut buf).ok()?;
        Some((u16::from(buf[0]) << 8) | u16::from(buf[1]))
    }

    fn take_u32(&mut self) -> Option<u32> {
        let mut buf = [0u8;4];
        self.read_exact(&mut buf).ok()?;
        Some(u32_from_buf!(buf))
    }

    fn take_i64(&mut self) -> Option<i64> {
        let mut buf = [0u8;8];
        self.read_exact(&mut buf).ok()?;
        Some((i64::from(buf[0]) << 56)
                | (i64::from(buf[1]) << 48)
                | (i64::from(buf[2]) << 40)
                | (i64::from(buf[3]) << 32)
                | (i64::from(buf[4]) << 24)
                | (i64::from(buf[5]) << 16)
                | (i64::from(buf[6]) << 8)
                | i64::from(buf[7]))
    }

    fn take_u64(&mut self) -> Option<u64> {
        let mut buf = [0u8;8];
        self.read_exact(&mut buf).ok()?;
        Some(u64_from_buf!(buf))
    }

    fn take_bytes(&mut self) -> Option<Vec<u8>> {
        let len = self.take_u32()? as usize;
        let mut buf = Vec::with_capacity(len);
        unsafe { buf.set_len(len); }
        self.read_exact(&mut buf).ok()?;
        Some(buf)
    }

    fn take_pub_key(&mut self) -> Option<PublicKey> {
        let buf = self.take_bytes()?;
        PublicKey::from_bytes(&buf)
    }

    fn take_sig_pair(&mut self) -> Option<SigPair> {
        let pub_key = self.take_pub_key()?;
        let signature = Signature::from_slice(&self.take_bytes()?)?;
        Some(SigPair {
            pub_key,
            signature
        })
    }

    fn take_asset(&mut self) -> Option<Asset> {
        let amount = self.take_i64()?;
        let decimals = self.take_u8()?;
        let symbol = match self.take_u8()? {
            0 => AssetSymbol::GOLD,
            1 => AssetSymbol::SILVER,
            _ => return None
        };
        Asset::new(amount, decimals, symbol)
    }
}

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

    #[test]
    fn test_u32_serialization() {
        let num: u32 = 0x0A0B0C0D;
        let mut v = Vec::with_capacity(4);
        v.push_u32(num);
        assert_eq!(v, [0x0A, 0x0B, 0x0C, 0x0D]);
    }

    #[test]
    fn test_u64_serialization() {
        let num: u64 = 0x0A0B0C0D_0A0B0C0D;
        let mut v = Vec::with_capacity(8);
        v.push_u64(num);

        assert_eq!(v, [0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0C, 0x0D]);
        let dec = Cursor::<&[u8]>::new(&v).take_u64().unwrap();
        assert_eq!(num, dec);
    }

    #[test]
    fn test_asset_serialization() {
        {
            let a = Asset::from_str("12.34 GOLD").unwrap();
            let mut v = vec![];
            v.push_asset(&a);

            let mut c = Cursor::<&[u8]>::new(&v);
            let b = c.take_asset().unwrap();
            assert_eq!(a.to_string(), b.to_string());
        }
        {
            let a = Asset {
                amount: 1,
                decimals: ::asset::MAX_PRECISION + 1,
                symbol: ::asset::AssetSymbol::GOLD
            };
            let mut v = vec![];
            v.push_asset(&a);

            let mut c = Cursor::<&[u8]>::new(&v);
            let b = c.take_asset();
            assert!(b.is_none());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 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.

//! Support for sending and receiving data link layer packets.

extern crate ipnetwork;
extern crate libc;
extern crate pnet_base;
extern crate pnet_sys;

use std::io;
use std::option::Option;
use std::time::Duration;

use ipnetwork::IpNetwork;

pub use pnet_base::{MacAddr, ParseMacAddrErr};

mod bindings;

#[cfg(windows)]
#[path = "winpcap.rs"]
mod backend;

#[cfg(windows)]
pub mod winpcap;

#[cfg(all(
    not(feature = "netmap"),
    any(target_os = "linux", target_os = "android")
))]
#[path = "linux.rs"]
mod backend;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;

#[cfg(all(
    not(feature = "netmap"),
    any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios")
))]
#[path = "bpf.rs"]
mod backend;

#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
pub mod bpf;

#[cfg(feature = "netmap")]
#[path = "netmap.rs"]
mod backend;

#[cfg(feature = "netmap")]
pub mod netmap;

#[cfg(feature = "pcap")]
pub mod pcap;

pub mod dummy;

/// Type alias for an `EtherType`.
pub type EtherType = u16;

/// Type of data link channel to present (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
    /// Send and receive layer 2 packets directly, including headers.
    Layer2,
    /// Send and receive "cooked" packets - send and receive network layer packets.
    Layer3(EtherType),
}

/// A channel for sending and receiving at the data link layer.
///
/// NOTE: It is important to always include a catch-all variant in match statements using this
/// enum, since new variants may be added. For example:
///
/// ```ignore
/// match some_channel {
///     Ethernet(tx, rx) => { /* Handle Ethernet packets */ },
///     _ => panic!("Unhandled channel type")
/// }
/// ```
pub enum Channel {
    /// A datalink channel which sends and receives Ethernet packets.
    Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>),

    /// This variant should never be used.
    ///
    /// Including it allows new variants to be added to `Channel` without breaking existing code.
    PleaseIncludeACatchAllVariantWhenMatchingOnThisEnum,
}

/// Socket fanout type (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FanoutType {
    HASH,
    LB,
    CPU,
    ROLLOVER,
    RND,
    QM,
    CBPF,
    EBPF,
}

/// Fanout settings (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FanoutOption {
    pub group_id: u16,
    pub fanout_type: FanoutType,
    pub defrag: bool,
    pub rollover: bool,
}

/// A generic configuration type, encapsulating all options supported by each backend.
///
/// Each option should be treated as a hint - each backend is free to ignore any and all
/// options which don't apply to it.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// Linux/BPF/Netmap only: The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// Linux/BPF/Netmap only: The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// Linux only: Specifies whether to read packets at the datalink layer or network layer.
    /// Defaults to Layer2
    pub channel_type: ChannelType,

    /// BPF/OS X only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults
    /// to: 1000.
    pub bpf_fd_attempts: usize,

    pub linux_fanout: Option<FanoutOption>,

    pub promiscuous: bool,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
            channel_type: ChannelType::Layer2,
            bpf_fd_attempts: 1000,
            linux_fanout: None,
            promiscuous: true,
        }
    }
}

/// Create a new datalink channel for sending and receiving data.
///
/// This allows for sending and receiving packets at the data link layer.
///
/// A list of network interfaces can be retrieved using datalink::interfaces().
///
/// The configuration serves as a hint to the backend - some or all of it may be used or ignored,
/// depending on which backend is used.
///
/// When matching on the returned channel, make sure to include a catch-all so that code doesn't
/// break when new channel types are added.
#[inline]
pub fn channel(network_interface: &NetworkInterface, configuration: Config) -> io::Result<Channel> {
    backend::channel(network_interface, (&configuration).into())
}

/// Trait to enable sending `$packet` packets.
pub trait DataLinkSender: Send {
    /// Create and send a number of packets.
    ///
    /// This will call `func` `num_packets` times. The function will be provided with a
    /// mutable packet to manipulate, which will then be sent. This allows packets to be
    /// built in-place, avoiding the copy required for `send`. If there is not sufficient
    /// capacity in the buffer, None will be returned.
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>>;

    /// Send a packet.
    ///
    /// This may require an additional copy compared to `build_and_send`, depending on the
    /// operating system being used. The second parameter is currently ignored, however
    /// `None` should be passed.
    fn send_to(&mut self, packet: &[u8], dst: Option<NetworkInterface>) -> Option<io::Result<()>>;
}

/// Structure for receiving packets at the data link layer. Should be constructed using
/// `datalink_channel()`.
pub trait DataLinkReceiver: Send {
    /// Get the next ethernet frame in the channel.
    fn next(&mut self) -> io::Result<&[u8]>;
}

/// Represents a network interface and its associated addresses.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
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>,
    /// IP addresses and netmasks for the interface.
    pub ips: Vec<IpNetwork>,
    /// Operating system specific flags for the interface.
    pub flags: u32,
}

impl NetworkInterface {
    /// Retrieve the MAC address associated with the interface.
    #[deprecated(
        since = "0.26.0",
        note = "Please use NetworkInterface's field 'mac' instead."
    )]
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    pub fn is_up(&self) -> bool {
        self.flags & (pnet_sys::IFF_UP as u32) != 0
    }
    pub fn is_broadcast(&self) -> bool {
        self.flags & (pnet_sys::IFF_BROADCAST as u32) != 0
    }
    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (pnet_sys::IFF_LOOPBACK as u32) != 0
    }
    pub fn is_point_to_point(&self) -> bool {
        self.flags & (pnet_sys::IFF_POINTOPOINT as u32) != 0
    }
    pub fn is_multicast(&self) -> bool {
        self.flags & (pnet_sys::IFF_MULTICAST as u32) != 0
    }
}

impl ::std::fmt::Display for NetworkInterface {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        const FLAGS: [&'static str; 5] =
            ["UP", "BROADCAST", "LOOPBACK", "POINTOPOINT", "MULTICAST"];
        let flags = if self.flags > 0 {
            let rets = [
                self.is_up(),
                self.is_broadcast(),
                self.is_loopback(),
                self.is_point_to_point(),
                self.is_multicast(),
            ];
            format!(
                "{:X}<{}>",
                self.flags,
                rets.iter()
                    .zip(FLAGS.iter())
                    .filter(|&(ret, _)| ret == &true)
                    .map(|(_, name)| name.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            )
        } else {
            format!("{:X}", self.flags)
        };

        let mac = self
            .mac
            .map(|mac| mac.to_string())
            .unwrap_or("N/A".to_owned());
        let ips = if self.ips.len() > 0 {
            format!(
                "\n{}",
                self.ips
                    .iter()
                    .map(|ip| {
                        if ip.is_ipv4() {
                            format!("       inet: {}", ip)
                        } else {
                            format!("      inet6: {}", ip)
                        }
                    })
                    .collect::<Vec<String>>()
                    .join("\n")
            )
        } else {
            "".to_string()
        };

        write!(
            f,
            "{}: flags={}
      index: {}
      ether: {}{}",
            self.name, flags, self.index, mac, ips
        )
    }
}

/// Get a list of available network interfaces for the current machine.
///
/// If you need the default network interface, you can choose the first
/// one that is up, not loopback and has an IP. This is not guaranteed to
/// work on each system but should work for basic packet sniffing:
///
/// ```
/// use pnet_datalink::interfaces;
///
/// // Get a vector with all network interfaces found
/// let all_interfaces = interfaces();
///
/// // Search for the default interface - the one that is
/// // up, not loopback and has an IP.
/// let default_interface = all_interfaces
///     .iter()
///     .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0)
///     .next();
///
/// match default_interface {
///     Some(interface) => println!("Found default interface with [{}].", interface.name),
///     None => println!("Error while finding the default interface."),
/// }
///
/// ```
///
pub fn interfaces() -> Vec<NetworkInterface> {
    backend::interfaces()
}
<commit_msg>Improve datalink interfaces example<commit_after>// Copyright (c) 2014-2016 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.

//! Support for sending and receiving data link layer packets.

extern crate ipnetwork;
extern crate libc;
extern crate pnet_base;
extern crate pnet_sys;

use std::io;
use std::option::Option;
use std::time::Duration;

use ipnetwork::IpNetwork;

pub use pnet_base::{MacAddr, ParseMacAddrErr};

mod bindings;

#[cfg(windows)]
#[path = "winpcap.rs"]
mod backend;

#[cfg(windows)]
pub mod winpcap;

#[cfg(all(
    not(feature = "netmap"),
    any(target_os = "linux", target_os = "android")
))]
#[path = "linux.rs"]
mod backend;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;

#[cfg(all(
    not(feature = "netmap"),
    any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios")
))]
#[path = "bpf.rs"]
mod backend;

#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
pub mod bpf;

#[cfg(feature = "netmap")]
#[path = "netmap.rs"]
mod backend;

#[cfg(feature = "netmap")]
pub mod netmap;

#[cfg(feature = "pcap")]
pub mod pcap;

pub mod dummy;

/// Type alias for an `EtherType`.
pub type EtherType = u16;

/// Type of data link channel to present (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
    /// Send and receive layer 2 packets directly, including headers.
    Layer2,
    /// Send and receive "cooked" packets - send and receive network layer packets.
    Layer3(EtherType),
}

/// A channel for sending and receiving at the data link layer.
///
/// NOTE: It is important to always include a catch-all variant in match statements using this
/// enum, since new variants may be added. For example:
///
/// ```ignore
/// match some_channel {
///     Ethernet(tx, rx) => { /* Handle Ethernet packets */ },
///     _ => panic!("Unhandled channel type")
/// }
/// ```
pub enum Channel {
    /// A datalink channel which sends and receives Ethernet packets.
    Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>),

    /// This variant should never be used.
    ///
    /// Including it allows new variants to be added to `Channel` without breaking existing code.
    PleaseIncludeACatchAllVariantWhenMatchingOnThisEnum,
}

/// Socket fanout type (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FanoutType {
    HASH,
    LB,
    CPU,
    ROLLOVER,
    RND,
    QM,
    CBPF,
    EBPF,
}

/// Fanout settings (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FanoutOption {
    pub group_id: u16,
    pub fanout_type: FanoutType,
    pub defrag: bool,
    pub rollover: bool,
}

/// A generic configuration type, encapsulating all options supported by each backend.
///
/// Each option should be treated as a hint - each backend is free to ignore any and all
/// options which don't apply to it.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// Linux/BPF/Netmap only: The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// Linux/BPF/Netmap only: The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// Linux only: Specifies whether to read packets at the datalink layer or network layer.
    /// Defaults to Layer2
    pub channel_type: ChannelType,

    /// BPF/OS X only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults
    /// to: 1000.
    pub bpf_fd_attempts: usize,

    pub linux_fanout: Option<FanoutOption>,

    pub promiscuous: bool,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
            channel_type: ChannelType::Layer2,
            bpf_fd_attempts: 1000,
            linux_fanout: None,
            promiscuous: true,
        }
    }
}

/// Create a new datalink channel for sending and receiving data.
///
/// This allows for sending and receiving packets at the data link layer.
///
/// A list of network interfaces can be retrieved using datalink::interfaces().
///
/// The configuration serves as a hint to the backend - some or all of it may be used or ignored,
/// depending on which backend is used.
///
/// When matching on the returned channel, make sure to include a catch-all so that code doesn't
/// break when new channel types are added.
#[inline]
pub fn channel(network_interface: &NetworkInterface, configuration: Config) -> io::Result<Channel> {
    backend::channel(network_interface, (&configuration).into())
}

/// Trait to enable sending `$packet` packets.
pub trait DataLinkSender: Send {
    /// Create and send a number of packets.
    ///
    /// This will call `func` `num_packets` times. The function will be provided with a
    /// mutable packet to manipulate, which will then be sent. This allows packets to be
    /// built in-place, avoiding the copy required for `send`. If there is not sufficient
    /// capacity in the buffer, None will be returned.
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>>;

    /// Send a packet.
    ///
    /// This may require an additional copy compared to `build_and_send`, depending on the
    /// operating system being used. The second parameter is currently ignored, however
    /// `None` should be passed.
    fn send_to(&mut self, packet: &[u8], dst: Option<NetworkInterface>) -> Option<io::Result<()>>;
}

/// Structure for receiving packets at the data link layer. Should be constructed using
/// `datalink_channel()`.
pub trait DataLinkReceiver: Send {
    /// Get the next ethernet frame in the channel.
    fn next(&mut self) -> io::Result<&[u8]>;
}

/// Represents a network interface and its associated addresses.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
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>,
    /// IP addresses and netmasks for the interface.
    pub ips: Vec<IpNetwork>,
    /// Operating system specific flags for the interface.
    pub flags: u32,
}

impl NetworkInterface {
    /// Retrieve the MAC address associated with the interface.
    #[deprecated(
        since = "0.26.0",
        note = "Please use NetworkInterface's field 'mac' instead."
    )]
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    pub fn is_up(&self) -> bool {
        self.flags & (pnet_sys::IFF_UP as u32) != 0
    }
    pub fn is_broadcast(&self) -> bool {
        self.flags & (pnet_sys::IFF_BROADCAST as u32) != 0
    }
    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (pnet_sys::IFF_LOOPBACK as u32) != 0
    }
    pub fn is_point_to_point(&self) -> bool {
        self.flags & (pnet_sys::IFF_POINTOPOINT as u32) != 0
    }
    pub fn is_multicast(&self) -> bool {
        self.flags & (pnet_sys::IFF_MULTICAST as u32) != 0
    }
}

impl ::std::fmt::Display for NetworkInterface {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        const FLAGS: [&'static str; 5] =
            ["UP", "BROADCAST", "LOOPBACK", "POINTOPOINT", "MULTICAST"];
        let flags = if self.flags > 0 {
            let rets = [
                self.is_up(),
                self.is_broadcast(),
                self.is_loopback(),
                self.is_point_to_point(),
                self.is_multicast(),
            ];
            format!(
                "{:X}<{}>",
                self.flags,
                rets.iter()
                    .zip(FLAGS.iter())
                    .filter(|&(ret, _)| ret == &true)
                    .map(|(_, name)| name.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            )
        } else {
            format!("{:X}", self.flags)
        };

        let mac = self
            .mac
            .map(|mac| mac.to_string())
            .unwrap_or("N/A".to_owned());
        let ips = if self.ips.len() > 0 {
            format!(
                "\n{}",
                self.ips
                    .iter()
                    .map(|ip| {
                        if ip.is_ipv4() {
                            format!("       inet: {}", ip)
                        } else {
                            format!("      inet6: {}", ip)
                        }
                    })
                    .collect::<Vec<String>>()
                    .join("\n")
            )
        } else {
            "".to_string()
        };

        write!(
            f,
            "{}: flags={}
      index: {}
      ether: {}{}",
            self.name, flags, self.index, mac, ips
        )
    }
}

/// Get a list of available network interfaces for the current machine.
///
/// If you need the default network interface, you can choose the first
/// one that is up, not loopback and has an IP. This is not guaranteed to
/// work on each system but should work for basic packet sniffing:
///
/// ```
/// use pnet_datalink::interfaces;
///
/// // Get a vector with all network interfaces found
/// let all_interfaces = interfaces();
///
/// // Search for the default interface - the one that is
/// // up, not loopback and has an IP.
/// let default_interface = all_interfaces
///     .iter()
///     .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty();
///
/// match default_interface {
///     Some(interface) => println!("Found default interface with [{}].", interface.name),
///     None => println!("Error while finding the default interface."),
/// }
///
/// ```
///
pub fn interfaces() -> Vec<NetworkInterface> {
    backend::interfaces()
}
<|endoftext|>


<commit_before>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");
    let has_pkgconfig = Command::new("pkg-config").output().is_ok();

    if env::var("LIBGIT2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");
    let msvc = target.contains("msvc");
    let mut cfg = cmake::Config::new("libgit2");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cfg.cflag("/GL-");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cfg.define("STATIC_CRT", "OFF");
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install
    // (or when pkg-config isn't found). As a result we just manually turn on
    // SSH support in libgit2 (a little jankily) here...
    if windows || !has_pkgconfig {
        if msvc {
            cfg.cflag("/DGIT_SSH");
        } else {
            cfg.cflag("-DGIT_SSH");
        }
        if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
            if msvc {
                cfg.cflag(format!(" /I{}", libssh2_include));
            } else {
                cfg.cflag(format!(" -I{}", libssh2_include));
            }
        }
    }

    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
                 .define("BUILD_CLAR", "OFF")
                 .define("CURL", "OFF")
                 .register_dep("SSH2")
                 .register_dep("Z")
                 .register_dep("OPENSSL")
                 .build();

    let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
    let mut contents = String::new();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if !msvc {
        File::open(flags).unwrap().read_to_string(&mut contents).unwrap();
        if !contents.contains("-DGIT_SSH") {
            panic!("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        prepend("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}
<commit_msg>Don't pick up libssh2 via pkg-config<commit_after>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e{
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");
    let has_pkgconfig = Command::new("pkg-config").output().is_ok();

    if env::var("LIBGIT2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");
    let msvc = target.contains("msvc");
    let mut cfg = cmake::Config::new("libgit2");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cfg.cflag("/GL-");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cfg.define("STATIC_CRT", "OFF");
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install
    // (or when pkg-config isn't found). As a result we just manually turn on
    // SSH support in libgit2 (a little jankily) here...
    if windows || !has_pkgconfig {
        if msvc {
            cfg.cflag("/DGIT_SSH");
        } else {
            cfg.cflag("-DGIT_SSH");
        }
        if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
            if msvc {
                cfg.cflag(format!(" /I{}", libssh2_include));
            } else {
                cfg.cflag(format!(" -I{}", libssh2_include));
            }
        }
    }

    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
                 .define("BUILD_CLAR", "OFF")
                 .define("CURL", "OFF")
                 .register_dep("SSH2")
                 .register_dep("Z")
                 .register_dep("OPENSSL")
                 .build();

    let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
    let mut contents = String::new();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if !msvc {
        t!(t!(File::open(flags)).read_to_string(&mut contents));
        if !contents.contains("-DGIT_SSH") {
            panic!("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        // libssh2 is linked in elsehwere, don't want it reported via pkg-config
        let pc = dst.join("lib/pkgconfig/libgit2.pc");
        let mut contents = String::new();
        t!(t!(File::open(&pc)).read_to_string(&mut contents));
        let contents = contents.replace(" -lssh2 ", " ");
        t!(t!(File::create(&pc)).write_all(contents.as_bytes()));

        prepend("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}
<|endoftext|>


<commit_before>extern crate num_traits;

use std::collections::HashSet;
use std::fmt;
use std::hash::Hash;
use std::vec::Vec;
use std::ops::{AddAssign, Mul, Div};
use self::num_traits::{One, Zero};
use std::str::FromStr;

use recognisable::Transition;
use cfg::*;
use push_down_automaton::{PushDown, PushDownAutomaton, PushDownInstruction};

/// Symbols of a `PushDown` created by an `CFG`
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum PushState<X, Y> {
    Designated,
    Initial,
    Nt(X),
    T(Y),
}

impl<A, Y> PushState<A, Y>
    where Y: Clone,
{
    pub fn map<F, B>(&self, f: F) -> PushState<B, Y>
        where F: Fn(&A) -> B,
    {
        match *self {
            PushState::Designated => PushState::Designated,
            PushState::Initial => PushState::Initial,
            PushState::Nt(ref a) => PushState::Nt(f(a)),
            PushState::T(ref x) => PushState::T(x.clone()),
        }
    }
}

impl<X: fmt::Display, Y: fmt::Display> fmt::Display for PushState<X, Y> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            PushState::Designated
                => write!(f, "@"),
            PushState::Initial
                => write!(f, "I"),
            PushState::Nt(ref x)
                => write!(f, "({})", x),
            PushState::T(ref x)
                => write!(f, "({})", x),
        }
    }
}

impl<N: Clone + Ord + PartialEq + Hash,
     T: Clone + Ord + PartialEq + Hash,
     W: Clone + Ord + PartialEq + One + FromStr + AddAssign + Mul<Output = W> + Div<Output = W> + Zero
     > From<CFG<N, T, W>> for PushDownAutomaton<PushState<N,T>, T, W>
{
     fn from(g: CFG<N, T, W>) -> Self {
        let mut transitions = Vec::new();

        let mut t_buffer= HashSet::new();

        // creates a Transition for every rule that replaces the nonterminal of the left side with the rightside of the rule
        for r in g.rules.clone(){
            let mut st = Vec::new();
            for v in r.composition.composition{

                match v {
                    LetterT::Value(x) => {
                        t_buffer.insert(x.clone());
                        st.insert(0,PushState::T(x.clone()));
                    },
                    LetterT::Label(x) => {
                        st.insert(0,PushState::Nt(x.clone()));
                    },
                }
            }

            transitions.push(
                Transition {
                    word: Vec::new(),
                    weight: r.weight.clone(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::Nt(r.head.clone())],
                        new_val: st.clone(),
                    }
                }
            );

        }
        // creates a transition for every terminal that reads the word
        for t in t_buffer{
            let mut tvec = Vec::new();
            tvec.push(t.clone());
            transitions.push(
                Transition {
                    word: tvec.clone(),
                    weight: W::one(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::T(t.clone())],
                        new_val: Vec::new(),
                    }
                }
            );

        }

        // creates a transition for the `Initial` symbol to all Nonterminals in `initial` with weight `one`
        for ini in g.initial{
            let mut tvec = Vec::new();
            tvec.push(PushState::Nt(ini));
            transitions.push(
                Transition {
                    word: Vec::new(),
                    weight: W::one(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::Initial],
                        new_val: tvec,
                    }
                }
            );
        }

        PushDownAutomaton::new(
            transitions,
            PushDown::new(PushState::Initial, PushState::Designated),
        )
    }
}
<commit_msg>src/push_down_automaton/from_cfg.rs: added tests<commit_after>extern crate num_traits;

use std::collections::HashSet;
use std::fmt;
use std::hash::Hash;
use std::vec::Vec;
use std::ops::{AddAssign, Mul, Div};
use self::num_traits::{One, Zero};
use std::str::FromStr;

use recognisable::Transition;
use cfg::*;
use push_down_automaton::{PushDown, PushDownAutomaton, PushDownInstruction};

/// Symbols of a `PushDown` created by an `CFG`
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum PushState<X, Y> {
    Designated,
    Initial,
    Nt(X),
    T(Y),
}

impl<A, Y> PushState<A, Y>
    where Y: Clone,
{
    pub fn map<F, B>(&self, f: F) -> PushState<B, Y>
        where F: Fn(&A) -> B,
    {
        match *self {
            PushState::Designated => PushState::Designated,
            PushState::Initial => PushState::Initial,
            PushState::Nt(ref a) => PushState::Nt(f(a)),
            PushState::T(ref x) => PushState::T(x.clone()),
        }
    }
}

impl<X: fmt::Display, Y: fmt::Display> fmt::Display for PushState<X, Y> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            PushState::Designated
                => write!(f, "@"),
            PushState::Initial
                => write!(f, "I"),
            PushState::Nt(ref x)
                => write!(f, "({})", x),
            PushState::T(ref x)
                => write!(f, "({})", x),
        }
    }
}

impl<N: Clone + Ord + PartialEq + Hash,
     T: Clone + Ord + PartialEq + Hash,
     W: Clone + Ord + PartialEq + One + FromStr + AddAssign + Mul<Output = W> + Div<Output = W> + Zero
     > From<CFG<N, T, W>> for PushDownAutomaton<PushState<N,T>, T, W>
{
     fn from(g: CFG<N, T, W>) -> Self {
        let mut transitions = Vec::new();

        let mut t_buffer= HashSet::new();

        // creates a Transition for every rule that replaces the nonterminal of the left side with the rightside of the rule
        for r in g.rules.clone(){
            let mut st = Vec::new();
            for v in r.composition.composition{

                match v {
                    LetterT::Value(x) => {
                        t_buffer.insert(x.clone());
                        st.insert(0,PushState::T(x.clone()));
                    },
                    LetterT::Label(x) => {
                        st.insert(0,PushState::Nt(x.clone()));
                    },
                }
            }

            transitions.push(
                Transition {
                    word: Vec::new(),
                    weight: r.weight.clone(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::Nt(r.head.clone())],
                        new_val: st.clone(),
                    }
                }
            );

        }
        // creates a transition for every terminal that reads the word
        for t in t_buffer{
            let mut tvec = Vec::new();
            tvec.push(t.clone());
            transitions.push(
                Transition {
                    word: tvec.clone(),
                    weight: W::one(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::T(t.clone())],
                        new_val: Vec::new(),
                    }
                }
            );

        }

        // creates a transition for the `Initial` symbol to all Nonterminals in `initial` with weight `one`
        for ini in g.initial{
            let mut tvec = Vec::new();
            tvec.push(PushState::Nt(ini));
            transitions.push(
                Transition {
                    word: Vec::new(),
                    weight: W::one(),
                    instruction: PushDownInstruction::Replace {
                        current_val: vec![PushState::Initial],
                        new_val: tvec,
                    }
                }
            );
        }

        PushDownAutomaton::new(
            transitions,
            PushDown::new(PushState::Initial, PushState::Designated),
        )
    }
}

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

    #[test]
    fn test_push_state_map_correctness() {
        let inputs = vec![
            (PushState::Designated, PushState::Designated),
            (PushState::Initial, PushState::Initial),
            (PushState::Nt(1), PushState::Nt(2)),
            (PushState::T('a'), PushState::T('a')),
        ];

        for (input, control_output) in inputs {
            assert_eq!(
                control_output,
                input.map(&|x: &u8| x * 2)
            );
        }
    }

    #[test]
    fn test_push_state_map_inverse() {
        let state: PushState<u8, char> = PushState::Nt(1);
        let mapped_state = state.map(&|x: &u8| x * 2);

        assert_eq!(
            state,
            mapped_state.map(&|x: &u8| x / 2)
        );
    }
}
<|endoftext|>


<commit_before>use glut;
use azure;
use geom;

import geom::point::Point2D;
import geom::rect::Rect;
import geom::size::Size2D;
import layers::*;
import rendergl::*;
import util::convert_rgb32_to_rgb24;

import glut::{check_loop, create_window, destroy_window, display_func, init, init_display_mode, post_redisplay, swap_buffers, timer_func};

import azure::cairo::CAIRO_FORMAT_RGB24;
import CairoContext = azure::cairo_hl::Context;
import azure::azure_hl::{Color, ColorPattern, DrawTarget};
import azure::cairo_hl::ImageSurface;

import comm::{chan, peek, port, recv, send};
import libc::c_uint;
import os::{getenv, setenv};
import task::{builder, get_opts, run_listener, set_opts};

class Renderer {
    let image: @Image;
    let mut image_layer: ImageLayer;
    let mut t: f32;
    let mut delta: f32;
    let mut render_context: option<RenderContext>;

    new() {
        let cairo_image = ImageSurface(CAIRO_FORMAT_RGB24, 500, 704);

        let draw_target = DrawTarget(cairo_image);
        draw_target.fill_rect(Rect(Point2D(50.0f32, 50.0f32), Size2D(300.0f32, 284.0f32)),
                              ColorPattern(Color(1.0f32, 1.0f32, 0.0f32, 1.0f32)));
        draw_target.flush();

        self.image = @Image(cairo_image.width() as uint, cairo_image.height() as uint, RGB24Format,
                            convert_rgb32_to_rgb24(cairo_image.data()));
        io::println(#fmt("image is %ux%u", self.image.width, self.image.height));

        self.image_layer = ImageLayer(self.image);

        self.t = 1.0f32;
        self.delta = -0.001f32;

        self.render_context = none;
    }

    fn get_display_callback(this: @Renderer) -> fn@() {
        fn@() {
            (*this).display_callback();
        }
    }

    fn display_callback() {
        alt self.render_context {
            none {
                self.render_context = some(init_render_context());
            }
            some(_) {
                // Nothing to do.
            }
        }
        let context = alt self.render_context {
            none { fail }
            some(ctx) { ctx }
        };

        let t = self.t;
        self.image_layer.common.transform = Matrix4(1.0f32 * t, 0.0f32,     0.0f32, 0.0f32,
                                                    0.0f32,     1.0f32 * t, 0.0f32, 0.0f32,
                                                    0.0f32,     0.0f32,     1.0f32, 0.0f32,
                                                    0.0f32,     0.0f32,     0.0f32, 1.0f32);

        render_scene(context, self.image_layer);

        self.t += self.delta;
        if self.t < 0.0f32 || self.t > 1.0f32 {
            self.delta = -self.delta;
        }

        swap_buffers();

        post_redisplay();
    }
}

#[test]
fn test_triangle_and_square() unsafe {
    let builder = builder();
    let opts = {
        sched: some({ mode: task::osmain, foreign_stack_size: none })
        with get_opts(builder)
    };
    set_opts(builder, opts);

    let po: port<()> = port();
    let ch = chan(po);
    let _result_ch: chan<()> = do run_listener(builder) |_po| {
        let renderer = @Renderer();

        init();
        init_display_mode(0 as c_uint);
        let window = create_window("Rust Layers");
        display_func(renderer.get_display_callback(renderer));

        let wakeup = port();
        let wakeup_chan = chan(wakeup);
        do timer_func(2500) {
            send(wakeup_chan, ());
        }

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }

        send(ch, ());
    };

    recv(po);
}

<commit_msg>Double buffer<commit_after>use glut;
use azure;
use geom;

import geom::point::Point2D;
import geom::rect::Rect;
import geom::size::Size2D;
import layers::*;
import rendergl::*;
import util::convert_rgb32_to_rgb24;

import glut::{DOUBLE, check_loop, create_window, destroy_window, display_func, init};
import glut::{init_display_mode, post_redisplay, swap_buffers, timer_func};

import azure::cairo::CAIRO_FORMAT_RGB24;
import CairoContext = azure::cairo_hl::Context;
import azure::azure_hl::{Color, ColorPattern, DrawTarget};
import azure::cairo_hl::ImageSurface;

import comm::{chan, peek, port, recv, send};
import libc::c_uint;
import os::{getenv, setenv};
import task::{builder, get_opts, run_listener, set_opts};

class Renderer {
    let image: @Image;
    let mut image_layer: ImageLayer;
    let mut t: f32;
    let mut delta: f32;
    let mut render_context: option<RenderContext>;

    new() {
        let cairo_image = ImageSurface(CAIRO_FORMAT_RGB24, 500, 704);

        let draw_target = DrawTarget(cairo_image);
        draw_target.fill_rect(Rect(Point2D(50.0f32, 50.0f32), Size2D(300.0f32, 284.0f32)),
                              ColorPattern(Color(1.0f32, 1.0f32, 0.0f32, 1.0f32)));
        draw_target.flush();

        self.image = @Image(cairo_image.width() as uint, cairo_image.height() as uint, RGB24Format,
                            convert_rgb32_to_rgb24(cairo_image.data()));
        io::println(#fmt("image is %ux%u", self.image.width, self.image.height));

        self.image_layer = ImageLayer(self.image);

        self.t = 1.0f32;
        self.delta = -0.001f32;

        self.render_context = none;
    }

    fn get_display_callback(this: @Renderer) -> fn@() {
        fn@() {
            (*this).display_callback();
        }
    }

    fn display_callback() {
        alt self.render_context {
            none {
                self.render_context = some(init_render_context());
            }
            some(_) {
                // Nothing to do.
            }
        }
        let context = alt self.render_context {
            none { fail }
            some(ctx) { ctx }
        };

        let t = self.t;
        self.image_layer.common.transform = Matrix4(1.0f32 * t, 0.0f32,     0.0f32, 0.0f32,
                                                    0.0f32,     1.0f32 * t, 0.0f32, 0.0f32,
                                                    0.0f32,     0.0f32,     1.0f32, 0.0f32,
                                                    0.0f32,     0.0f32,     0.0f32, 1.0f32);

        render_scene(context, self.image_layer);

        self.t += self.delta;
        if self.t < 0.0f32 || self.t > 1.0f32 {
            self.delta = -self.delta;
        }

        swap_buffers();

        post_redisplay();
    }
}

#[test]
fn test_triangle_and_square() unsafe {
    let builder = builder();
    let opts = {
        sched: some({ mode: task::osmain, foreign_stack_size: none })
        with get_opts(builder)
    };
    set_opts(builder, opts);

    let po: port<()> = port();
    let ch = chan(po);
    let _result_ch: chan<()> = do run_listener(builder) |_po| {
        let renderer = @Renderer();

        init();
        init_display_mode(DOUBLE as c_uint);
        let window = create_window("Rust Layers");
        display_func(renderer.get_display_callback(renderer));

        let wakeup = port();
        let wakeup_chan = chan(wakeup);
        do timer_func(2500) {
            send(wakeup_chan, ());
        }

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }

        send(ch, ());
    };

    recv(po);
}

<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::process::Command;

use clap::{App, ArgMatches};

use utils;
use CliResult;
use commands::Config;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uninstalls the sentry-cli executable")
}

pub fn execute<'a>(_matches: &ArgMatches<'a>, _config: &Config) -> CliResult<()> {
    let exe = env::current_exe()?;

    if !utils::prompt_to_continue("Do you really want to uninstall sentry-cli?")? {
        println!("Aborted!");
        return Ok(());
    }

    if !utils::is_writable(&exe) {
        println!("Need to sudo to uninstall {}", exe.display());
        Command::new("sudo")
            .arg("-k")
            .arg("rm -f")
            .arg(&exe)
            .status()?;
    } else {
        fs::remove_file(&exe)?;
    }
    println!("Uninstalled!");

    Ok(())
}
<commit_msg>Fixed sudo based uninstall<commit_after>use std::fs;
use std::env;
use std::process::Command;

use clap::{App, ArgMatches};

use utils;
use CliResult;
use commands::Config;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uninstalls the sentry-cli executable")
}

pub fn execute<'a>(_matches: &ArgMatches<'a>, _config: &Config) -> CliResult<()> {
    let exe = env::current_exe()?;

    if !utils::prompt_to_continue("Do you really want to uninstall sentry-cli?")? {
        println!("Aborted!");
        return Ok(());
    }

    if !utils::is_writable(&exe) {
        println!("Need to sudo to uninstall {}", exe.display());
        Command::new("sudo")
            .arg("-k")
            .arg("rm")
            .arg("-f")
            .arg(&exe)
            .status()?;
    } else {
        fs::remove_file(&exe)?;
    }
    println!("Uninstalled!");

    Ok(())
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        function_parameters::{async_param_to_remove, CParameter},
        functions::{find_function, find_index_to_ignore, finish_function_name},
        imports::Imports,
        out_parameters::use_function_return_for_result,
        ref_mode::RefMode,
        rust_type::RustType,
    },
    config,
    consts::TYPE_PARAMETERS_START,
    env::Env,
    library::{
        Class, Concurrency, Function, Fundamental, Nullable, ParameterDirection, Type, TypeId,
    },
    traits::IntoString,
};
use std::{collections::vec_deque::VecDeque, slice::Iter};

#[derive(Clone, Eq, Debug, PartialEq)]
pub enum BoundType {
    NoWrapper,
    // lifetime
    IsA(Option<char>),
    // lifetime <- shouldn't be used but just in case...
    AsRef(Option<char>),
}

impl BoundType {
    pub fn need_isa(&self) -> bool {
        matches!(*self, Self::IsA(_))
    }

    // TODO: This is just a heuristic for now, based on what we do in codegen!
    // Theoretically the surrounding function should determine whether it needs to
    // reuse an alias (ie. to use in `call_func::<P, Q, R>`) or not.
    // In the latter case an `impl` is generated instead of a type name/alias.
    pub fn has_alias(&self) -> bool {
        matches!(*self, Self::NoWrapper)
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Bound {
    pub bound_type: BoundType,
    pub parameter_name: String,
    /// Bound does not have an alias when `param: impl type_str` is used
    pub alias: Option<char>,
    pub type_str: String,
    pub callback_modified: bool,
}

#[derive(Clone, Debug)]
pub struct Bounds {
    unused: VecDeque<char>,
    used: Vec<Bound>,
    unused_lifetimes: VecDeque<char>,
    lifetimes: Vec<char>,
}

impl Default for Bounds {
    fn default() -> Bounds {
        Bounds {
            unused: (TYPE_PARAMETERS_START..)
                .take_while(|x| *x <= 'Z')
                .collect(),
            used: Vec::new(),
            unused_lifetimes: "abcdefg".chars().collect(),
            lifetimes: Vec::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CallbackInfo {
    pub callback_type: String,
    pub success_parameters: String,
    pub error_parameters: String,
    pub bound_name: char,
}

impl Bounds {
    pub fn add_for_parameter(
        &mut self,
        env: &Env,
        func: &Function,
        par: &CParameter,
        r#async: bool,
        concurrency: Concurrency,
        configured_functions: &[&config::functions::Function],
    ) -> (Option<String>, Option<CallbackInfo>) {
        let type_name = RustType::builder(env, par.typ)
            .ref_mode(RefMode::ByRefFake)
            .try_build();
        if (r#async && async_param_to_remove(&par.name)) || type_name.is_err() {
            return (None, None);
        }
        let mut type_string = type_name.into_string();
        let mut callback_info = None;
        let mut ret = None;
        let mut need_is_into_check = false;

        if !par.instance_parameter && par.direction != ParameterDirection::Out {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
                if r#async && (par.name == "callback" || par.name.ends_with("_callback")) {
                    let func_name = func.c_identifier.as_ref().unwrap();
                    let finish_func_name = finish_function_name(func_name);
                    if let Some(function) = find_function(env, &finish_func_name) {
                        // FIXME: This should work completely based on the analysis of the finish() function
                        // but that a) happens afterwards and b) is not accessible from here either.
                        let mut out_parameters =
                            find_out_parameters(env, function, configured_functions);
                        if use_function_return_for_result(
                            env,
                            function.ret.typ,
                            &func.name,
                            configured_functions,
                        ) {
                            out_parameters.insert(
                                0,
                                RustType::builder(env, function.ret.typ)
                                    .direction(function.ret.direction)
                                    .nullable(function.ret.nullable)
                                    .try_build()
                                    .into_string(),
                            );
                        }
                        let parameters = format_out_parameters(&out_parameters);
                        let error_type = find_error_type(env, function);
                        type_string = format!(
                            "FnOnce(Result<{}, {}>) + Send + 'static",
                            parameters, error_type
                        );
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: parameters,
                            error_parameters: error_type,
                            bound_name,
                        });
                    }
                } else if par.c_type == "GDestroyNotify" || env.library.type_(par.typ).is_function()
                {
                    need_is_into_check = par.c_type != "GDestroyNotify";
                    if let Type::Function(_) = env.library.type_(par.typ) {
                        let callback_parameters_config =
                            configured_functions.iter().find_map(|f| {
                                f.parameters
                                    .iter()
                                    .find(|p| p.ident.is_match(&par.name))
                                    .map(|p| &p.callback_parameters)
                            });

                        let mut rust_ty = RustType::builder(env, par.typ)
                            .direction(par.direction)
                            .scope(par.scope)
                            .concurrency(concurrency);
                        if let Some(callback_parameters_config) = callback_parameters_config {
                            rust_ty =
                                rust_ty.callback_parameters_config(callback_parameters_config);
                        }
                        type_string = rust_ty
                            .try_from_glib(&par.try_from_glib)
                            .try_build()
                            .into_string();
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: String::new(),
                            error_parameters: String::new(),
                            bound_name,
                        });
                    }
                }
                if (!need_is_into_check || !*par.nullable) && par.c_type != "GDestroyNotify" {
                    self.add_parameter(&par.name, &type_string, bound_type, r#async)
                }
            }
        } else if par.instance_parameter {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
            }
        }

        (ret, callback_info)
    }

    pub fn type_for(env: &Env, type_id: TypeId, nullable: Nullable) -> Option<BoundType> {
        use self::BoundType::*;
        match env.library.type_(type_id) {
            Type::Fundamental(Fundamental::Filename | Fundamental::OsString) => Some(AsRef(None)),
            Type::Class(Class {
                final_type: true, ..
            }) => None,
            Type::Class(Class {
                final_type: false, ..
            }) => Some(IsA(None)),
            Type::Interface(..) => Some(IsA(None)),
            Type::List(_) | Type::SList(_) | Type::CArray(_) => None,
            Type::Fundamental(_) if *nullable => None,
            Type::Function(_) => Some(NoWrapper),
            _ => None,
        }
    }

    fn get_to_glib_extra(bound_type: &BoundType) -> String {
        use self::BoundType::*;
        match bound_type {
            AsRef(_) => ".as_ref()".to_owned(),
            IsA(_) => ".as_ref()".to_owned(),
            _ => String::new(),
        }
    }

    pub fn add_parameter(
        &mut self,
        name: &str,
        type_str: &str,
        mut bound_type: BoundType,
        r#async: bool,
    ) {
        if r#async && name == "callback" {
            bound_type = BoundType::NoWrapper;
        }
        if self.used.iter().any(|n| n.parameter_name == name) {
            return;
        }
        let alias = bound_type
            .has_alias()
            .then(|| self.unused.pop_front().expect("No free type aliases!"));
        self.used.push(Bound {
            bound_type,
            parameter_name: name.to_owned(),
            alias,
            type_str: type_str.to_owned(),
            callback_modified: false,
        });
    }

    pub fn get_parameter_bound(&self, name: &str) -> Option<&Bound> {
        self.iter().find(move |n| n.parameter_name == name)
    }

    pub fn update_imports(&self, imports: &mut Imports) {
        //TODO: import with versions
        use self::BoundType::*;
        for used in &self.used {
            match used.bound_type {
                NoWrapper => (),
                IsA(_) => imports.add("glib::object::IsA"),
                AsRef(_) => imports.add_used_type(&used.type_str),
            }
        }
    }

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

    pub fn iter(&self) -> Iter<'_, Bound> {
        self.used.iter()
    }

    pub fn iter_lifetimes(&self) -> Iter<'_, char> {
        self.lifetimes.iter()
    }
}

#[derive(Clone, Debug)]
pub struct PropertyBound {
    pub alias: char,
    pub type_str: String,
}

impl PropertyBound {
    pub fn get(env: &Env, type_id: TypeId) -> Option<PropertyBound> {
        let type_ = env.type_(type_id);
        if type_.is_final_type() {
            return None;
        }
        Some(PropertyBound {
            alias: TYPE_PARAMETERS_START,
            type_str: RustType::builder(env, type_id)
                .ref_mode(RefMode::ByRefFake)
                .try_build()
                .into_string(),
        })
    }
}

fn find_out_parameters(
    env: &Env,
    function: &Function,
    configured_functions: &[&config::functions::Function],
) -> Vec<String> {
    let index_to_ignore = find_index_to_ignore(&function.parameters, Some(&function.ret));
    function
        .parameters
        .iter()
        .enumerate()
        .filter(|&(index, param)| {
            Some(index) != index_to_ignore
                && param.direction == ParameterDirection::Out
                && param.name != "error"
        })
        .map(|(_, param)| {
            // FIXME: This should work completely based on the analysis of the finish() function
            // but that a) happens afterwards and b) is not accessible from here either.
            let nullable = configured_functions
                .iter()
                .find_map(|f| f.parameters.iter().find_map(|p| p.nullable))
                .unwrap_or(param.nullable);

            RustType::builder(env, param.typ)
                .direction(param.direction)
                .nullable(nullable)
                .try_build()
                .into_string()
        })
        .collect()
}

fn format_out_parameters(parameters: &[String]) -> String {
    if parameters.len() == 1 {
        parameters[0].to_string()
    } else {
        format!("({})", parameters.join(", "))
    }
}

fn find_error_type(env: &Env, function: &Function) -> String {
    let error_param = function
        .parameters
        .iter()
        .find(|param| param.direction == ParameterDirection::Out && param.name == "error")
        .expect("error type");
    if let Type::Record(_) = env.type_(error_param.typ) {
        return RustType::builder(env, error_param.typ)
            .direction(error_param.direction)
            .try_build()
            .into_string();
    }
    panic!("cannot find error type")
}

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

    #[test]
    fn get_new_all() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        // Don't add second time
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        bounds.add_parameter("b", "", typ.clone(), false);
        bounds.add_parameter("c", "", typ.clone(), false);
        bounds.add_parameter("d", "", typ.clone(), false);
        bounds.add_parameter("e", "", typ.clone(), false);
        bounds.add_parameter("f", "", typ.clone(), false);
        bounds.add_parameter("g", "", typ.clone(), false);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("i", "", typ.clone(), false);
        bounds.add_parameter("j", "", typ.clone(), false);
        bounds.add_parameter("k", "", typ, false);
    }

    #[test]
    #[should_panic(expected = "No free type aliases!")]
    fn exhaust_type_parameters() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::NoWrapper;
        for c in 'a'..='l' {
            // Should panic on `l` because all type parameters are exhausted
            bounds.add_parameter(c.to_string().as_str(), "", typ.clone(), false);
        }
    }

    #[test]
    fn get_parameter_bound() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::NoWrapper;
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("a").unwrap();
        // `NoWrapper `bounds are expected to have an alias:
        assert_eq!(bound.alias, Some('P'));
        assert_eq!(bound.bound_type, typ);
        let bound = bounds.get_parameter_bound("b").unwrap();
        assert_eq!(bound.alias, Some('Q'));
        assert_eq!(bound.bound_type, typ);
        assert_eq!(bounds.get_parameter_bound("c"), None);
    }

    #[test]
    fn impl_bound() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("a").unwrap();
        // `IsA` is simplified to an inline `foo: impl IsA<Bar>` and
        // lacks an alias/type-parameter:
        assert_eq!(bound.alias, None);
        assert_eq!(bound.bound_type, typ);

        let typ = BoundType::AsRef(None);
        bounds.add_parameter("c", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("c").unwrap();
        // Same `impl AsRef<Foo>` simplification as `IsA`:
        assert_eq!(bound.alias, None);
        assert_eq!(bound.bound_type, typ);
    }
}
<commit_msg>Limit async function return parameter configuration to those where the parameter name actually matches<commit_after>use crate::{
    analysis::{
        function_parameters::{async_param_to_remove, CParameter},
        functions::{find_function, find_index_to_ignore, finish_function_name},
        imports::Imports,
        out_parameters::use_function_return_for_result,
        ref_mode::RefMode,
        rust_type::RustType,
    },
    config,
    consts::TYPE_PARAMETERS_START,
    env::Env,
    library::{
        Class, Concurrency, Function, Fundamental, Nullable, ParameterDirection, Type, TypeId,
    },
    traits::IntoString,
};
use std::{collections::vec_deque::VecDeque, slice::Iter};

#[derive(Clone, Eq, Debug, PartialEq)]
pub enum BoundType {
    NoWrapper,
    // lifetime
    IsA(Option<char>),
    // lifetime <- shouldn't be used but just in case...
    AsRef(Option<char>),
}

impl BoundType {
    pub fn need_isa(&self) -> bool {
        matches!(*self, Self::IsA(_))
    }

    // TODO: This is just a heuristic for now, based on what we do in codegen!
    // Theoretically the surrounding function should determine whether it needs to
    // reuse an alias (ie. to use in `call_func::<P, Q, R>`) or not.
    // In the latter case an `impl` is generated instead of a type name/alias.
    pub fn has_alias(&self) -> bool {
        matches!(*self, Self::NoWrapper)
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Bound {
    pub bound_type: BoundType,
    pub parameter_name: String,
    /// Bound does not have an alias when `param: impl type_str` is used
    pub alias: Option<char>,
    pub type_str: String,
    pub callback_modified: bool,
}

#[derive(Clone, Debug)]
pub struct Bounds {
    unused: VecDeque<char>,
    used: Vec<Bound>,
    unused_lifetimes: VecDeque<char>,
    lifetimes: Vec<char>,
}

impl Default for Bounds {
    fn default() -> Bounds {
        Bounds {
            unused: (TYPE_PARAMETERS_START..)
                .take_while(|x| *x <= 'Z')
                .collect(),
            used: Vec::new(),
            unused_lifetimes: "abcdefg".chars().collect(),
            lifetimes: Vec::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CallbackInfo {
    pub callback_type: String,
    pub success_parameters: String,
    pub error_parameters: String,
    pub bound_name: char,
}

impl Bounds {
    pub fn add_for_parameter(
        &mut self,
        env: &Env,
        func: &Function,
        par: &CParameter,
        r#async: bool,
        concurrency: Concurrency,
        configured_functions: &[&config::functions::Function],
    ) -> (Option<String>, Option<CallbackInfo>) {
        let type_name = RustType::builder(env, par.typ)
            .ref_mode(RefMode::ByRefFake)
            .try_build();
        if (r#async && async_param_to_remove(&par.name)) || type_name.is_err() {
            return (None, None);
        }
        let mut type_string = type_name.into_string();
        let mut callback_info = None;
        let mut ret = None;
        let mut need_is_into_check = false;

        if !par.instance_parameter && par.direction != ParameterDirection::Out {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
                if r#async && (par.name == "callback" || par.name.ends_with("_callback")) {
                    let func_name = func.c_identifier.as_ref().unwrap();
                    let finish_func_name = finish_function_name(func_name);
                    if let Some(function) = find_function(env, &finish_func_name) {
                        // FIXME: This should work completely based on the analysis of the finish() function
                        // but that a) happens afterwards and b) is not accessible from here either.
                        let mut out_parameters =
                            find_out_parameters(env, function, configured_functions);
                        if use_function_return_for_result(
                            env,
                            function.ret.typ,
                            &func.name,
                            configured_functions,
                        ) {
                            out_parameters.insert(
                                0,
                                RustType::builder(env, function.ret.typ)
                                    .direction(function.ret.direction)
                                    .nullable(function.ret.nullable)
                                    .try_build()
                                    .into_string(),
                            );
                        }
                        let parameters = format_out_parameters(&out_parameters);
                        let error_type = find_error_type(env, function);
                        type_string = format!(
                            "FnOnce(Result<{}, {}>) + Send + 'static",
                            parameters, error_type
                        );
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: parameters,
                            error_parameters: error_type,
                            bound_name,
                        });
                    }
                } else if par.c_type == "GDestroyNotify" || env.library.type_(par.typ).is_function()
                {
                    need_is_into_check = par.c_type != "GDestroyNotify";
                    if let Type::Function(_) = env.library.type_(par.typ) {
                        let callback_parameters_config =
                            configured_functions.iter().find_map(|f| {
                                f.parameters
                                    .iter()
                                    .find(|p| p.ident.is_match(&par.name))
                                    .map(|p| &p.callback_parameters)
                            });

                        let mut rust_ty = RustType::builder(env, par.typ)
                            .direction(par.direction)
                            .scope(par.scope)
                            .concurrency(concurrency);
                        if let Some(callback_parameters_config) = callback_parameters_config {
                            rust_ty =
                                rust_ty.callback_parameters_config(callback_parameters_config);
                        }
                        type_string = rust_ty
                            .try_from_glib(&par.try_from_glib)
                            .try_build()
                            .into_string();
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: String::new(),
                            error_parameters: String::new(),
                            bound_name,
                        });
                    }
                }
                if (!need_is_into_check || !*par.nullable) && par.c_type != "GDestroyNotify" {
                    self.add_parameter(&par.name, &type_string, bound_type, r#async)
                }
            }
        } else if par.instance_parameter {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
            }
        }

        (ret, callback_info)
    }

    pub fn type_for(env: &Env, type_id: TypeId, nullable: Nullable) -> Option<BoundType> {
        use self::BoundType::*;
        match env.library.type_(type_id) {
            Type::Fundamental(Fundamental::Filename | Fundamental::OsString) => Some(AsRef(None)),
            Type::Class(Class {
                final_type: true, ..
            }) => None,
            Type::Class(Class {
                final_type: false, ..
            }) => Some(IsA(None)),
            Type::Interface(..) => Some(IsA(None)),
            Type::List(_) | Type::SList(_) | Type::CArray(_) => None,
            Type::Fundamental(_) if *nullable => None,
            Type::Function(_) => Some(NoWrapper),
            _ => None,
        }
    }

    fn get_to_glib_extra(bound_type: &BoundType) -> String {
        use self::BoundType::*;
        match bound_type {
            AsRef(_) => ".as_ref()".to_owned(),
            IsA(_) => ".as_ref()".to_owned(),
            _ => String::new(),
        }
    }

    pub fn add_parameter(
        &mut self,
        name: &str,
        type_str: &str,
        mut bound_type: BoundType,
        r#async: bool,
    ) {
        if r#async && name == "callback" {
            bound_type = BoundType::NoWrapper;
        }
        if self.used.iter().any(|n| n.parameter_name == name) {
            return;
        }
        let alias = bound_type
            .has_alias()
            .then(|| self.unused.pop_front().expect("No free type aliases!"));
        self.used.push(Bound {
            bound_type,
            parameter_name: name.to_owned(),
            alias,
            type_str: type_str.to_owned(),
            callback_modified: false,
        });
    }

    pub fn get_parameter_bound(&self, name: &str) -> Option<&Bound> {
        self.iter().find(move |n| n.parameter_name == name)
    }

    pub fn update_imports(&self, imports: &mut Imports) {
        //TODO: import with versions
        use self::BoundType::*;
        for used in &self.used {
            match used.bound_type {
                NoWrapper => (),
                IsA(_) => imports.add("glib::object::IsA"),
                AsRef(_) => imports.add_used_type(&used.type_str),
            }
        }
    }

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

    pub fn iter(&self) -> Iter<'_, Bound> {
        self.used.iter()
    }

    pub fn iter_lifetimes(&self) -> Iter<'_, char> {
        self.lifetimes.iter()
    }
}

#[derive(Clone, Debug)]
pub struct PropertyBound {
    pub alias: char,
    pub type_str: String,
}

impl PropertyBound {
    pub fn get(env: &Env, type_id: TypeId) -> Option<PropertyBound> {
        let type_ = env.type_(type_id);
        if type_.is_final_type() {
            return None;
        }
        Some(PropertyBound {
            alias: TYPE_PARAMETERS_START,
            type_str: RustType::builder(env, type_id)
                .ref_mode(RefMode::ByRefFake)
                .try_build()
                .into_string(),
        })
    }
}

fn find_out_parameters(
    env: &Env,
    function: &Function,
    configured_functions: &[&config::functions::Function],
) -> Vec<String> {
    let index_to_ignore = find_index_to_ignore(&function.parameters, Some(&function.ret));
    function
        .parameters
        .iter()
        .enumerate()
        .filter(|&(index, param)| {
            Some(index) != index_to_ignore
                && param.direction == ParameterDirection::Out
                && param.name != "error"
        })
        .map(|(_, param)| {
            // FIXME: This should work completely based on the analysis of the finish() function
            // but that a) happens afterwards and b) is not accessible from here either.
            let nullable = configured_functions
                .iter()
                .find_map(|f| {
                    f.parameters
                        .iter()
                        .filter(|p| p.ident.is_match(&param.name))
                        .find_map(|p| p.nullable)
                })
                .unwrap_or(param.nullable);

            RustType::builder(env, param.typ)
                .direction(param.direction)
                .nullable(nullable)
                .try_build()
                .into_string()
        })
        .collect()
}

fn format_out_parameters(parameters: &[String]) -> String {
    if parameters.len() == 1 {
        parameters[0].to_string()
    } else {
        format!("({})", parameters.join(", "))
    }
}

fn find_error_type(env: &Env, function: &Function) -> String {
    let error_param = function
        .parameters
        .iter()
        .find(|param| param.direction == ParameterDirection::Out && param.name == "error")
        .expect("error type");
    if let Type::Record(_) = env.type_(error_param.typ) {
        return RustType::builder(env, error_param.typ)
            .direction(error_param.direction)
            .try_build()
            .into_string();
    }
    panic!("cannot find error type")
}

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

    #[test]
    fn get_new_all() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        // Don't add second time
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        bounds.add_parameter("b", "", typ.clone(), false);
        bounds.add_parameter("c", "", typ.clone(), false);
        bounds.add_parameter("d", "", typ.clone(), false);
        bounds.add_parameter("e", "", typ.clone(), false);
        bounds.add_parameter("f", "", typ.clone(), false);
        bounds.add_parameter("g", "", typ.clone(), false);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("i", "", typ.clone(), false);
        bounds.add_parameter("j", "", typ.clone(), false);
        bounds.add_parameter("k", "", typ, false);
    }

    #[test]
    #[should_panic(expected = "No free type aliases!")]
    fn exhaust_type_parameters() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::NoWrapper;
        for c in 'a'..='l' {
            // Should panic on `l` because all type parameters are exhausted
            bounds.add_parameter(c.to_string().as_str(), "", typ.clone(), false);
        }
    }

    #[test]
    fn get_parameter_bound() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::NoWrapper;
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("a").unwrap();
        // `NoWrapper `bounds are expected to have an alias:
        assert_eq!(bound.alias, Some('P'));
        assert_eq!(bound.bound_type, typ);
        let bound = bounds.get_parameter_bound("b").unwrap();
        assert_eq!(bound.alias, Some('Q'));
        assert_eq!(bound.bound_type, typ);
        assert_eq!(bounds.get_parameter_bound("c"), None);
    }

    #[test]
    fn impl_bound() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("a").unwrap();
        // `IsA` is simplified to an inline `foo: impl IsA<Bar>` and
        // lacks an alias/type-parameter:
        assert_eq!(bound.alias, None);
        assert_eq!(bound.bound_type, typ);

        let typ = BoundType::AsRef(None);
        bounds.add_parameter("c", "", typ.clone(), false);
        let bound = bounds.get_parameter_bound("c").unwrap();
        // Same `impl AsRef<Foo>` simplification as `IsA`:
        assert_eq!(bound.alias, None);
        assert_eq!(bound.bound_type, typ);
    }
}
<|endoftext|>