<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.

use std::error::Error;
use std::fmt;
use std::str::FromStr;

#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// The number of bytes in an ethernet (MAC) address.
pub const ETHER_ADDR_LEN: usize = 6;

/// Structure of a 48-bit Ethernet address.
type EtherAddr = [u8; ETHER_ADDR_LEN];

const LOCAL_ADDR_BIT: u8 = 0x02;
const MULTICAST_ADDR_BIT: u8 = 0x01;

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

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

    /// Construct an all-zero `MacAddr` instance.
    pub fn zero() -> MacAddr {
        Default::default()
    }

    /// Construct a broadcast `MacAddr` instance.
    pub fn broadcast() -> MacAddr {
        [0xff; ETHER_ADDR_LEN].into()
    }

    /// Returns true if a `MacAddr` is an all-zero address.
    pub fn is_zero(&self) -> bool {
        *self == Self::zero()
    }

    /// Returns true if the MacAddr is a universally administered addresses (UAA).
    pub fn is_universal(&self) -> bool {
        !self.is_local()
    }

    /// Returns true if the MacAddr is a locally administered addresses (LAA).
    pub fn is_local(&self) -> bool {
        (self.0 & LOCAL_ADDR_BIT) == LOCAL_ADDR_BIT
    }

    /// Returns true if the MacAddr is a unicast address.
    pub fn is_unicast(&self) -> bool {
        !self.is_multicast()
    }

    /// Returns true if the MacAddr is a multicast address.
    pub fn is_multicast(&self) -> bool {
        (self.0 & MULTICAST_ADDR_BIT) == MULTICAST_ADDR_BIT
    }

    /// Returns true if the MacAddr is a broadcast address.
    pub fn is_broadcast(&self) -> bool {
        *self == Self::broadcast()
    }
}

impl From<EtherAddr> for MacAddr {
    fn from(addr: EtherAddr) -> MacAddr {
        MacAddr(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])
    }
}

impl From<MacAddr> for EtherAddr {
    fn from(addr: MacAddr) -> Self {
        [addr.0, addr.1, addr.2, addr.3, addr.4, addr.5]
    }
}

impl PartialEq<EtherAddr> for MacAddr {
    fn eq(&self, other: &EtherAddr) -> bool {
        *self == MacAddr::from(*other)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(
            fmt,
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            self.0, self.1, self.2, self.3, self.4, self.5
        )
    }
}

#[cfg(feature = "serde")]
impl Serialize for MacAddr {
    /// Serializes the MAC address.
    ///
    /// It serializes either to a string or its binary representation, depending on what the format
    /// prefers.
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            serializer.serialize_str(&format!("{}", self))
        } else {
            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
        }
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MacAddr {
    /// Deserializes the MAC address.
    ///
    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
    /// human-readable property of the deserializer.
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct MacAddrVisitor;
        impl<'de> de::Visitor<'de> for MacAddrVisitor {
            type Value = MacAddr;

            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
                value.parse().map_err(|err| E::custom(&format!("{}", err)))
            }

            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
                if v.len() == 6 {
                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
                } else {
                    Err(E::invalid_length(v.len(), &self))
                }
            }

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(
                    formatter,
                    "either a string representation of a MAC address or 6-element byte array"
                )
            }
        }

        // Decide what hint to provide to the deserializer based on if it is human readable or not
        if deserializer.is_human_readable() {
            deserializer.deserialize_str(MacAddrVisitor)
        } else {
            deserializer.deserialize_bytes(MacAddrVisitor)
        }
    }
}

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

/// 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 Error for ParseMacAddrErr {}

impl ParseMacAddrErr {
    fn description(&self) -> &str {
        match *self {
            ParseMacAddrErr::TooManyComponents => "Too many components in a MAC address string",
            ParseMacAddrErr::TooFewComponents => "Too few components in a MAC address string",
            ParseMacAddrErr::InvalidComponent => "Invalid component in a MAC address string",
        }
    }
}

impl fmt::Display for ParseMacAddrErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

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)
        }
    }
}

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

    #[test]
    fn mac_addr_from_str() {
        assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr::zero()));
        assert_eq!("ff:ff:ff:ff:ff:ff".parse(), Ok(MacAddr::broadcast()));
        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)
        );
    }

    #[test]
    fn str_from_mac_addr() {
        assert_eq!(format!("{}", MacAddr::zero()), "00:00:00:00:00:00");
        assert_eq!(format!("{}", MacAddr::broadcast()), "ff:ff:ff:ff:ff:ff");
        assert_eq!(
            format!("{}", MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
            "12:34:56:78:09:ab"
        );
    }

    #[test]
    fn type_of_addr() {
        assert!(MacAddr::zero().is_zero());
        assert!(MacAddr::broadcast().is_broadcast());

        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        assert!(mac.is_local());
        assert!(mac.is_unicast());

        let mac = MacAddr(0xac, 0x87, 0xa3, 0x07, 0x32, 0xb8);
        assert!(mac.is_universal());
        assert!(mac.is_unicast());
    }

    #[test]
    fn convertion() {
        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        let addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB];

        assert_eq!(mac, MacAddr::from(addr));
        assert_eq!(addr, EtherAddr::from(mac));
        assert!(mac == addr);
    }

    #[cfg(feature = "serde")]
    mod serde {
        extern crate serde_test;
        use self::serde_test::{
            assert_de_tokens, assert_de_tokens_error, assert_tokens, Compact, Configure, Readable,
            Token,
        };
        use super::*;

        #[test]
        fn string() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.readable(), &[Token::Str("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::String("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::BorrowedStr("11:22:33:44:55:66")]);
            assert_de_tokens_error::<Readable<MacAddr>>(
                &[Token::Str("not an address")],
                "Invalid component in a MAC address string",
            );
            // It still can detect bytes if provided
            assert_de_tokens(
                &mac.readable(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
        }

        #[test]
        fn bytes() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(
                &mac.compact(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            assert_de_tokens(
                &mac.compact(),
                &[Token::BorrowedBytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            let err = "invalid length 2, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(&[Token::Bytes(&[0x11, 0x33])], err);
            let err = "invalid length 7, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])],
                err,
            );
            // Still can decode string in the compact mode
            assert_de_tokens(&mac.compact(), &[Token::Str("11:22:33:44:55:66")]);
        }
    }
}
<commit_msg>get MacAddr octets as fixed-size array<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.

use std::error::Error;
use std::fmt;
use std::str::FromStr;

#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// The number of bytes in an ethernet (MAC) address.
pub const ETHER_ADDR_LEN: usize = 6;

/// Structure of a 48-bit Ethernet address.
type EtherAddr = [u8; ETHER_ADDR_LEN];

const LOCAL_ADDR_BIT: u8 = 0x02;
const MULTICAST_ADDR_BIT: u8 = 0x01;

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

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

    /// Construct an all-zero `MacAddr` instance.
    pub fn zero() -> MacAddr {
        Default::default()
    }

    /// Construct a broadcast `MacAddr` instance.
    pub fn broadcast() -> MacAddr {
        [0xff; ETHER_ADDR_LEN].into()
    }

    /// Returns true if a `MacAddr` is an all-zero address.
    pub fn is_zero(&self) -> bool {
        *self == Self::zero()
    }

    /// Returns true if the MacAddr is a universally administered addresses (UAA).
    pub fn is_universal(&self) -> bool {
        !self.is_local()
    }

    /// Returns true if the MacAddr is a locally administered addresses (LAA).
    pub fn is_local(&self) -> bool {
        (self.0 & LOCAL_ADDR_BIT) == LOCAL_ADDR_BIT
    }

    /// Returns true if the MacAddr is a unicast address.
    pub fn is_unicast(&self) -> bool {
        !self.is_multicast()
    }

    /// Returns true if the MacAddr is a multicast address.
    pub fn is_multicast(&self) -> bool {
        (self.0 & MULTICAST_ADDR_BIT) == MULTICAST_ADDR_BIT
    }

    /// Returns true if the MacAddr is a broadcast address.
    pub fn is_broadcast(&self) -> bool {
        *self == Self::broadcast()
    }

    /// Returns the six eight-bit integers that make up this address
    pub fn octets(&self) -> [u8; 6] {
        [self.0, self.1, self.2, self.3, self.4, self.5]
    }
}

impl From<EtherAddr> for MacAddr {
    fn from(addr: EtherAddr) -> MacAddr {
        MacAddr(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])
    }
}

impl From<MacAddr> for EtherAddr {
    fn from(addr: MacAddr) -> Self {
        [addr.0, addr.1, addr.2, addr.3, addr.4, addr.5]
    }
}

impl PartialEq<EtherAddr> for MacAddr {
    fn eq(&self, other: &EtherAddr) -> bool {
        *self == MacAddr::from(*other)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(
            fmt,
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            self.0, self.1, self.2, self.3, self.4, self.5
        )
    }
}

#[cfg(feature = "serde")]
impl Serialize for MacAddr {
    /// Serializes the MAC address.
    ///
    /// It serializes either to a string or its binary representation, depending on what the format
    /// prefers.
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            serializer.serialize_str(&format!("{}", self))
        } else {
            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
        }
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MacAddr {
    /// Deserializes the MAC address.
    ///
    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
    /// human-readable property of the deserializer.
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct MacAddrVisitor;
        impl<'de> de::Visitor<'de> for MacAddrVisitor {
            type Value = MacAddr;

            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
                value.parse().map_err(|err| E::custom(&format!("{}", err)))
            }

            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
                if v.len() == 6 {
                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
                } else {
                    Err(E::invalid_length(v.len(), &self))
                }
            }

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(
                    formatter,
                    "either a string representation of a MAC address or 6-element byte array"
                )
            }
        }

        // Decide what hint to provide to the deserializer based on if it is human readable or not
        if deserializer.is_human_readable() {
            deserializer.deserialize_str(MacAddrVisitor)
        } else {
            deserializer.deserialize_bytes(MacAddrVisitor)
        }
    }
}

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

/// 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 Error for ParseMacAddrErr {}

impl ParseMacAddrErr {
    fn description(&self) -> &str {
        match *self {
            ParseMacAddrErr::TooManyComponents => "Too many components in a MAC address string",
            ParseMacAddrErr::TooFewComponents => "Too few components in a MAC address string",
            ParseMacAddrErr::InvalidComponent => "Invalid component in a MAC address string",
        }
    }
}

impl fmt::Display for ParseMacAddrErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

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)
        }
    }
}

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

    #[test]
    fn mac_addr_from_str() {
        assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr::zero()));
        assert_eq!("ff:ff:ff:ff:ff:ff".parse(), Ok(MacAddr::broadcast()));
        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)
        );
    }

    #[test]
    fn str_from_mac_addr() {
        assert_eq!(format!("{}", MacAddr::zero()), "00:00:00:00:00:00");
        assert_eq!(format!("{}", MacAddr::broadcast()), "ff:ff:ff:ff:ff:ff");
        assert_eq!(
            format!("{}", MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
            "12:34:56:78:09:ab"
        );
    }

    #[test]
    fn fixed_size_slice_from_mac_addr() {
        assert_eq!(
            MacAddr(0x00, 0x01, 0x02, 0x03, 0x04, 0x05).octets(),
            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05]
        );
    }

    #[test]
    fn type_of_addr() {
        assert!(MacAddr::zero().is_zero());
        assert!(MacAddr::broadcast().is_broadcast());

        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        assert!(mac.is_local());
        assert!(mac.is_unicast());

        let mac = MacAddr(0xac, 0x87, 0xa3, 0x07, 0x32, 0xb8);
        assert!(mac.is_universal());
        assert!(mac.is_unicast());
    }

    #[test]
    fn convertion() {
        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        let addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB];

        assert_eq!(mac, MacAddr::from(addr));
        assert_eq!(addr, EtherAddr::from(mac));
        assert!(mac == addr);
    }

    #[cfg(feature = "serde")]
    mod serde {
        extern crate serde_test;
        use self::serde_test::{
            assert_de_tokens, assert_de_tokens_error, assert_tokens, Compact, Configure, Readable,
            Token,
        };
        use super::*;

        #[test]
        fn string() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.readable(), &[Token::Str("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::String("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::BorrowedStr("11:22:33:44:55:66")]);
            assert_de_tokens_error::<Readable<MacAddr>>(
                &[Token::Str("not an address")],
                "Invalid component in a MAC address string",
            );
            // It still can detect bytes if provided
            assert_de_tokens(
                &mac.readable(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
        }

        #[test]
        fn bytes() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(
                &mac.compact(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            assert_de_tokens(
                &mac.compact(),
                &[Token::BorrowedBytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            let err = "invalid length 2, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(&[Token::Bytes(&[0x11, 0x33])], err);
            let err = "invalid length 7, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])],
                err,
            );
            // Still can decode string in the compact mode
            assert_de_tokens(&mac.compact(), &[Token::Str("11:22:33:44:55:66")]);
        }
    }
}
<|endoftext|>


<commit_before>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_alias, doc_hidden, not_version_condition,
        version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
    special_functions,
};
use crate::{
    analysis::{
        self, bounds::Bounds, functions::FuncVisibility, namespaces, try_from_glib::TryFromGlib,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    version::Version,
    writer::{primitives::tabs, safety_assertion_mode_to_str, ToCode},
};
use log::warn;
use std::{
    fmt,
    io::{Result, Write},
    result::Result as StdResult,
};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::functions::Info,
    special_functions: Option<&analysis::special_functions::Infos>,
    scope_version: Option<Version>,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if !analysis.status.need_generate() {
        return Ok(());
    }

    if analysis.is_async_finish(env) {
        return Ok(());
    }

    if let Some(special_functions) = special_functions {
        if special_functions::generate(w, env, analysis, special_functions, scope_version)? {
            return Ok(());
        }
    }

    let mut commented = false;
    let mut comment_prefix = "";
    let mut pub_prefix = if in_trait {
        "".to_owned()
    } else {
        format!("{} ", analysis.visibility)
    };

    match analysis.func_visibility {
        FuncVisibility::Public => {}
        FuncVisibility::Comment => {
            commented = true;
            comment_prefix = "//";
        }
        FuncVisibility::Private => {
            if in_trait {
                warn!(
                    "Generating trait method for private function {}",
                    analysis.glib_name
                );
            } else {
                pub_prefix = "".to_string();
            }
        }
        FuncVisibility::Hidden => return Ok(()),
    }
    let unsafe_ = if analysis.unsafe_ { "unsafe " } else { "" };
    let declaration = declaration(env, analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    writeln!(w)?;
    if !in_trait || only_declaration {
        cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
    }
    cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
    let version = Version::if_stricter_than(analysis.version, scope_version);
    version_condition(w, env, None, version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    if !in_trait || only_declaration {
        doc_alias(w, &analysis.glib_name, comment_prefix, indent)?;
        if analysis.codegen_name() != analysis.func_name {
            doc_alias(w, &analysis.func_name, comment_prefix, indent)?;
        }
    }
    writeln!(
        w,
        "{}{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        unsafe_,
        declaration,
        suffix,
    )?;

    if !only_declaration {
        let body = body_chunk(env, analysis).to_code(env);
        for s in body {
            writeln!(w, "{}{}", tabs(indent), s)?;
        }
    }

    if analysis.async_future.is_some() {
        let declaration = declaration_futures(env, analysis);
        let suffix = if only_declaration { ";" } else { " {" };

        writeln!(w)?;
        if !in_trait || only_declaration {
            cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
        }

        writeln!(w, "{}{}", tabs(indent), comment_prefix)?;
        cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
        version_condition(w, env, None, version, commented, indent)?;
        not_version_condition(w, analysis.not_version, commented, indent)?;
        doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
        writeln!(
            w,
            "{}{}{}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            declaration,
            suffix
        )?;

        if !only_declaration {
            let body = body_chunk_futures(env, analysis).unwrap();
            for s in body.lines() {
                if !s.is_empty() {
                    writeln!(w, "{}{}{}", tabs(indent + 1), comment_prefix, s)?;
                } else {
                    writeln!(w)?;
                }
            }
            writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
        }
    }

    Ok(())
}

pub fn declaration(env: &Env, analysis: &analysis::functions::Info) -> String {
    let outs_as_return = !analysis.outs.is_empty();
    let return_str = if outs_as_return {
        out_parameters_as_return(env, analysis)
    } else if analysis.ret.bool_return_is_error.is_some() {
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            " -> Result<(), error::BoolError>".into()
        } else {
            " -> Result<(), glib::error::BoolError>".into()
        }
    } else if let Some(return_type) = analysis.ret.to_return_value(
        env,
        analysis
            .ret
            .parameter
            .as_ref()
            .map_or(&TryFromGlib::Default, |par| &par.try_from_glib),
        false,
    ) {
        format!(" -> {}", return_type)
    } else {
        String::new()
    };
    let mut param_str = String::with_capacity(100);

    let (bounds, _) = bounds(&analysis.bounds, &[], false, false);

    for par in analysis.parameters.rust_parameters.iter() {
        if !param_str.is_empty() {
            param_str.push_str(", ")
        }
        let c_par = &analysis.parameters.c_parameters[par.ind_c];
        let s = c_par.to_parameter(env, &analysis.bounds, false);
        param_str.push_str(&s);
    }

    format!(
        "fn {}{}({}){}",
        analysis.codegen_name(),
        bounds,
        param_str,
        return_str,
    )
}

pub fn declaration_futures(env: &Env, analysis: &analysis::functions::Info) -> String {
    let async_future = analysis.async_future.as_ref().unwrap();

    let return_str = format!(
        " -> Pin<Box_<dyn std::future::Future<Output = Result<{}, {}>> + 'static>>",
        async_future.success_parameters, async_future.error_parameters
    );

    let mut param_str = String::with_capacity(100);

    let mut skipped = 0;
    let mut skipped_bounds = vec![];
    for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        if c_par.name == "callback" || c_par.name == "cancellable" {
            skipped += 1;
            if let Some(alias) = analysis
                .bounds
                .get_parameter_bound(&c_par.name)
                .and_then(|bound| bound.type_parameter_reference())
            {
                skipped_bounds.push(alias);
            }
            continue;
        }

        if pos - skipped > 0 {
            param_str.push_str(", ")
        }

        let s = c_par.to_parameter(env, &analysis.bounds, true);
        param_str.push_str(&s);
    }

    let (bounds, _) = bounds(&analysis.bounds, skipped_bounds.as_ref(), true, false);

    format!(
        "fn {}{}({}){}",
        async_future.name, bounds, param_str, return_str,
    )
}

pub fn bounds(
    bounds: &Bounds,
    skip: &[char],
    r#async: bool,
    filter_callback_modified: bool,
) -> (String, Vec<String>) {
    use crate::analysis::bounds::BoundType::*;

    if bounds.is_empty() {
        return (String::new(), Vec::new());
    }

    let skip_lifetimes = bounds
        .iter()
        // TODO: False or true?
        .filter(|bound| bound.alias.map_or(false, |alias| skip.contains(&alias)))
        .filter_map(|bound| match bound.bound_type {
            IsA(lifetime) | AsRef(lifetime) => lifetime,
            _ => None,
        })
        .collect::<Vec<_>>();

    let lifetimes = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .collect::<Vec<_>>();

    let bounds = bounds.iter().filter(|bound| {
        bound.alias.map_or(true, |alias| !skip.contains(&alias))
            && (!filter_callback_modified || !bound.callback_modified)
    });

    let type_names = lifetimes
        .iter()
        .cloned()
        .chain(
            bounds
                .clone()
                .filter_map(|b| b.type_parameter_definition(r#async)),
        )
        .collect::<Vec<_>>();

    let type_names = if type_names.is_empty() {
        String::new()
    } else {
        format!("<{}>", type_names.join(", "))
    };

    let bounds = lifetimes
        .into_iter()
        // TODO: enforce that this is only used on NoWrapper!
        // TODO: Analyze if alias is used in function, otherwise set to None!
        .chain(bounds.filter_map(|b| b.alias).map(|a| a.to_string()))
        .collect::<Vec<_>>();

    (type_names, bounds)
}

pub fn body_chunk(env: &Env, analysis: &analysis::functions::Info) -> Chunk {
    if analysis.func_visibility == FuncVisibility::Comment {
        return ffi_function_todo(env, &analysis.glib_name);
    }

    let outs_as_return = !analysis.outs.is_empty();

    let mut builder = function_body_chunk::Builder::new();
    builder
        .glib_name(&format!(
            "{}::{}",
            env.main_sys_crate_name(),
            analysis.glib_name
        ))
        .assertion(analysis.assertion)
        .ret(&analysis.ret)
        .transformations(&analysis.parameters.transformations)
        .in_unsafe(analysis.unsafe_)
        .outs_mode(analysis.outs.mode);

    if analysis.r#async {
        if let Some(ref trampoline) = analysis.trampoline {
            builder.async_trampoline(trampoline);
        } else {
            warn!(
                "Async function {} has no associated _finish function",
                analysis.codegen_name(),
            );
        }
    } else {
        for trampoline in analysis.callbacks.iter() {
            builder.callback(trampoline);
        }
        for trampoline in analysis.destroys.iter() {
            builder.destroy(trampoline);
        }
    }

    for par in &analysis.parameters.c_parameters {
        if outs_as_return && analysis.outs.iter().any(|out| out.lib_par.name == par.name) {
            builder.out_parameter(env, par);
        } else {
            builder.parameter();
        }
    }

    let (bounds, bounds_names) = bounds(&analysis.bounds, &[], false, true);

    builder.generate(env, bounds, bounds_names.join(", "))
}

pub fn body_chunk_futures(
    env: &Env,
    analysis: &analysis::functions::Info,
) -> StdResult<String, fmt::Error> {
    use crate::analysis::ref_mode::RefMode;
    use std::fmt::Write;

    let async_future = analysis.async_future.as_ref().unwrap();

    let mut body = String::new();

    let gio_future_name = if env.config.library_name != "Gio" {
        "gio::GioFuture"
    } else {
        "crate::GioFuture"
    };
    writeln!(body)?;

    if !async_future.assertion.is_none() {
        writeln!(
            body,
            "{}",
            safety_assertion_mode_to_str(async_future.assertion)
        )?;
    }
    let skip = if async_future.is_method { 1 } else { 0 };

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" || par.name == "callback" {
            continue;
        }

        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        let type_ = env.type_(par.typ);
        let is_str = matches!(
            *type_,
            library::Type::Fundamental(library::Fundamental::Utf8)
        );

        if *c_par.nullable {
            writeln!(
                body,
                "let {} = {}.map(ToOwned::to_owned);",
                par.name, par.name
            )?;
        } else if is_str {
            writeln!(body, "let {} = String::from({});", par.name, par.name)?;
        } else if c_par.ref_mode != RefMode::None {
            writeln!(body, "let {} = {}.clone();", par.name, par.name)?;
        }
    }

    if async_future.is_method {
        writeln!(
            body,
            "Box_::pin({}::new(self, move |obj, cancellable, send| {{",
            gio_future_name
        )?;
    } else {
        writeln!(
            body,
            "Box_::pin({}::new(&(), move |_obj, cancellable, send| {{",
            gio_future_name
        )?;
    }

    if async_future.is_method {
        writeln!(body, "\tobj.{}(", analysis.codegen_name())?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.codegen_name())?;
    } else {
        writeln!(body, "\t{}(", analysis.codegen_name())?;
    }

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" {
            writeln!(body, "\t\tSome(cancellable),")?;
        } else if par.name == "callback" {
            continue;
        } else {
            let c_par = &analysis.parameters.c_parameters[par.ind_c];

            if *c_par.nullable {
                writeln!(
                    body,
                    "\t\t{}.as_ref().map(::std::borrow::Borrow::borrow),",
                    par.name
                )?;
            } else if c_par.ref_mode != RefMode::None {
                writeln!(body, "\t\t&{},", par.name)?;
            } else {
                writeln!(body, "\t\t{},", par.name)?;
            }
        }
    }

    writeln!(body, "\t\tmove |res| {{")?;
    writeln!(body, "\t\t\tsend.resolve(res);")?;
    writeln!(body, "\t\t}},")?;
    writeln!(body, "\t);")?;
    writeln!(body, "}}))")?;

    Ok(body)
}
<commit_msg>codgen: add a allow dead code guard for not public functions<commit_after>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_alias, doc_hidden, not_version_condition,
        version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
    special_functions,
};
use crate::{
    analysis::{
        self, bounds::Bounds, functions::FuncVisibility, namespaces, try_from_glib::TryFromGlib,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    version::Version,
    writer::{primitives::tabs, safety_assertion_mode_to_str, ToCode},
};
use log::warn;
use std::{
    fmt,
    io::{Result, Write},
    result::Result as StdResult,
};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::functions::Info,
    special_functions: Option<&analysis::special_functions::Infos>,
    scope_version: Option<Version>,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if !analysis.status.need_generate() {
        return Ok(());
    }

    if analysis.is_async_finish(env) {
        return Ok(());
    }

    if let Some(special_functions) = special_functions {
        if special_functions::generate(w, env, analysis, special_functions, scope_version)? {
            return Ok(());
        }
    }

    let mut commented = false;
    let mut comment_prefix = "";
    let mut pub_prefix = if in_trait {
        "".to_owned()
    } else {
        format!("{} ", analysis.visibility)
    };

    match analysis.func_visibility {
        FuncVisibility::Public => {}
        FuncVisibility::Comment => {
            commented = true;
            comment_prefix = "//";
        }
        FuncVisibility::Private => {
            if in_trait {
                warn!(
                    "Generating trait method for private function {}",
                    analysis.glib_name
                );
            } else {
                pub_prefix = "".to_string();
            }
        }
        FuncVisibility::Hidden => return Ok(()),
    }
    let unsafe_ = if analysis.unsafe_ { "unsafe " } else { "" };
    let declaration = declaration(env, analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    writeln!(w)?;
    if !in_trait || only_declaration {
        cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
    }
    cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
    let version = Version::if_stricter_than(analysis.version, scope_version);
    version_condition(w, env, None, version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    if !in_trait || only_declaration {
        doc_alias(w, &analysis.glib_name, comment_prefix, indent)?;
        if analysis.codegen_name() != analysis.func_name {
            doc_alias(w, &analysis.func_name, comment_prefix, indent)?;
        }
    }
    let dead_code_cfg = if !analysis.visibility.is_public() {
        "#[allow(dead_code)]"
    } else {
        ""
    };

    writeln!(
        w,
        "{}{}{}{}{}{}{}",
        dead_code_cfg,
        tabs(indent),
        comment_prefix,
        pub_prefix,
        unsafe_,
        declaration,
        suffix,
    )?;

    if !only_declaration {
        let body = body_chunk(env, analysis).to_code(env);
        for s in body {
            writeln!(w, "{}{}", tabs(indent), s)?;
        }
    }

    if analysis.async_future.is_some() {
        let declaration = declaration_futures(env, analysis);
        let suffix = if only_declaration { ";" } else { " {" };

        writeln!(w)?;
        if !in_trait || only_declaration {
            cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
        }

        writeln!(w, "{}{}", tabs(indent), comment_prefix)?;
        cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
        version_condition(w, env, None, version, commented, indent)?;
        not_version_condition(w, analysis.not_version, commented, indent)?;
        doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
        writeln!(
            w,
            "{}{}{}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            declaration,
            suffix
        )?;

        if !only_declaration {
            let body = body_chunk_futures(env, analysis).unwrap();
            for s in body.lines() {
                if !s.is_empty() {
                    writeln!(w, "{}{}{}", tabs(indent + 1), comment_prefix, s)?;
                } else {
                    writeln!(w)?;
                }
            }
            writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
        }
    }

    Ok(())
}

pub fn declaration(env: &Env, analysis: &analysis::functions::Info) -> String {
    let outs_as_return = !analysis.outs.is_empty();
    let return_str = if outs_as_return {
        out_parameters_as_return(env, analysis)
    } else if analysis.ret.bool_return_is_error.is_some() {
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            " -> Result<(), error::BoolError>".into()
        } else {
            " -> Result<(), glib::error::BoolError>".into()
        }
    } else if let Some(return_type) = analysis.ret.to_return_value(
        env,
        analysis
            .ret
            .parameter
            .as_ref()
            .map_or(&TryFromGlib::Default, |par| &par.try_from_glib),
        false,
    ) {
        format!(" -> {}", return_type)
    } else {
        String::new()
    };
    let mut param_str = String::with_capacity(100);

    let (bounds, _) = bounds(&analysis.bounds, &[], false, false);

    for par in analysis.parameters.rust_parameters.iter() {
        if !param_str.is_empty() {
            param_str.push_str(", ")
        }
        let c_par = &analysis.parameters.c_parameters[par.ind_c];
        let s = c_par.to_parameter(env, &analysis.bounds, false);
        param_str.push_str(&s);
    }

    format!(
        "fn {}{}({}){}",
        analysis.codegen_name(),
        bounds,
        param_str,
        return_str,
    )
}

pub fn declaration_futures(env: &Env, analysis: &analysis::functions::Info) -> String {
    let async_future = analysis.async_future.as_ref().unwrap();

    let return_str = format!(
        " -> Pin<Box_<dyn std::future::Future<Output = Result<{}, {}>> + 'static>>",
        async_future.success_parameters, async_future.error_parameters
    );

    let mut param_str = String::with_capacity(100);

    let mut skipped = 0;
    let mut skipped_bounds = vec![];
    for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        if c_par.name == "callback" || c_par.name == "cancellable" {
            skipped += 1;
            if let Some(alias) = analysis
                .bounds
                .get_parameter_bound(&c_par.name)
                .and_then(|bound| bound.type_parameter_reference())
            {
                skipped_bounds.push(alias);
            }
            continue;
        }

        if pos - skipped > 0 {
            param_str.push_str(", ")
        }

        let s = c_par.to_parameter(env, &analysis.bounds, true);
        param_str.push_str(&s);
    }

    let (bounds, _) = bounds(&analysis.bounds, skipped_bounds.as_ref(), true, false);

    format!(
        "fn {}{}({}){}",
        async_future.name, bounds, param_str, return_str,
    )
}

pub fn bounds(
    bounds: &Bounds,
    skip: &[char],
    r#async: bool,
    filter_callback_modified: bool,
) -> (String, Vec<String>) {
    use crate::analysis::bounds::BoundType::*;

    if bounds.is_empty() {
        return (String::new(), Vec::new());
    }

    let skip_lifetimes = bounds
        .iter()
        // TODO: False or true?
        .filter(|bound| bound.alias.map_or(false, |alias| skip.contains(&alias)))
        .filter_map(|bound| match bound.bound_type {
            IsA(lifetime) | AsRef(lifetime) => lifetime,
            _ => None,
        })
        .collect::<Vec<_>>();

    let lifetimes = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .collect::<Vec<_>>();

    let bounds = bounds.iter().filter(|bound| {
        bound.alias.map_or(true, |alias| !skip.contains(&alias))
            && (!filter_callback_modified || !bound.callback_modified)
    });

    let type_names = lifetimes
        .iter()
        .cloned()
        .chain(
            bounds
                .clone()
                .filter_map(|b| b.type_parameter_definition(r#async)),
        )
        .collect::<Vec<_>>();

    let type_names = if type_names.is_empty() {
        String::new()
    } else {
        format!("<{}>", type_names.join(", "))
    };

    let bounds = lifetimes
        .into_iter()
        // TODO: enforce that this is only used on NoWrapper!
        // TODO: Analyze if alias is used in function, otherwise set to None!
        .chain(bounds.filter_map(|b| b.alias).map(|a| a.to_string()))
        .collect::<Vec<_>>();

    (type_names, bounds)
}

pub fn body_chunk(env: &Env, analysis: &analysis::functions::Info) -> Chunk {
    if analysis.func_visibility == FuncVisibility::Comment {
        return ffi_function_todo(env, &analysis.glib_name);
    }

    let outs_as_return = !analysis.outs.is_empty();

    let mut builder = function_body_chunk::Builder::new();
    builder
        .glib_name(&format!(
            "{}::{}",
            env.main_sys_crate_name(),
            analysis.glib_name
        ))
        .assertion(analysis.assertion)
        .ret(&analysis.ret)
        .transformations(&analysis.parameters.transformations)
        .in_unsafe(analysis.unsafe_)
        .outs_mode(analysis.outs.mode);

    if analysis.r#async {
        if let Some(ref trampoline) = analysis.trampoline {
            builder.async_trampoline(trampoline);
        } else {
            warn!(
                "Async function {} has no associated _finish function",
                analysis.codegen_name(),
            );
        }
    } else {
        for trampoline in analysis.callbacks.iter() {
            builder.callback(trampoline);
        }
        for trampoline in analysis.destroys.iter() {
            builder.destroy(trampoline);
        }
    }

    for par in &analysis.parameters.c_parameters {
        if outs_as_return && analysis.outs.iter().any(|out| out.lib_par.name == par.name) {
            builder.out_parameter(env, par);
        } else {
            builder.parameter();
        }
    }

    let (bounds, bounds_names) = bounds(&analysis.bounds, &[], false, true);

    builder.generate(env, bounds, bounds_names.join(", "))
}

pub fn body_chunk_futures(
    env: &Env,
    analysis: &analysis::functions::Info,
) -> StdResult<String, fmt::Error> {
    use crate::analysis::ref_mode::RefMode;
    use std::fmt::Write;

    let async_future = analysis.async_future.as_ref().unwrap();

    let mut body = String::new();

    let gio_future_name = if env.config.library_name != "Gio" {
        "gio::GioFuture"
    } else {
        "crate::GioFuture"
    };
    writeln!(body)?;

    if !async_future.assertion.is_none() {
        writeln!(
            body,
            "{}",
            safety_assertion_mode_to_str(async_future.assertion)
        )?;
    }
    let skip = if async_future.is_method { 1 } else { 0 };

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" || par.name == "callback" {
            continue;
        }

        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        let type_ = env.type_(par.typ);
        let is_str = matches!(
            *type_,
            library::Type::Fundamental(library::Fundamental::Utf8)
        );

        if *c_par.nullable {
            writeln!(
                body,
                "let {} = {}.map(ToOwned::to_owned);",
                par.name, par.name
            )?;
        } else if is_str {
            writeln!(body, "let {} = String::from({});", par.name, par.name)?;
        } else if c_par.ref_mode != RefMode::None {
            writeln!(body, "let {} = {}.clone();", par.name, par.name)?;
        }
    }

    if async_future.is_method {
        writeln!(
            body,
            "Box_::pin({}::new(self, move |obj, cancellable, send| {{",
            gio_future_name
        )?;
    } else {
        writeln!(
            body,
            "Box_::pin({}::new(&(), move |_obj, cancellable, send| {{",
            gio_future_name
        )?;
    }

    if async_future.is_method {
        writeln!(body, "\tobj.{}(", analysis.codegen_name())?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.codegen_name())?;
    } else {
        writeln!(body, "\t{}(", analysis.codegen_name())?;
    }

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" {
            writeln!(body, "\t\tSome(cancellable),")?;
        } else if par.name == "callback" {
            continue;
        } else {
            let c_par = &analysis.parameters.c_parameters[par.ind_c];

            if *c_par.nullable {
                writeln!(
                    body,
                    "\t\t{}.as_ref().map(::std::borrow::Borrow::borrow),",
                    par.name
                )?;
            } else if c_par.ref_mode != RefMode::None {
                writeln!(body, "\t\t&{},", par.name)?;
            } else {
                writeln!(body, "\t\t{},", par.name)?;
            }
        }
    }

    writeln!(body, "\t\tmove |res| {{")?;
    writeln!(body, "\t\t\tsend.resolve(res);")?;
    writeln!(body, "\t\t}},")?;
    writeln!(body, "\t);")?;
    writeln!(body, "}}))")?;

    Ok(body)
}
<|endoftext|>


<commit_before>import middle::trans;

import trans::decl_cdecl_fn;
import trans::type_names;
import trans::ModuleRef;
import trans::TypeRef;
import trans::ValueRef;

import trans::T_crate;
import trans::T_f32;
import trans::T_f64;
import trans::T_fn;
import trans::T_i8;
import trans::T_i32;
import trans::T_int;
import trans::T_opaque_chan_ptr;
import trans::T_opaque_port_ptr;
import trans::T_opaque_vec_ptr;
import trans::T_ptr;
import trans::T_size_t;
import trans::T_str;
import trans::T_taskptr;
import trans::T_tydesc;
import trans::T_void;

type upcalls = rec(
    ValueRef grow_task,
    ValueRef log_int,
    ValueRef log_float,
    ValueRef log_double,
    ValueRef log_str,
    ValueRef trace_word,
    ValueRef trace_str,
    ValueRef new_port,
    ValueRef del_port,
    ValueRef new_chan,
    ValueRef flush_chan,
    ValueRef del_chan,
    ValueRef clone_chan,
    ValueRef _yield,
    ValueRef sleep,
    ValueRef _join,
    ValueRef send,
    ValueRef recv,
    ValueRef _fail,
    ValueRef kill,
    ValueRef exit,
    ValueRef malloc,
    ValueRef free,
    ValueRef mark,
    ValueRef new_str,
    ValueRef new_vec,
    ValueRef vec_grow,
    ValueRef require_rust_sym,
    ValueRef require_c_sym,
    ValueRef get_type_desc,
    ValueRef new_task,
    ValueRef start_task,
    ValueRef new_thread,
    ValueRef start_thread
);

fn declare_upcalls(type_names tn, ModuleRef llmod) -> @upcalls {
    fn decl(type_names tn, ModuleRef llmod, str name, vec[TypeRef] tys,
            TypeRef rv) -> ValueRef {
        let vec[TypeRef] arg_tys = [T_taskptr(tn)];
        for (TypeRef t in tys) { arg_tys += [t]; }
        auto fn_ty = T_fn(arg_tys, rv);
        ret trans::decl_cdecl_fn(llmod, "upcall_" + name, fn_ty);
    }

    auto dv = bind decl(tn, llmod, _, _, T_void());
    auto d = bind decl(tn, llmod, _, _, _);

    // FIXME: Sigh:.. remove this when I fix the typechecker pushdown.
    // --pcwalton
    let vec[TypeRef] empty_vec = [];

    ret @rec(
        grow_task=dv("grow_task", [T_size_t()]),
        log_int=dv("log_int", [T_i32(), T_i32()]),
        log_float=dv("log_float", [T_i32(), T_f32()]),
        log_double=dv("log_double", [T_i32(), T_ptr(T_f64())]),
        log_str=dv("log_str", [T_i32(), T_ptr(T_str())]),
        trace_word=dv("trace_word", [T_int()]),
        trace_str=dv("trace_str", [T_ptr(T_i8())]),
        new_port=d("new_port", [T_size_t()], T_opaque_port_ptr()),
        del_port=dv("del_port", [T_opaque_port_ptr()]),
        new_chan=d("new_chan", [T_opaque_port_ptr()], T_opaque_chan_ptr()),
        flush_chan=dv("flush_chan", [T_opaque_chan_ptr()]),
        del_chan=dv("del_chan", [T_opaque_chan_ptr()]),
        clone_chan=d("clone_chan", [T_taskptr(tn), T_opaque_chan_ptr()],
                     T_opaque_chan_ptr()),
        _yield=dv("yield", empty_vec),
        sleep=dv("sleep", [T_size_t()]),
        _join=dv("join", [T_taskptr(tn)]),
        send=dv("send", [T_opaque_chan_ptr(), T_ptr(T_i8())]),
        recv=dv("recv", [T_ptr(T_ptr(T_i8())), T_opaque_port_ptr()]),
        _fail=dv("fail", [T_ptr(T_i8()), T_ptr(T_i8()), T_size_t()]),
        kill=dv("kill", [T_taskptr(tn)]),
        exit=dv("exit", empty_vec),
        malloc=d("malloc", [T_size_t(), T_ptr(T_tydesc(tn))],
                               T_ptr(T_i8())),
        free=dv("free", [T_ptr(T_i8()), T_int()]),
        mark=d("mark", [T_ptr(T_i8())], T_int()),
        new_str=d("new_str", [T_ptr(T_i8()), T_size_t()], T_ptr(T_str())),
        new_vec=d("new_vec", [T_size_t(), T_ptr(T_tydesc(tn))],
                                 T_opaque_vec_ptr()),
        vec_grow=d("vec_grow", [T_opaque_vec_ptr(), T_size_t(),
                                   T_ptr(T_int()), T_ptr(T_tydesc(tn))],
                   T_opaque_vec_ptr()),
        require_rust_sym=d("require_rust_sym",
                           [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                               T_size_t(), T_ptr(T_i8()),
                               T_ptr(T_ptr(T_i8()))],
                           T_int()),
        require_c_sym=d("require_c_sym",
                        [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                            T_ptr(T_i8()), T_ptr(T_i8())],
                        T_int()),
        get_type_desc=d("get_type_desc",
                        [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                            T_size_t(), T_ptr(T_ptr(T_tydesc(tn)))],
                        T_ptr(T_tydesc(tn))),
        new_task=d("new_task", [T_ptr(T_str())], T_taskptr(tn)),
        start_task=d("start_task", [T_taskptr(tn),
                                    T_int(), T_int(), T_size_t()],
                     T_taskptr(tn)),
        new_thread=d("new_thread", [T_ptr(T_i8())], T_taskptr(tn)),
        start_thread=d("start_thread", [T_taskptr(tn), T_int(), T_int(),
                                           T_int(), T_size_t()],
                       T_taskptr(tn))
    );
}

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Using the right mode...<commit_after>import middle::trans;

import trans::decl_cdecl_fn;
import trans::type_names;
import trans::ModuleRef;
import trans::TypeRef;
import trans::ValueRef;

import trans::T_crate;
import trans::T_f32;
import trans::T_f64;
import trans::T_fn;
import trans::T_i8;
import trans::T_i32;
import trans::T_int;
import trans::T_opaque_chan_ptr;
import trans::T_opaque_port_ptr;
import trans::T_opaque_vec_ptr;
import trans::T_ptr;
import trans::T_size_t;
import trans::T_str;
import trans::T_taskptr;
import trans::T_tydesc;
import trans::T_void;

type upcalls = rec(
    ValueRef grow_task,
    ValueRef log_int,
    ValueRef log_float,
    ValueRef log_double,
    ValueRef log_str,
    ValueRef trace_word,
    ValueRef trace_str,
    ValueRef new_port,
    ValueRef del_port,
    ValueRef new_chan,
    ValueRef flush_chan,
    ValueRef del_chan,
    ValueRef clone_chan,
    ValueRef _yield,
    ValueRef sleep,
    ValueRef _join,
    ValueRef send,
    ValueRef recv,
    ValueRef _fail,
    ValueRef kill,
    ValueRef exit,
    ValueRef malloc,
    ValueRef free,
    ValueRef mark,
    ValueRef new_str,
    ValueRef new_vec,
    ValueRef vec_grow,
    ValueRef require_rust_sym,
    ValueRef require_c_sym,
    ValueRef get_type_desc,
    ValueRef new_task,
    ValueRef start_task,
    ValueRef new_thread,
    ValueRef start_thread
);

fn declare_upcalls(type_names tn, ModuleRef llmod) -> @upcalls {
    fn decl(type_names tn, ModuleRef llmod, str name, vec[TypeRef] tys,
            TypeRef rv) -> ValueRef {
        let vec[TypeRef] arg_tys = [T_taskptr(tn)];
        for (TypeRef t in tys) { arg_tys += [t]; }
        auto fn_ty = T_fn(arg_tys, rv);
        ret trans::decl_cdecl_fn(llmod, "upcall_" + name, fn_ty);
    }

    auto dv = bind decl(tn, llmod, _, _, T_void());
    auto d = bind decl(tn, llmod, _, _, _);

    // FIXME: Sigh:.. remove this when I fix the typechecker pushdown.
    // --pcwalton
    let vec[TypeRef] empty_vec = [];

    ret @rec(
        grow_task=dv("grow_task", [T_size_t()]),
        log_int=dv("log_int", [T_i32(), T_i32()]),
        log_float=dv("log_float", [T_i32(), T_f32()]),
        log_double=dv("log_double", [T_i32(), T_ptr(T_f64())]),
        log_str=dv("log_str", [T_i32(), T_ptr(T_str())]),
        trace_word=dv("trace_word", [T_int()]),
        trace_str=dv("trace_str", [T_ptr(T_i8())]),
        new_port=d("new_port", [T_size_t()], T_opaque_port_ptr()),
        del_port=dv("del_port", [T_opaque_port_ptr()]),
        new_chan=d("new_chan", [T_opaque_port_ptr()], T_opaque_chan_ptr()),
        flush_chan=dv("flush_chan", [T_opaque_chan_ptr()]),
        del_chan=dv("del_chan", [T_opaque_chan_ptr()]),
        clone_chan=d("clone_chan", [T_taskptr(tn), T_opaque_chan_ptr()],
                     T_opaque_chan_ptr()),
        _yield=dv("yield", empty_vec),
        sleep=dv("sleep", [T_size_t()]),
        _join=dv("join", [T_taskptr(tn)]),
        send=dv("send", [T_opaque_chan_ptr(), T_ptr(T_i8())]),
        recv=dv("recv", [T_ptr(T_ptr(T_i8())), T_opaque_port_ptr()]),
        _fail=dv("fail", [T_ptr(T_i8()), T_ptr(T_i8()), T_size_t()]),
        kill=dv("kill", [T_taskptr(tn)]),
        exit=dv("exit", empty_vec),
        malloc=d("malloc", [T_size_t(), T_ptr(T_tydesc(tn))],
                               T_ptr(T_i8())),
        free=dv("free", [T_ptr(T_i8()), T_int()]),
        mark=d("mark", [T_ptr(T_i8())], T_int()),
        new_str=d("new_str", [T_ptr(T_i8()), T_size_t()], T_ptr(T_str())),
        new_vec=d("new_vec", [T_size_t(), T_ptr(T_tydesc(tn))],
                                 T_opaque_vec_ptr()),
        vec_grow=d("vec_grow", [T_opaque_vec_ptr(), T_size_t(),
                                   T_ptr(T_int()), T_ptr(T_tydesc(tn))],
                   T_opaque_vec_ptr()),
        require_rust_sym=d("require_rust_sym",
                           [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                               T_size_t(), T_ptr(T_i8()),
                               T_ptr(T_ptr(T_i8()))],
                           T_int()),
        require_c_sym=d("require_c_sym",
                        [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                            T_ptr(T_i8()), T_ptr(T_i8())],
                        T_int()),
        get_type_desc=d("get_type_desc",
                        [T_ptr(T_crate(tn)), T_size_t(), T_size_t(),
                            T_size_t(), T_ptr(T_ptr(T_tydesc(tn)))],
                        T_ptr(T_tydesc(tn))),
        new_task=d("new_task", [T_ptr(T_str())], T_taskptr(tn)),
        start_task=d("start_task", [T_taskptr(tn),
                                    T_int(), T_int(), T_size_t()],
                     T_taskptr(tn)),
        new_thread=d("new_thread", [T_ptr(T_i8())], T_taskptr(tn)),
        start_thread=d("start_thread", [T_taskptr(tn), T_int(), T_int(),
                                           T_int(), T_size_t()],
                       T_taskptr(tn))
    );
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::cell::RefCell;
use std::error;
use std::fmt;
use std::io;
use std::rc::Rc;
use std::string;

use serde_json::Value;
use super::*;
use template::Template;

const VOID_ELEMENTS: [&'static str; 13] = ["area", "base", "br", "col", "hr", "img", "input",
                                           "link", "meta", "command", "keygen", "source",
                                           "!DOCTYPE"];
pub type CodegenResult = Result<String, CodegenError>;

macro_rules! html_try {
    ($result:expr) => {
        if let Err(io_error) = $result {
            return Err(CodegenError::IoError(io_error));
        }
    }
}

pub struct Codegen {
    elements: Vec<AstResult>,
    variables: BTreeMap<String, Value>,
    parent: Rc<RefCell<Template>>,
}

impl Codegen {
    pub fn new(ast: Vec<AstResult>,
               json: BTreeMap<String, Value>,
               parent: Rc<RefCell<Template>>)
               -> Self {
        Codegen {
            elements: ast,
            variables: json,
            parent: parent,
        }
    }

    pub fn render_component(ast: Vec<AstResult>,
                            json: BTreeMap<String, Value>,
                            parent: Rc<RefCell<Template>>)
                            -> CodegenResult {
        Codegen {
            elements: ast,
            variables: json,
            parent: parent,
        }
        .generate_html()
    }

    pub fn generate_html(&mut self) -> CodegenResult {
        let mut html = String::new();

        for element in &self.elements {
            match self.render(element) {
                Ok(string) => html.push_str(&*string),
                Err(error) => return Err(error),
            }
        }
        Ok(html)
    }

    pub fn call_component(component: &Component,
                          arg_map: Option<BTreeMap<String, Value>>,
                          parent: &Rc<RefCell<Template>>)
                          -> CodegenResult {
        let mut codegen = if let Some(arg_map) = arg_map {
            Codegen {
                elements: component.ast(),
                variables: arg_map,
                parent: parent.clone(),
            }
        } else {
            Codegen {
                elements: component.ast(),
                variables: BTreeMap::new(),
                parent: parent.clone(),
            }
        };
        codegen.generate_html()
    }

    fn generate_from_component(&self, component_call: ComponentCall) -> CodegenResult {
        let parent = self.parent.borrow();
        if let Some(component) = parent.get_component(component_call.name()) {
            let args = component.args();
            let arg_values = component_call.values();
            let mut arg_map = BTreeMap::new();
            if args.len() == arg_values.len() {
                let zipped = args.iter().zip(arg_values.iter());

                for (ref arg, ref value) in zipped {

                    match **arg {
                        ArgKey::Json(ref arg_name) => {
                            if let ArgKey::Json(ref arg_value) = **value {
                                let value = match self.get_variable(arg_value) {
                                    Ok(value) => value,
                                    Err(error) => return Err(error),
                                };
                                arg_map.insert(arg_name.clone(), value);
                            }
                        }
                        ArgKey::Comp(ref name) => {
                            return Err(CodegenError::CompPassedToComp(name.clone()))
                        }
                    }
                }
                Codegen::render_component(component.ast(), arg_map, self.parent.clone())
            } else {
                Err(CodegenError::WrongNumberOfArguments(args.len(), arg_values.len()))
            }
        } else {
            Err(CodegenError::NoSuchComponent(String::from(component_call.name())))
        }
    }

    fn render_element(&self, element: &Element) -> CodegenResult {
        use std::io::Write;
        let mut html = Vec::new();
        let tag = &**element.tag();
        html_try!(write!(&mut html, "<{}", tag));

        if !element.classes().is_empty() {

            html_try!(write!(&mut html, " class=\""));
            let mut classes_iter = element.classes().iter();
            html_try!(write!(&mut html, "{}", classes_iter.next().unwrap()));

            for class in classes_iter {
                if !class.is_empty() {
                    html_try!(write!(&mut html, " {}", &*class));
                }
            }
            html_try!(write!(&mut html, "\""));
        }

        if !element.attributes().is_empty() {
            for (key, value) in element.attributes() {
                if !key.is_empty() {
                    if !value.is_empty() {
                        html_try!(write!(&mut html, " {}=\"{}\"", key, value));
                    } else {
                        html_try!(write!(&mut html, " {}", key));
                    }
                }
            }
        }

        html_try!(write!(&mut html, ">"));

        for void in &VOID_ELEMENTS {
            if void == &tag {
                return match String::from_utf8(html) {
                    Ok(html) => Ok(html),
                    Err(error) => Err(CodegenError::FromUtf8Error(error)),
                };
            }
        }

        if let Some(ref resource) = *element.resource() {
            match self.generate_from_component(resource.clone()) {
                Ok(rendered) => html_try!(write!(&mut html, "{}", rendered)),
                Err(err) => return Err(err),
            }

        } else {
            for child in element.children() {
                match self.render(child) {
                    Ok(rendered) => {
                        html_try!(write!(&mut html, "{}", rendered));
                    }
                    Err(error) => return Err(error),
                }
            }
        }

        html_try!(write!(&mut html, "</{}>", tag));

        match String::from_utf8(html) {
            Ok(html) => Ok(html),
            Err(error) => Err(CodegenError::FromUtf8Error(error)),
        }
    }

    fn render_function(&self, function: &FunctionCall) -> CodegenResult {
        let mut arguments: BTreeMap<String, ArgValue> = BTreeMap::new();

        for (key, value) in function.args().clone() {

            match value {
                ArgKey::Json(id) => {
                    let real_value = match self.get_variable(&id) {
                        Ok(value) => value,
                        Err(error) => return Err(error),
                    };
                    arguments.insert(key, ArgValue::Json(Some(real_value)));
                }
                ArgKey::Comp(id) => {
                    let parent = self.parent.borrow();
                    let real_value = match parent.get_component(&id) {
                        Some(value) => Some(value.clone()),
                        None => None,
                    };
                    arguments.insert(key, ArgValue::Comp(real_value));
                }
            }
        }

        let parent = self.parent.borrow();
        if let Some(fun) = parent.get_function(function.identifier()) {
            match fun(arguments, &self.parent) {
                Ok(string) => Ok(string),
                Err(error) => Err(CodegenError::FunctionError(error)),
            }
        } else {
            Err(CodegenError::NoSuchFunction(String::from(function.identifier())))
        }
    }

    fn get_variable(&self, name: &String) -> Result<Value, CodegenError> {
        let segments: Vec<&str> = name.split('.').collect();

        if segments.len() == 1 {
            match self.variables.get(name) {
                Some(value) => Ok(value.clone()),
                None => Ok(Value::String(String::new())),
            }
        } else {
            match Value::Object(self.variables.clone()).find_path(&segments) {
                Some(value) => return Ok(value.clone()),
                _ => return Err(CodegenError::NotAnObjectOrNull(String::from(name.clone()))),

            }
        }
    }

    fn render(&self, token: &AstResult) -> CodegenResult {
        use super::Token::*;
        match *token {
            Ok(Html(ref element)) => self.render_element(element),
            Ok(Text(ref text)) => Ok(text.clone()),
            Ok(Variable(ref variable)) => {
                match self.get_variable(variable) {
                    Ok(value) => Ok(value_to_string(&value)),
                    Err(error) => Err(error),
                }
            }
            Ok(CompCall(ref component_call)) => {
                self.generate_from_component(component_call.clone())
            }
            Ok(Function(ref function)) => self.render_function(function),
            Err(ref error) => Err(CodegenError::AstError(error.clone())),
        }
    }
}

#[derive(Debug)]
/// Errors generated during code generation
pub enum CodegenError {
    /// Errors during AST parsing.
    AstError(AstError),
    /// Component passed to a component.
    CompPassedToComp(String),
    /// Error converting to a UTF8 String.
    FromUtf8Error(string::FromUtf8Error),
    /// An error generated by a Polly Function.
    FunctionError(String),
    /// Error generated from writing to a buffer.
    IoError(io::Error),
    /// No such component in the template.
    NoSuchComponent(String),
    /// No such function in the template.
    NoSuchFunction(String),
    /// JSON wasn't an object, or it was Null.
    NotAnObjectOrNull(String),
    /// Wrong number of arguments passed in.
    WrongNumberOfArguments(usize, usize),
}

impl error::Error for CodegenError {
    fn description(&self) -> &str {
        use self::CodegenError::*;

        match *self {
            AstError(ref error) => error.description(),
            CompPassedToComp(_) => {
                "Currently you cannot cannot pass a component to another component: "
            }
            FromUtf8Error(ref error) => error.description(),
            FunctionError(_) => "Function produced error: ",
            IoError(ref error) => error.description(),
            NoSuchComponent(_) => "Component called doesn't exist in the current template: ",
            NoSuchFunction(_) => "Function called doesn't exist in the current template: ",
            NotAnObjectOrNull(_) => "JSON passed in wasn't an object, or was null: ",
            WrongNumberOfArguments(_, _) => "Incorrect number of arguments passed in: ",
        }
    }
}

impl fmt::Display for CodegenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CodegenError::*;
        use std::error::Error;

        let msg = match *self {
            AstError(ref error) => format!("{}", error),
            CompPassedToComp(ref name) => format!("{} NAME: {}", self.description(), name),
            FromUtf8Error(ref error) => format!("{}", error),
            FunctionError(ref error) => format!("{} ERROR: {}", self.description(), error),
            IoError(ref error) => format!("{}", error),
            NoSuchComponent(ref name) | NoSuchFunction(ref name) | NotAnObjectOrNull(ref name) => {
                format!("{} NAME: {}", self.description(), name)
            }
            WrongNumberOfArguments(expected, actual) => {
                format!("{} EXPECTED: {} ACTUAL: {}",
                        self.description(),
                        expected,
                        actual)
            }
        };

        write!(f, "{}", msg)
    }
}

fn value_to_string(value: &Value) -> String {
    use serde_json::Value;
    match *value {
        Value::Null => String::new(),
        Value::Bool(value) => value.to_string(),
        Value::I64(value) => value.to_string(),
        Value::U64(value) => value.to_string(),
        Value::F64(value) => value.to_string(),
        Value::String(ref string) => string.clone(),
        Value::Array(ref vector) => {
            let mut concated_string = String::new();
            for value in vector {
                concated_string.push_str(&*value_to_string(value));
            }
            concated_string
        }
        Value::Object(ref object) => {
            let mut concated_string = String::new();
            for ref value in object.values() {
                concated_string.push_str(&*value_to_string(*value));
            }
            concated_string
        }
    }
}
<commit_msg>changed format! to to_string<commit_after>use std::collections::BTreeMap;
use std::cell::RefCell;
use std::error;
use std::fmt;
use std::io;
use std::rc::Rc;
use std::string;

use serde_json::Value;
use super::*;
use template::Template;

const VOID_ELEMENTS: [&'static str; 13] = ["area", "base", "br", "col", "hr", "img", "input",
                                           "link", "meta", "command", "keygen", "source",
                                           "!DOCTYPE"];
pub type CodegenResult = Result<String, CodegenError>;

macro_rules! html_try {
    ($result:expr) => {
        if let Err(io_error) = $result {
            return Err(CodegenError::IoError(io_error));
        }
    }
}

pub struct Codegen {
    elements: Vec<AstResult>,
    variables: BTreeMap<String, Value>,
    parent: Rc<RefCell<Template>>,
}

impl Codegen {
    pub fn new(ast: Vec<AstResult>,
               json: BTreeMap<String, Value>,
               parent: Rc<RefCell<Template>>)
               -> Self {
        Codegen {
            elements: ast,
            variables: json,
            parent: parent,
        }
    }

    pub fn render_component(ast: Vec<AstResult>,
                            json: BTreeMap<String, Value>,
                            parent: Rc<RefCell<Template>>)
                            -> CodegenResult {
        Codegen {
            elements: ast,
            variables: json,
            parent: parent,
        }
        .generate_html()
    }

    pub fn generate_html(&mut self) -> CodegenResult {
        let mut html = String::new();

        for element in &self.elements {
            match self.render(element) {
                Ok(string) => html.push_str(&*string),
                Err(error) => return Err(error),
            }
        }
        Ok(html)
    }

    pub fn call_component(component: &Component,
                          arg_map: Option<BTreeMap<String, Value>>,
                          parent: &Rc<RefCell<Template>>)
                          -> CodegenResult {
        let mut codegen = if let Some(arg_map) = arg_map {
            Codegen {
                elements: component.ast(),
                variables: arg_map,
                parent: parent.clone(),
            }
        } else {
            Codegen {
                elements: component.ast(),
                variables: BTreeMap::new(),
                parent: parent.clone(),
            }
        };
        codegen.generate_html()
    }

    fn generate_from_component(&self, component_call: ComponentCall) -> CodegenResult {
        let parent = self.parent.borrow();
        if let Some(component) = parent.get_component(component_call.name()) {
            let args = component.args();
            let arg_values = component_call.values();
            let mut arg_map = BTreeMap::new();
            if args.len() == arg_values.len() {
                let zipped = args.iter().zip(arg_values.iter());

                for (ref arg, ref value) in zipped {

                    match **arg {
                        ArgKey::Json(ref arg_name) => {
                            if let ArgKey::Json(ref arg_value) = **value {
                                let value = match self.get_variable(arg_value) {
                                    Ok(value) => value,
                                    Err(error) => return Err(error),
                                };
                                arg_map.insert(arg_name.clone(), value);
                            }
                        }
                        ArgKey::Comp(ref name) => {
                            return Err(CodegenError::CompPassedToComp(name.clone()))
                        }
                    }
                }
                Codegen::render_component(component.ast(), arg_map, self.parent.clone())
            } else {
                Err(CodegenError::WrongNumberOfArguments(args.len(), arg_values.len()))
            }
        } else {
            Err(CodegenError::NoSuchComponent(String::from(component_call.name())))
        }
    }

    fn render_element(&self, element: &Element) -> CodegenResult {
        use std::io::Write;
        let mut html = Vec::new();
        let tag = &**element.tag();
        html_try!(write!(&mut html, "<{}", tag));

        if !element.classes().is_empty() {

            html_try!(write!(&mut html, " class=\""));
            let mut classes_iter = element.classes().iter();
            html_try!(write!(&mut html, "{}", classes_iter.next().unwrap()));

            for class in classes_iter {
                if !class.is_empty() {
                    html_try!(write!(&mut html, " {}", &*class));
                }
            }
            html_try!(write!(&mut html, "\""));
        }

        if !element.attributes().is_empty() {
            for (key, value) in element.attributes() {
                if !key.is_empty() {
                    if !value.is_empty() {
                        html_try!(write!(&mut html, " {}=\"{}\"", key, value));
                    } else {
                        html_try!(write!(&mut html, " {}", key));
                    }
                }
            }
        }

        html_try!(write!(&mut html, ">"));

        for void in &VOID_ELEMENTS {
            if void == &tag {
                return match String::from_utf8(html) {
                    Ok(html) => Ok(html),
                    Err(error) => Err(CodegenError::FromUtf8Error(error)),
                };
            }
        }

        if let Some(ref resource) = *element.resource() {
            match self.generate_from_component(resource.clone()) {
                Ok(rendered) => html_try!(write!(&mut html, "{}", rendered)),
                Err(err) => return Err(err),
            }

        } else {
            for child in element.children() {
                match self.render(child) {
                    Ok(rendered) => {
                        html_try!(write!(&mut html, "{}", rendered));
                    }
                    Err(error) => return Err(error),
                }
            }
        }

        html_try!(write!(&mut html, "</{}>", tag));

        match String::from_utf8(html) {
            Ok(html) => Ok(html),
            Err(error) => Err(CodegenError::FromUtf8Error(error)),
        }
    }

    fn render_function(&self, function: &FunctionCall) -> CodegenResult {
        let mut arguments: BTreeMap<String, ArgValue> = BTreeMap::new();

        for (key, value) in function.args().clone() {

            match value {
                ArgKey::Json(id) => {
                    let real_value = match self.get_variable(&id) {
                        Ok(value) => value,
                        Err(error) => return Err(error),
                    };
                    arguments.insert(key, ArgValue::Json(Some(real_value)));
                }
                ArgKey::Comp(id) => {
                    let parent = self.parent.borrow();
                    let real_value = match parent.get_component(&id) {
                        Some(value) => Some(value.clone()),
                        None => None,
                    };
                    arguments.insert(key, ArgValue::Comp(real_value));
                }
            }
        }

        let parent = self.parent.borrow();
        if let Some(fun) = parent.get_function(function.identifier()) {
            match fun(arguments, &self.parent) {
                Ok(string) => Ok(string),
                Err(error) => Err(CodegenError::FunctionError(error)),
            }
        } else {
            Err(CodegenError::NoSuchFunction(String::from(function.identifier())))
        }
    }

    fn get_variable(&self, name: &String) -> Result<Value, CodegenError> {
        let segments: Vec<&str> = name.split('.').collect();

        if segments.len() == 1 {
            match self.variables.get(name) {
                Some(value) => Ok(value.clone()),
                None => Ok(Value::String(String::new())),
            }
        } else {
            match Value::Object(self.variables.clone()).find_path(&segments) {
                Some(value) => return Ok(value.clone()),
                _ => return Err(CodegenError::NotAnObjectOrNull(String::from(name.clone()))),

            }
        }
    }

    fn render(&self, token: &AstResult) -> CodegenResult {
        use super::Token::*;
        match *token {
            Ok(Html(ref element)) => self.render_element(element),
            Ok(Text(ref text)) => Ok(text.clone()),
            Ok(Variable(ref variable)) => {
                match self.get_variable(variable) {
                    Ok(value) => Ok(value_to_string(&value)),
                    Err(error) => Err(error),
                }
            }
            Ok(CompCall(ref component_call)) => {
                self.generate_from_component(component_call.clone())
            }
            Ok(Function(ref function)) => self.render_function(function),
            Err(ref error) => Err(CodegenError::AstError(error.clone())),
        }
    }
}

#[derive(Debug)]
/// Errors generated during code generation
pub enum CodegenError {
    /// Errors during AST parsing.
    AstError(AstError),
    /// Component passed to a component.
    CompPassedToComp(String),
    /// Error converting to a UTF8 String.
    FromUtf8Error(string::FromUtf8Error),
    /// An error generated by a Polly Function.
    FunctionError(String),
    /// Error generated from writing to a buffer.
    IoError(io::Error),
    /// No such component in the template.
    NoSuchComponent(String),
    /// No such function in the template.
    NoSuchFunction(String),
    /// JSON wasn't an object, or it was Null.
    NotAnObjectOrNull(String),
    /// Wrong number of arguments passed in.
    WrongNumberOfArguments(usize, usize),
}

impl error::Error for CodegenError {
    fn description(&self) -> &str {
        use self::CodegenError::*;

        match *self {
            AstError(ref error) => error.description(),
            CompPassedToComp(_) => {
                "Currently you cannot cannot pass a component to another component: "
            }
            FromUtf8Error(ref error) => error.description(),
            FunctionError(_) => "Function produced error: ",
            IoError(ref error) => error.description(),
            NoSuchComponent(_) => "Component called doesn't exist in the current template: ",
            NoSuchFunction(_) => "Function called doesn't exist in the current template: ",
            NotAnObjectOrNull(_) => "JSON passed in wasn't an object, or was null: ",
            WrongNumberOfArguments(_, _) => "Incorrect number of arguments passed in: ",
        }
    }
}

impl fmt::Display for CodegenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CodegenError::*;
        use std::error::Error;

        let msg = match *self {
            AstError(ref error) => error.to_string(),
            CompPassedToComp(ref name) => format!("{} NAME: {}", self.description(), name),
            FromUtf8Error(ref error) => error.to_string(),
            FunctionError(ref error) => format!("{} ERROR: {}", self.description(), error),
            IoError(ref error) => error.to_string(),
            NoSuchComponent(ref name) | NoSuchFunction(ref name) | NotAnObjectOrNull(ref name) => {
                format!("{} NAME: {}", self.description(), name)
            }
            WrongNumberOfArguments(expected, actual) => {
                format!("{} EXPECTED: {} ACTUAL: {}",
                        self.description(),
                        expected,
                        actual)
            }
        };

        write!(f, "{}", msg)
    }
}

fn value_to_string(value: &Value) -> String {
    use serde_json::Value;
    match *value {
        Value::Null => String::new(),
        Value::Bool(value) => value.to_string(),
        Value::I64(value) => value.to_string(),
        Value::U64(value) => value.to_string(),
        Value::F64(value) => value.to_string(),
        Value::String(ref string) => string.clone(),
        Value::Array(ref vector) => {
            let mut concated_string = String::new();
            for value in vector {
                concated_string.push_str(&*value_to_string(value));
            }
            concated_string
        }
        Value::Object(ref object) => {
            let mut concated_string = String::new();
            for ref value in object.values() {
                concated_string.push_str(&*value_to_string(*value));
            }
            concated_string
        }
    }
}
<|endoftext|>


<commit_before>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use std::ptr;

    use log::info;

    use winapi::shared::minwindef::*;
    use winapi::um::handleapi::*;
    use winapi::um::jobapi2::*;
    use winapi::um::processthreadsapi::*;
    use winapi::um::winnt::HANDLE;
    use winapi::um::winnt::*;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // On normal exits (not ctrl-c), we don't want to kill any child
            // processes. The destructor here configures our job object to
            // **not** kill everything on close, then closes the job object.
            unsafe {
                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<commit_msg>Auto merge of #8389 - hbina:spelling, r=ehuss<commit_after>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cargo). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use std::ptr;

    use log::info;

    use winapi::shared::minwindef::*;
    use winapi::um::handleapi::*;
    use winapi::um::jobapi2::*;
    use winapi::um::processthreadsapi::*;
    use winapi::um::winnt::HANDLE;
    use winapi::um::winnt::*;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // On normal exits (not ctrl-c), we don't want to kill any child
            // processes. The destructor here configures our job object to
            // **not** kill everything on close, then closes the job object.
            unsafe {
                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<|endoftext|>