<commit_before>use std::fmt;
use std::str::FromStr;

use super::rfc5322::{Rfc5322Parser, MIME_LINE_LENGTH};
use super::header::{FromHeader, ToFoldedHeader};
use super::results::{ParsingResult,ParsingError};

/// Represents an RFC 5322 Address
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub enum Address {
    /// A "regular" email address
    Mailbox(Mailbox),
    /// A named group of mailboxes
    Group(String, Vec<Mailbox>),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    #[unstable]
    pub fn new_mailbox(address: String) -> Address {
        Address::Mailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    #[unstable]
    pub fn new_mailbox_with_name(name: String, address: String) -> Address {
        Address::Mailbox(Mailbox::new_with_name(name, address))
    }

    /// Shortcut function to make a new Group with a collection of mailboxes
    #[unstable]
    pub fn new_group(name: String, mailboxes: Vec<Mailbox>) -> Address {
        Address::Group(name, mailboxes)
    }
}

impl fmt::Display for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Address::Mailbox(ref mbox) => mbox.fmt(fmt),
            Address::Group(ref name, ref mboxes) => {
                let mut mailbox_list = String::new();
                for mbox in mboxes.iter() {
                    if mailbox_list.len() > 0 {
                        // Insert the separator if there's already things in this list
                        mailbox_list.push_str(", ");
                    }
                    mailbox_list.push_str(mbox.to_string().as_slice());
                }
                write!(fmt, "{}: {};", name, mailbox_list)
            }
        }
    }
}

/// Represents an RFC 5322 mailbox
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    #[stable]
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    #[stable]
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Display for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    type Err = ParsingError;

    fn from_str(s: &str) -> ParsingResult<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> ParsingResult<Vec<Address>> {
        Ok(AddressParser::new(value.as_slice()).parse_address_list())
    }
}

impl ToFoldedHeader for Vec<Address> {
    fn to_folded_header(start_pos: usize, value: Vec<Address>) -> ParsingResult<String> {
        let mut header = String::new();

        let mut line_len = start_pos;

        for addr in value.iter() {
            let addr_str = format!("{}, ", addr);

            if line_len + addr_str.len() > MIME_LINE_LENGTH {
                // Adding this would cause a wrap, so wrap before!
                header.push_str("\r\n\t");
                line_len = 0;
            }
            line_len += addr_str.len();
            header.push_str(addr_str.as_slice());
        }

        // Clear up the final ", "
        let real_len = header.len() - 2;
        header.truncate(real_len);

        Ok(header)
    }
}

#[stable]
pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    #[stable]
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    #[stable]
    pub fn parse_address_list(&mut self) -> Vec<Address> {
        let mut result = Vec::new();

        while !self.p.eof() {
            self.p.push_position();

            match self.parse_group() {
                Ok(x) => result.push(x),
                Err(_) => {
                    // If we failed to parse as group, try again as mailbox
                    self.p.pop_position();
                    match self.parse_mailbox() {
                        Ok(x) => result.push(Address::Mailbox(x)),
                        Err(_) => ()
                    };
                }
            };

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == ',' {
                // Clear the separator
                self.p.consume_char();
            }
        }

        result
    }

    #[stable]
    pub fn parse_group(&mut self) -> ParsingResult<Address> {
        let name = match self.p.consume_phrase(false) {
            Some(x) => x,
            None => return Err(ParsingError::new("Couldn't find group name.".to_string()))
        };

        if !self.p.eof() && self.p.peek() != ':' {
            return Err(ParsingError::new(format!("Expected ':', found {}.",
                                                 self.p.peek())));
        };

        self.p.consume_char();
        let mut mailboxes = Vec::new();

        while !self.p.eof() && self.p.peek() != ';' {
            mailboxes.push(try!(self.parse_mailbox()));

            if !self.p.eof() && self.p.peek() == ',' {
                self.p.consume_char();
            }
        };

        Ok(Address::Group(name, mailboxes))
    }

    #[stable]
    pub fn parse_mailbox(&mut self) -> ParsingResult<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        match self.parse_name_addr() {
            Ok(result) => Ok(result),
            Err(_) => {
                // Revert back to our original position to try to parse an addr-spec
                self.p.pop_position();
                Ok(Mailbox::new(try!(self.parse_addr_spec())))
            }
        }
    }

    fn parse_name_addr(&mut self) -> ParsingResult<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();
        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address header.".to_string()));
        };

        // Find angle-addr
        if self.p.peek() != '<' {
            return Err(ParsingError::new("Could only find display name while parsing address header.".to_string()));
        } else {
            self.p.consume_char();
        };

        let addr = try!(self.parse_addr_spec());
        if self.p.consume_char() != '>' {
            // Fail because we should have a closing RANGLE here (to match the opening one)
            Err(ParsingError::new("Missing '>' at end while parsing address header.".to_string()))
        } else {
            Ok(match display_name {
                Some(name) => Mailbox::new_with_name(name, addr),
                None => Mailbox::new(addr)
            })
        }
    }

    fn parse_addr_spec(&mut self) -> ParsingResult<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = match self.p.consume_phrase(true) {
            Some(x) => x,
            None => return Err(ParsingError::new("Couldn't find local part while parsing address.".to_string()))
        };

        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address.".to_string()));
        };

        let at_char = self.p.consume_char();
        if at_char != '@' {
            return Err(ParsingError::new(format!("Expected @, found {} instead.", at_char)));
        };

        let domain = try!(self.parse_domain());
        Ok(format!("{}@{}", local_part, domain))
    }

    fn parse_domain(&mut self) -> ParsingResult<String> {
        // TODO: support domain-literal
        match self.p.consume_atom(true) {
            Some(x) => Ok(x),
            None => Err(ParsingError::new("Failed to parse domain.".to_string()))
        }
    }
}

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

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = "\"Joe Blogs\" <joe@example.org>".parse::<Mailbox>().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!("Not an address".parse::<Mailbox>().is_err());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_group_to_string() {
        let addr = Address::new_group("undisclosed recipients".to_string(), vec![]);
        assert_eq!(addr.to_string(), "undisclosed recipients: ;".to_string());

        let addr = Address::new_group("group test".to_string(), vec![
            Mailbox::new("joe@example.org".to_string()),
            Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
        assert_eq!(addr.to_string(), "group test: <joe@example.org>, \"John Doe\" <john@example.org>;".to_string());
    }

    #[test]
    fn test_address_group_parsing() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>,john@example.org;");
        let addr = parser.parse_group().unwrap();
        match addr {
            Address::Group(name, mboxes) => {
                assert_eq!(name, "A Group".to_string());
                assert_eq!(mboxes, vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new("john@example.org".to_string()),
                ]);
            },
            _ => assert!(false),
        }
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list(), vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_address_list_parsing_groups() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>; <third@example.org>, <fourth@example.org>");
        assert_eq!(parser.parse_address_list(), vec![
            Address::new_group("A Group".to_string(), vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
            ]),
            Address::new_mailbox("third@example.org".to_string()),
            Address::new_mailbox("fourth@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_to_header_generation() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ];

        let header = Header::new_with_value("From:".to_string(), addresses).unwrap();
        assert_eq!(header.get_value::<String>().unwrap(),
                   "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
    }

    #[test]
    fn test_to_header_line_wrap() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
            Address::new_mailbox_with_name("Mr Black".to_string(), "mafia_black@example.org".to_string()),
        ];

        let header = Header::new_with_value("To".to_string(), addresses).unwrap();
        assert_eq!(header.to_string().as_slice(),
                   "To: \"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>, \r\n\
                   \t\"Mr Black\" <mafia_black@example.org>");
    }
}
<commit_msg>Better error messages for parse_address_list<commit_after>use std::fmt;
use std::str::FromStr;

use super::rfc5322::{Rfc5322Parser, MIME_LINE_LENGTH};
use super::header::{FromHeader, ToFoldedHeader};
use super::results::{ParsingResult,ParsingError};

/// Represents an RFC 5322 Address
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub enum Address {
    /// A "regular" email address
    Mailbox(Mailbox),
    /// A named group of mailboxes
    Group(String, Vec<Mailbox>),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    #[unstable]
    pub fn new_mailbox(address: String) -> Address {
        Address::Mailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    #[unstable]
    pub fn new_mailbox_with_name(name: String, address: String) -> Address {
        Address::Mailbox(Mailbox::new_with_name(name, address))
    }

    /// Shortcut function to make a new Group with a collection of mailboxes
    #[unstable]
    pub fn new_group(name: String, mailboxes: Vec<Mailbox>) -> Address {
        Address::Group(name, mailboxes)
    }
}

impl fmt::Display for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Address::Mailbox(ref mbox) => mbox.fmt(fmt),
            Address::Group(ref name, ref mboxes) => {
                let mut mailbox_list = String::new();
                for mbox in mboxes.iter() {
                    if mailbox_list.len() > 0 {
                        // Insert the separator if there's already things in this list
                        mailbox_list.push_str(", ");
                    }
                    mailbox_list.push_str(mbox.to_string().as_slice());
                }
                write!(fmt, "{}: {};", name, mailbox_list)
            }
        }
    }
}

/// Represents an RFC 5322 mailbox
#[derive(PartialEq, Eq, Debug)]
#[stable]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    #[stable]
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    #[stable]
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Display for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    type Err = ParsingError;

    fn from_str(s: &str) -> ParsingResult<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> ParsingResult<Vec<Address>> {
        AddressParser::new(value.as_slice()).parse_address_list()
    }
}

impl ToFoldedHeader for Vec<Address> {
    fn to_folded_header(start_pos: usize, value: Vec<Address>) -> ParsingResult<String> {
        let mut header = String::new();

        let mut line_len = start_pos;

        for addr in value.iter() {
            let addr_str = format!("{}, ", addr);

            if line_len + addr_str.len() > MIME_LINE_LENGTH {
                // Adding this would cause a wrap, so wrap before!
                header.push_str("\r\n\t");
                line_len = 0;
            }
            line_len += addr_str.len();
            header.push_str(addr_str.as_slice());
        }

        // Clear up the final ", "
        let real_len = header.len() - 2;
        header.truncate(real_len);

        Ok(header)
    }
}

#[stable]
pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    #[stable]
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    #[stable]
    pub fn parse_address_list(&mut self) -> ParsingResult<Vec<Address>> {
        let mut result = Vec::new();

        let mut i = 0;
        while !self.p.eof() {
            if i > 100 {
                panic!("DAM");
            };
            i += 1;
            self.p.push_position();

            match self.parse_group() {
                Ok(x) => result.push(x),
                Err(e) => {
                    // If we failed to parse as group, try again as mailbox
                    self.p.pop_position();
                    result.push(Address::Mailbox(match self.parse_mailbox() {
                        Ok(x) => x,
                        Err(e2) => return Err(ParsingError::new(
                            format!("Failed to parse as group: {}\n\
                                     Failed to parse as mailbox: {}", e, e2)
                        ))
                    }));
                }
            };

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == ',' {
                // Clear the separator
                self.p.consume_char();
            }
        }

        Ok(result)
    }

    #[stable]
    pub fn parse_group(&mut self) -> ParsingResult<Address> {
        let name = match self.p.consume_phrase(false) {
            Some(x) => x,
            None => return Err(ParsingError::new("Couldn't find group name.".to_string()))
        };

        if !self.p.eof() && self.p.peek() != ':' {
            return Err(ParsingError::new(format!("Expected ':', found {}.",
                                                 self.p.peek())));
        };

        self.p.consume_char();
        let mut mailboxes = Vec::new();

        while !self.p.eof() && self.p.peek() != ';' {
            mailboxes.push(try!(self.parse_mailbox()));

            if !self.p.eof() && self.p.peek() == ',' {
                self.p.consume_char();
            }
        };

        Ok(Address::Group(name, mailboxes))
    }

    #[stable]
    pub fn parse_mailbox(&mut self) -> ParsingResult<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        match self.parse_name_addr() {
            Ok(result) => Ok(result),
            Err(_) => {
                // Revert back to our original position to try to parse an addr-spec
                self.p.pop_position();
                Ok(Mailbox::new(try!(self.parse_addr_spec())))
            }
        }
    }

    fn parse_name_addr(&mut self) -> ParsingResult<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();
        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address header.".to_string()));
        };

        // Find angle-addr
        if self.p.peek() != '<' {
            return Err(ParsingError::new("Could only find display name while parsing address header.".to_string()));
        } else {
            self.p.consume_char();
        };

        let addr = try!(self.parse_addr_spec());
        if self.p.consume_char() != '>' {
            // Fail because we should have a closing RANGLE here (to match the opening one)
            Err(ParsingError::new("Missing '>' at end while parsing address header.".to_string()))
        } else {
            Ok(match display_name {
                Some(name) => Mailbox::new_with_name(name, addr),
                None => Mailbox::new(addr)
            })
        }
    }

    fn parse_addr_spec(&mut self) -> ParsingResult<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = match self.p.consume_phrase(true) {
            Some(x) => x,
            None => return Err(ParsingError::new(format!("Couldn't find local part while parsing address.")))
        };

        if self.p.eof() {
            return Err(ParsingError::new("Reached EOF while parsing address.".to_string()));
        };

        let at_char = self.p.consume_char();
        if at_char != '@' {
            return Err(ParsingError::new(format!("Expected @, found {} instead.", at_char)));
        };

        let domain = try!(self.parse_domain());
        Ok(format!("{}@{}", local_part, domain))
    }

    fn parse_domain(&mut self) -> ParsingResult<String> {
        // TODO: support domain-literal
        match self.p.consume_atom(true) {
            Some(x) => Ok(x),
            None => Err(ParsingError::new("Failed to parse domain.".to_string()))
        }
    }
}

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

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = "\"Joe Blogs\" <joe@example.org>".parse::<Mailbox>().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!("Not an address".parse::<Mailbox>().is_err());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_group_to_string() {
        let addr = Address::new_group("undisclosed recipients".to_string(), vec![]);
        assert_eq!(addr.to_string(), "undisclosed recipients: ;".to_string());

        let addr = Address::new_group("group test".to_string(), vec![
            Mailbox::new("joe@example.org".to_string()),
            Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
        assert_eq!(addr.to_string(), "group test: <joe@example.org>, \"John Doe\" <john@example.org>;".to_string());
    }

    #[test]
    fn test_address_group_parsing() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>,john@example.org;");
        let addr = parser.parse_group().unwrap();
        match addr {
            Address::Group(name, mboxes) => {
                assert_eq!(name, "A Group".to_string());
                assert_eq!(mboxes, vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new("john@example.org".to_string()),
                ]);
            },
            _ => assert!(false),
        }
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_address_list_parsing_groups() {
        let mut parser = AddressParser::new("A Group:\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>; <third@example.org>, <fourth@example.org>");
        assert_eq!(parser.parse_address_list().unwrap(), vec![
            Address::new_group("A Group".to_string(), vec![
                    Mailbox::new_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
                    Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
            ]),
            Address::new_mailbox("third@example.org".to_string()),
            Address::new_mailbox("fourth@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_to_header_generation() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ];

        let header = Header::new_with_value("From:".to_string(), addresses).unwrap();
        assert_eq!(header.get_value::<String>().unwrap(),
                   "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
    }

    #[test]
    fn test_to_header_line_wrap() {
        let addresses = vec![
            Address::new_mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::new_mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
            Address::new_mailbox_with_name("Mr Black".to_string(), "mafia_black@example.org".to_string()),
        ];

        let header = Header::new_with_value("To".to_string(), addresses).unwrap();
        assert_eq!(header.to_string().as_slice(),
                   "To: \"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>, \r\n\
                   \t\"Mr Black\" <mafia_black@example.org>");
    }
}
<|endoftext|>


<commit_before>//! Define trait for vectors

use std::iter::Sum;
use ndarray::{Array, NdFloat, Ix1, Ix2, LinalgScalar, ArrayBase, Data, Dimension};
use num_traits::float::Float;
use super::impls::outer::ImplOuter;

/// Norms of ndarray
pub trait Norm {
    type Output;
    /// rename of `norm_l2`
    fn norm(&self) -> Self::Output {
        self.norm_l2()
    }
    /// L-1 norm
    fn norm_l1(&self) -> Self::Output;
    /// L-2 norm
    fn norm_l2(&self) -> Self::Output;
    /// maximum norm
    fn norm_max(&self) -> Self::Output;
}

impl<A, S, D, T> Norm for ArrayBase<S, D>
    where A: LinalgScalar + Absolute<Output = T>,
          T: Float + Sum,
          S: Data<Elem = A>,
          D: Dimension
{
    type Output = T;
    fn norm_l1(&self) -> Self::Output {
        self.iter().map(|x| x.abs()).sum()
    }
    fn norm_l2(&self) -> Self::Output {
        self.iter().map(|x| x.sq_abs()).sum::<T>().sqrt()
    }
    fn norm_max(&self) -> Self::Output {
        self.iter().fold(T::zero(), |f, &val| {
            let v = val.abs();
            if f > v { f } else { v }
        })
    }
}

/// Field with norm
pub trait Absolute {
    type Output: Float;
    fn sq_abs(&self) -> Self::Output;
    fn abs(&self) -> Self::Output {
        self.sq_abs().sqrt()
    }
}

impl<A: Float> Absolute for A {
    type Output = A;
    fn sq_abs(&self) -> A {
        *self * *self
    }
    fn abs(&self) -> A {
        Float::abs(*self)
    }
}

/// Outer product
pub fn outer<A, S1, S2>(a: &ArrayBase<S1, Ix1>, b: &ArrayBase<S2, Ix1>) -> Array<A, Ix2>
    where A: NdFloat + ImplOuter,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>
{
    let m = a.len();
    let n = b.len();
    let mut ab = Array::zeros((n, m));
    ImplOuter::outer(m,
                     n,
                     a.as_slice_memory_order().unwrap(),
                     b.as_slice_memory_order().unwrap(),
                     ab.as_slice_memory_order_mut().unwrap());
    ab.reversed_axes()
}
<commit_msg>Rewrite Norm<commit_after>//! Define trait for vectors

use std::iter::Sum;
use ndarray::*;
use num_traits::Float;

use super::types::*;

/// Define norm as a metric linear space (not as a matrix)
///
/// For operator norms, see opnorm module
pub trait Norm {
    type Output;
    /// rename of `norm_l2`
    fn norm(&self) -> Self::Output {
        self.norm_l2()
    }
    /// L-1 norm
    fn norm_l1(&self) -> Self::Output;
    /// L-2 norm
    fn norm_l2(&self) -> Self::Output;
    /// maximum norm
    fn norm_max(&self) -> Self::Output;
}

impl<A, S, D, T> Norm for ArrayBase<S, D>
    where A: LinalgScalar + Absolute<Output = T>,
          T: Float + Sum,
          S: Data<Elem = A>,
          D: Dimension
{
    type Output = T;
    fn norm_l1(&self) -> Self::Output {
        self.iter().map(|x| x.abs()).sum()
    }
    fn norm_l2(&self) -> Self::Output {
        self.iter().map(|x| x.squared()).sum::<T>().sqrt()
    }
    fn norm_max(&self) -> Self::Output {
        self.iter().fold(T::zero(), |f, &val| {
            let v = val.abs();
            if f > v { f } else { v }
        })
    }
}

/// Field with norm
pub trait Absolute {
    type Output: Float;
    fn squared(&self) -> Self::Output;
    fn abs(&self) -> Self::Output {
        self.squared().sqrt()
    }
}

macro_rules! impl_abs {
    ($f:ty, $c:ty) => {

impl Absolute for $f {
    type Output = Self;
    fn squared(&self) -> Self::Output {
        *self * *self
    }
    fn abs(&self) -> Self::Output {
        Float::abs(*self)
    }
}

impl Absolute for $c {
    type Output = $f;
    fn squared(&self) -> Self::Output {
        self.norm_sqr()
    }
    fn abs(&self) -> Self::Output {
        self.norm()
    }
}

}} // impl_abs!

impl_abs!(f64, c64);
impl_abs!(f32, c32);
<|endoftext|>


<commit_before>use Client;

pub mod info;
pub mod user;
pub mod list;

/// Shortcuts to the endpoints

impl Client {
	pub fn api(&self) -> Api
	{
		Api { client: self.clone() }
	}
}

#[allow(dead_code)]
pub struct Api {
	client: Client,
}

// Todo: move dat shit away
<commit_msg>doc cleanup - remove unused cod - hide not yet implemented mods<commit_after>use Client;

pub mod info;
#[doc(hidden)]
pub mod user;
#[doc(hidden)]
pub mod list;

// Todo: move dat shit away
<|endoftext|>


<commit_before>pub fn guess_single_xor_char_decode(bytes: &Vec<u8>) -> (u16, Vec<u8>, u8) {
	use english_scoring::*;
	use key::*;

	let mut top_score = 0;
	let mut top_decode = vec![];
	let mut top_x = 2;
	for x in 0..255 {
		let mut rk = RepeatingKey::new_bytes(&vec![x]);
		let decoded = rk.encrypt_bytes(&bytes);

		let score = score_combined(&decoded);
		if score > top_score {
			top_score = score;
			top_decode = decoded;
			top_x = x;
		}
	}

	(top_score, top_decode, top_x)
}

pub fn guess_repeating_xor_key(data_bytes: &Vec<u8>, max_keysize: usize) -> (u16, Vec<u8>, Vec<u8>) {
	// As described at http://cryptopals.com/sets/1/challenges/6/
	use byte_manipulation::*;
	use byte_utilities::*;
	use english_scoring::*;
	use general_utilities::*;
	use key::*;

	// Shortcut single character key guesses
	if (max_keysize == 1) {
		let (a, b, c) = guess_single_xor_char_decode(&data_bytes);
		return (a, b, vec![c]);
	}

	// For each KEYSIZE, take the first KEYSIZE worth of bytes, and the second KEYSIZE worth of bytes,
	// and find the edit distance between them. Normalize this result by dividing by KEYSIZE.
	// The KEYSIZE with the smallest normalized edit distance is probably the key.
	//  You could proceed perhaps with the smallest 2-3 KEYSIZE values. Or take 4 KEYSIZE blocks instead of 2 and average the distances.
	let mut keysize_points = Vec::new();
	for keysize in 2..max_keysize+1 {
		if data_bytes.len() < keysize * 4 {
			continue;
		}

		let chunks: Vec<Vec<u8>> = data_bytes.chunks(keysize).map(|x| slice_to_vec(x)).collect();
		assert!(chunks.len() >= 4);

		let distance1 = hamming_distance(&chunks[0], &chunks[1]);
		let distance2 = hamming_distance(&chunks[1], &chunks[2]);
		let distance3 = hamming_distance(&chunks[2], &chunks[3]);
		let distance4 = hamming_distance(&chunks[3], &chunks[4]);
		let avg_distance: f32 = (distance1 + distance2 + distance3 + distance4) as f32 / 4.0;
		let normalized_distance = avg_distance / keysize as f32;

		let pair = (keysize, normalized_distance);
		keysize_points.push(pair);
	}

	// Sort into the best keysize values
	keysize_points.sort_by(|a, b| {
		use std::cmp::Ordering::*;

		let (_, x): (_, f32) = *a;
		let (_, y): (_, f32) = *b;

		// f32s require partial cmp
		x.partial_cmp(&y).unwrap_or(Equal)
	});

	let mut top_score = 0;
	let mut top_decode: Vec<u8> = Vec::new();
	let mut top_key: Vec<u8> = Vec::new();

	// Check the best 10, any more than that starts using up a lot of time.
	for y in 0..10 {
		if y >= keysize_points.len() {
			break;
		}
		let (best_keysize, _) = keysize_points[y];

		// Now that you probably know the KEYSIZE: break the ciphertext into blocks of KEYSIZE length.
		let blocks: Vec<Vec<u8>> = data_bytes
			.chunks(best_keysize)
			.map(|x| slice_to_vec(x))
			.collect();

		// Now transpose the blocks: make a block that is the first byte of every block, and a block that is the second byte of every block, and so on.
		let transposed_blocks: Vec<Vec<u8>> = transpose_chunks(&blocks);

		// Solve each block as if it was single-character XOR. You already have code to do this.
		//: Vec<(u16, String, u8)>
		let solved_key: Vec<_> = transposed_blocks.iter()
			.map(|x| {
				let (_, _, guess) = guess_single_xor_char_decode(x);
				guess
			})
			.collect();

		// For each block, the single-byte XOR key that produces the best looking histogram is the repeating-key XOR key byte for that block.
		// Put them together and you have the key.
		let mut repeating_key = RepeatingKey::new_bytes(&solved_key);
		let decoded = repeating_key.encrypt_bytes(&data_bytes);

		let score = score_combined(&decoded);
		if score > top_score {
			top_score = score;
			top_decode = decoded;
			top_key = solved_key;
		}
	}

	(top_score, top_decode, top_key)
}

//-----------------------------------------------------------------------------

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

	#[test]
	fn guess_single_xor_char_decode_i_set_1_problem_3() {
		// http://cryptopals.com/sets/1/challenges/3/
		use byte_conversion::*;
		let encoded = hex_to_bytes("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736");

		let (top_score, guessed_decode, guessed_character) = guess_single_xor_char_decode(&encoded);

		let expected = readable_text_to_bytes(&"Cooking MC's like a pound of bacon");

		assert_eq!(expected, guessed_decode);
		assert_eq!(88, guessed_character);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_single_xor_char_decode_i_example() {
		// http://cryptopals.com/sets/1/challenges/3/
		use byte_conversion::*;
		use key::*;

		let mut rk = RepeatingKey::new("Q");
		let example = readable_text_to_bytes(&"Some super hard to decrypt (but still English!) text.");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_character) = guess_single_xor_char_decode(&encrypted);

		assert_eq!(example, guessed_decode);
		assert_eq!('Q' as u8, guessed_character);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_repeating_xor_key_decode_i_single_character() {
		// http://cryptopals.com/sets/1/challenges/3/
		use byte_conversion::*;
		use key::*;

		let example_key: Vec<u8> = readable_text_to_bytes(&"Q");
		let mut rk = RepeatingKey::new_bytes(&example_key);
		let example = readable_text_to_bytes(&"Some super hard to decrypt (but still English!) text.");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_key) = guess_repeating_xor_key(&encrypted, 1);

		assert_eq!(example, guessed_decode);
		assert_eq!(example_key, guessed_key);
		assert!(top_score > 0);
	}
}
<commit_msg>Attacks non-trivial tests<commit_after>pub fn guess_single_xor_char_decode(bytes: &Vec<u8>) -> (u16, Vec<u8>, u8) {
	use english_scoring::*;
	use key::*;

	let mut top_score = 0;
	let mut top_decode = vec![];
	let mut top_x = 2;
	for x in 0..255 {
		let mut rk = RepeatingKey::new_bytes(&vec![x]);
		let decoded = rk.encrypt_bytes(&bytes);

		let score = score_combined(&decoded);
		if score > top_score {
			top_score = score;
			top_decode = decoded;
			top_x = x;
		}
	}

	(top_score, top_decode, top_x)
}

pub fn guess_repeating_xor_key(data_bytes: &Vec<u8>, max_keysize: usize) -> (u16, Vec<u8>, Vec<u8>) {
	// As described at http://cryptopals.com/sets/1/challenges/6/
	use byte_manipulation::*;
	use byte_utilities::*;
	use english_scoring::*;
	use general_utilities::*;
	use key::*;

	// Shortcut single character key guesses
	if (max_keysize == 1) {
		let (a, b, c) = guess_single_xor_char_decode(&data_bytes);
		return (a, b, vec![c]);
	}

	// For each KEYSIZE, take the first KEYSIZE worth of bytes, and the second KEYSIZE worth of bytes,
	// and find the edit distance between them. Normalize this result by dividing by KEYSIZE.
	// The KEYSIZE with the smallest normalized edit distance is probably the key.
	//  You could proceed perhaps with the smallest 2-3 KEYSIZE values. Or take 4 KEYSIZE blocks instead of 2 and average the distances.
	let mut keysize_points = Vec::new();
	for keysize in 2..max_keysize+1 {
		if data_bytes.len() < keysize * 4 {
			continue;
		}

		let chunks: Vec<Vec<u8>> = data_bytes.chunks(keysize).map(|x| slice_to_vec(x)).collect();
		assert!(chunks.len() >= 4);

		let distance1 = hamming_distance(&chunks[0], &chunks[1]);
		let distance2 = hamming_distance(&chunks[1], &chunks[2]);
		let distance3 = hamming_distance(&chunks[2], &chunks[3]);
		let distance4 = hamming_distance(&chunks[3], &chunks[4]);
		let avg_distance: f32 = (distance1 + distance2 + distance3 + distance4) as f32 / 4.0;
		let normalized_distance = avg_distance / keysize as f32;

		let pair = (keysize, normalized_distance);
		keysize_points.push(pair);
	}

	// Sort into the best keysize values
	keysize_points.sort_by(|a, b| {
		use std::cmp::Ordering::*;

		let (_, x): (_, f32) = *a;
		let (_, y): (_, f32) = *b;

		// f32s require partial cmp
		x.partial_cmp(&y).unwrap_or(Equal)
	});

	let mut top_score = 0;
	let mut top_decode: Vec<u8> = Vec::new();
	let mut top_key: Vec<u8> = Vec::new();

	// Check the best 10, any more than that starts using up a lot of time.
	for y in 0..10 {
		if y >= keysize_points.len() {
			break;
		}
		let (best_keysize, _) = keysize_points[y];

		// Now that you probably know the KEYSIZE: break the ciphertext into blocks of KEYSIZE length.
		let blocks: Vec<Vec<u8>> = data_bytes
			.chunks(best_keysize)
			.map(|x| slice_to_vec(x))
			.collect();

		// Now transpose the blocks: make a block that is the first byte of every block, and a block that is the second byte of every block, and so on.
		let transposed_blocks: Vec<Vec<u8>> = transpose_chunks(&blocks);

		// Solve each block as if it was single-character XOR. You already have code to do this.
		//: Vec<(u16, String, u8)>
		let solved_key: Vec<_> = transposed_blocks.iter()
			.map(|x| {
				let (_, _, guess) = guess_single_xor_char_decode(x);
				guess
			})
			.collect();

		// For each block, the single-byte XOR key that produces the best looking histogram is the repeating-key XOR key byte for that block.
		// Put them together and you have the key.
		let mut repeating_key = RepeatingKey::new_bytes(&solved_key);
		let decoded = repeating_key.encrypt_bytes(&data_bytes);

		let score = score_combined(&decoded);
		if score > top_score {
			top_score = score;
			top_decode = decoded;
			top_key = solved_key;
		}
	}

	(top_score, top_decode, top_key)
}

//-----------------------------------------------------------------------------

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

	#[test]
	fn guess_single_xor_char_decode_i_set_1_problem_3() {
		// http://cryptopals.com/sets/1/challenges/3/
		use byte_conversion::*;
		let encoded = hex_to_bytes("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736");

		let (top_score, guessed_decode, guessed_character) = guess_single_xor_char_decode(&encoded);

		let expected = readable_text_to_bytes(&"Cooking MC's like a pound of bacon");

		assert_eq!(expected, guessed_decode);
		assert_eq!(88, guessed_character);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_single_xor_char_decode_i_example() {
		use byte_conversion::*;
		use key::*;

		let mut rk = RepeatingKey::new("Q");
		let example = readable_text_to_bytes(&"Some super hard to decrypt (but still English!) text.");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_character) = guess_single_xor_char_decode(&encrypted);

		assert_eq!(example, guessed_decode);
		assert_eq!('Q' as u8, guessed_character);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_repeating_xor_key_decode_i_single_character() {
		use byte_conversion::*;
		use key::*;

		let example_key: Vec<u8> = readable_text_to_bytes(&"Q");
		let mut rk = RepeatingKey::new_bytes(&example_key);
		let example = readable_text_to_bytes(&"Some super hard to decrypt (but still English!) text.");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_key) = guess_repeating_xor_key(&encrypted, 1);

		assert_eq!(example, guessed_decode);
		assert_eq!(example_key, guessed_key);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_repeating_xor_key_decode_i_two_characters() {
		use byte_conversion::*;
		use key::*;

		let example_key: Vec<u8> = readable_text_to_bytes(&"Qr");
		let mut rk = RepeatingKey::new_bytes(&example_key);
		let example = readable_text_to_bytes(&"Some super hard to decrypt (but still English!) text.");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_key) = guess_repeating_xor_key(&encrypted, 2);

		assert_eq!(example, guessed_decode);
		assert_eq!(example_key, guessed_key);
		assert!(top_score > 0);
	}

	#[test]
	fn guess_repeating_xor_key_decode_i_non_trivial_key() {
		// http://cryptopals.com/sets/1/challenges/6/
		use byte_conversion::*;
		use key::*;

		let example_key: Vec<u8> = readable_text_to_bytes(&"Terminator X: Bring the noise");
		let mut rk = RepeatingKey::new_bytes(&example_key);
		let example = readable_text_to_bytes(&"I\'m back and I\'m ringin\' the bell \nA rockin\' on the mike while the fly girls yell \nIn ecstasy in the back of me \nWell that\'s my DJ Deshay cuttin\' all them Z\'s \nHittin\' hard and the girlies goin\' crazy \nVanilla\'s on the mike, man I\'m not lazy. \n\nI\'m lettin\' my drug kick in \nIt controls my mouth and I begin \nTo just let it flow, let my concepts go \nMy posse\'s to the side yellin\', Go Vanilla Go! \n\nSmooth \'cause that\'s the way I will be \nAnd if youdon\'t give a damn, then \nWhy you starin\' at me \nSo get off \'cause I control the stage \nThere\'s no dissin\' allowed \nI\'m in my own phase \nThe girlies sa y they love me and that is ok \nAnd I can dance better than any kid n\' play \n\nStage 2 -- Yea the one ya\' wanna listen to \nIt\'s off my head so let the beat play through \nSo I can funk it up and make it sound good \n1-2-3 Yo -- Knock on some wood \nFor good luck, I like my rhymes atrocious \nSupercalafragilisticexpialidocious \nI\'m an effect and that you can bet \nI can take a fly girl and make her wet. \n\nI\'m like Samson -- Samson to Delilah \nThere\'s no denyin\', You can try to hang \nBut you\'ll keep tryin\' to get my style \nOver and over, practice makes perfect \nBut not if you\'re a loafer. \n\nYou\'ll get nowhere, no place, no time, no girls \nSoon -- Oh my God, homebody, you probably eat \nSpaghetti with a spoon! Come on and say it! \n\nVIP. Vanilla Ice yep, yep, I\'m comin\' hard like a rhino \nIntoxicating so you stagger like a wino \nSo punks stop trying and girl stop cryin\' \nVanilla Ice is sellin\' and you people are buyin\' \n\'Cause why the freaks are jockin\' like Crazy Glue \nMovin\' and groovin\' trying to sing along \nAll through the ghetto groovin\' this here song \nNow you\'re amazed by the VIP posse. \n\nSteppin\' so hard like a German Nazi \nStartled by the bases hittin\' ground \nThere\'s no trippin\' on mine, I\'m just gettin\' down \nSparkamatic, I\'m hangin\' tight like a fanatic \nYou trapped me once and I thought that \nYou might have it \nSo step down and lend me your ear \n\'89 in my time! You, \'90 is my year. \n\nYou\'re weakenin\' fast, YO! and I can tell it \nYour body\'s gettin\' hot, so, so I can smell it \nSo don\'t be mad and don\'t be sad \n\'Cause the lyrics belong to ICE, You can call me Dad \nYou\'re pitchin\' a fit, so step back and endure \nLet the witch doctor, Ice, do thedance to cure \nSo come up close and don\'t be square \nYou wanna battle me -- Anytime, anywhere \n\nYou thought that Iwas weak, Boy, you\'re dead wrong \nSo come on, everybody and sing this song \n\nSay -- Play that funky music Say, go white boy, go white boy go \nplay that funky music Go white boy, go white boy, go \nLay down and boogie and play that funky music till you die. \n\nPlay that funky music Come on, Come on, let me hear \nPlay that funky music white boy you sayit, say it \nPlay that funky music A little louder now \nPlay that funky music, white boy Come on, Come on, Come on \nPlay that funky music \n");

		let encrypted = rk.encrypt_bytes(&example);
		let (top_score, guessed_decode, guessed_key) = guess_repeating_xor_key(&encrypted, 29);

		assert_eq!(bytes_to_readable_text(&example), bytes_to_readable_text(&guessed_decode));
		assert_eq!(example_key, guessed_key);
		assert!(top_score > 0);
	}
}
<|endoftext|>


<commit_before>use std::fmt;

const SCREEN_WIDTH: usize = 256;
const SCREEN_HEIGHT: usize = 240;

// Shamlessly copied from SprocketNES...too much typing
static PALETTE: [u8; 192] = [
    124,124,124,    0,0,252,        0,0,188,        68,40,188,
    148,0,132,      168,0,32,       168,16,0,       136,20,0,
    80,48,0,        0,120,0,        0,104,0,        0,88,0,
    0,64,88,        0,0,0,          0,0,0,          0,0,0,
    188,188,188,    0,120,248,      0,88,248,       104,68,252,
    216,0,204,      228,0,88,       248,56,0,       228,92,16,
    172,124,0,      0,184,0,        0,168,0,        0,168,68,
    0,136,136,      0,0,0,          0,0,0,          0,0,0,
    248,248,248,    60,188,252,     104,136,252,    152,120,248,
    248,120,248,    248,88,152,     248,120,88,     252,160,68,
    248,184,0,      184,248,24,     88,216,84,      88,248,152,
    0,232,216,      120,120,120,    0,0,0,          0,0,0,
    252,252,252,    164,228,252,    184,184,248,    216,184,248,
    248,184,248,    248,164,192,    240,208,176,    252,224,168,
    248,216,120,    216,248,120,    184,248,184,    184,248,216,
    0,252,252,      248,216,248,    0,0,0,          0,0,0
];

pub struct Ppu {
    ppuctrl: u8,
    ppumask: u8,
    ppustatus: u8,
    oamaddr: u8,
    oamdata: u8,
    ppuscroll: u8,
    ppuaddr: u8,
    ppudata: u8,
    oamdma: u8,

    cycles: u64,
    pub frame: Box<[u8; SCREEN_WIDTH*SCREEN_HEIGHT*3]>,
}

impl Ppu {
    pub fn new() -> Ppu {
        Ppu {
            ppuctrl: 0,
            ppumask: 0,
            ppustatus: 0,
            oamaddr: 0,
            oamdata: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            ppudata: 0,
            oamdma: 0,

            cycles: 0,
            frame: Box::new([0u8; SCREEN_WIDTH*SCREEN_HEIGHT*3]),
        }
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        if addr == 0x4014 {
            self.oamdma
        } else {
            match addr {
                0x0 => self.ppuctrl,
                0x1 => self.ppumask,
                0x2 => self.ppustatus,
                0x3 => self.oamaddr,
                0x4 => self.oamdata,
                0x5 => self.ppuscroll,
                0x6 => self.ppuaddr,
                0x7 => self.ppudata,
                _ => panic!("Attemped access of nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn write_to_reg(&mut self, addr: u16, val: u8) {
        if addr == 0x4014 {
            self.oamdma = val;
        } else {
            match addr {
                0x0 => self.ppuctrl = val,
                0x1 => self.ppumask = val,
                0x2 => self.ppustatus = val,
                0x3 => self.oamaddr = val,
                0x4 => self.oamdata = val,
                0x5 => self.ppuscroll = val,
                0x6 => self.ppuaddr = val,
                0x7 => self.ppudata = val,
                _ => panic!("Attemped write to nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn power_up(&mut self) {
        self.ppustatus = 0b10100000;
    }

    pub fn step(&mut self, cpu_cycle: &u64) {

    }
}

impl fmt::Debug for Ppu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "PPU: ppuctrl: {:#x}, ppumask: {:#x}, ppustatus: {:#x}, oamaddr: {:#x}, oamdata: \
                {:#x}, ppuscroll: {:#x}, ppuaddr: {:#x},  ppudata: {:#x}, oamdma: {:#x}",
               self.ppuctrl,
               self.ppumask,
               self.ppustatus,
               self.oamaddr,
               self.oamdata,
               self.ppuscroll,
               self.ppuaddr,
               self.ppudata,
               self.oamdma)
    }
}
<commit_msg>Start modelling ppu scanline functionality<commit_after>use std::fmt;

const SCREEN_WIDTH: usize = 256;
const SCREEN_HEIGHT: usize = 240;
const CPU_CYCLES_PER_SCANLINE: u16 = 114;

// Shamlessly copied from SprocketNES...too much typing
static PALETTE: [u8; 192] = [
    124,124,124,    0,0,252,        0,0,188,        68,40,188,
    148,0,132,      168,0,32,       168,16,0,       136,20,0,
    80,48,0,        0,120,0,        0,104,0,        0,88,0,
    0,64,88,        0,0,0,          0,0,0,          0,0,0,
    188,188,188,    0,120,248,      0,88,248,       104,68,252,
    216,0,204,      228,0,88,       248,56,0,       228,92,16,
    172,124,0,      0,184,0,        0,168,0,        0,168,68,
    0,136,136,      0,0,0,          0,0,0,          0,0,0,
    248,248,248,    60,188,252,     104,136,252,    152,120,248,
    248,120,248,    248,88,152,     248,120,88,     252,160,68,
    248,184,0,      184,248,24,     88,216,84,      88,248,152,
    0,232,216,      120,120,120,    0,0,0,          0,0,0,
    252,252,252,    164,228,252,    184,184,248,    216,184,248,
    248,184,248,    248,164,192,    240,208,176,    252,224,168,
    248,216,120,    216,248,120,    184,248,184,    184,248,216,
    0,252,252,      248,216,248,    0,0,0,          0,0,0
];

pub struct Ppu {
    ppuctrl: u8,
    ppumask: u8,
    ppustatus: u8,
    oamaddr: u8,
    oamdata: u8,
    ppuscroll: u8,
    ppuaddr: u8,
    ppudata: u8,
    oamdma: u8,

    ppu_cycle: u16,
    cycles:
    scanline: u16,
    pub frame: Box<[u8; SCREEN_WIDTH*SCREEN_HEIGHT*3]>,
}

impl Ppu {
    pub fn new() -> Ppu {
        Ppu {
            ppuctrl: 0,
            ppumask: 0,
            ppustatus: 0,
            oamaddr: 0,
            oamdata: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            ppudata: 0,
            oamdma: 0,

            ppu_cycle: 0,
            scanline: 0,
            frame: Box::new([0u8; SCREEN_WIDTH*SCREEN_HEIGHT*3]),
        }
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        if addr == 0x4014 {
            self.oamdma
        } else {
            match addr {
                0x0 => self.ppuctrl,
                0x1 => self.ppumask,
                0x2 => self.ppustatus,
                0x3 => self.oamaddr,
                0x4 => self.oamdata,
                0x5 => self.ppuscroll,
                0x6 => self.ppuaddr,
                0x7 => self.ppudata,
                _ => panic!("Attemped access of nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn write_to_reg(&mut self, addr: u16, val: u8) {
        if addr == 0x4014 {
            self.oamdma = val;
        } else {
            match addr {
                0x0 => self.ppuctrl = val,
                0x1 => self.ppumask = val,
                0x2 => self.ppustatus = val,
                0x3 => self.oamaddr = val,
                0x4 => self.oamdata = val,
                0x5 => self.ppuscroll = val,
                0x6 => self.ppuaddr = val,
                0x7 => self.ppudata = val,
                _ => panic!("Attemped write to nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn power_up(&mut self) {
        self.ppustatus = 0b10100000;
    }

    fn render_scanline(&mut self) {
        for scanline_cycle in 0..340{
            match self.ppu_cycle {
                0 => {}, // Idle cyle
                1...256 => {}, // Visible
                257...320 => {}, // Sprite Pre-fetch
                321...336 => {}, // Tile Pre-fetch
                337...340 => {}, // Unused Nametable fetch
            }
        }
        self.scanline += 1;
    }

    pub fn step(&mut self, current_cpu_cycle: &u64) {
        while self.cycles < current_cpu_cycle {
            self.render_scanline();
            if self.scanline == VBLANK_SCANLINE {
                self.vblank();
            } else if self.scanline == FINAL_SCANLINE {
                self.scanline = 0;
            }
            self.cycles += CPU_CYCLES_PER_SCANLINE; // It's easier to just deal in cpu cycles.
        }
    }
}

impl fmt::Debug for Ppu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "PPU: ppuctrl: {:#x}, ppumask: {:#x}, ppustatus: {:#x}, oamaddr: {:#x}, oamdata: \
                {:#x}, ppuscroll: {:#x}, ppuaddr: {:#x},  ppudata: {:#x}, oamdma: {:#x}",
               self.ppuctrl,
               self.ppumask,
               self.ppustatus,
               self.oamaddr,
               self.oamdata,
               self.ppuscroll,
               self.ppuaddr,
               self.ppudata,
               self.oamdma)
    }
}
<|endoftext|>


<commit_before>use {Action, InputTranslator, InputRebind, MouseTranslationData};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    #[allow(missing_docs)]
    pub fn new(size: Size) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    #[allow(missing_docs)]
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn viewport_size(mut self, sz: Size) -> Self {
        self.mouse_data.viewport_size = sz;
        self
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    #[allow(missing_docs)]
    pub fn with_action_mapping(mut self, but: Button, act: A) -> Self {
        self.input_remappings.push((but, act));
        self
    }

    #[allow(missing_docs)]
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    #[allow(missing_docs)]
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

impl<A: Action> Default for RebindBuilder<A> {
    fn default() -> Self {
        Self::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    #[allow(dead_code, unused_variables, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        //input_rebind.keymap.btn_map = self.input_remappings.iter().map(|x| x.clone()).collect();

        input_rebind
    }
}

<commit_msg>Add documentation comments<commit_after>use {Action, InputTranslator, InputRebind, MouseTranslationData};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    /// Creates a new `RebindBuilder` with the specified viewport size.
    pub fn new(size: Size) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    /// Set whether the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    /// Set whether the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    /// Set whether the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    /// Set whether the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    /// Sets the viewport size used for mouse position calculations.
    pub fn viewport_size(mut self, size: Size) -> Self {
        self.mouse_data.viewport_size = sz;
        self
    }

    /// Returns the currently set viewport size.
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    /// Add an association between the Button and Action in the built object.
    pub fn with_action_mapping(mut self, button: Button, action: A) -> Self {
        self.input_remappings.push((but, act));
        self
    }

    /// Creates an `InputTranslator` from this builder object. Consumes self.
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    /// Creates an `InputRebind` from this builder object. Consumes self.
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

impl<A: Action> Default for RebindBuilder<A> {
    /// Creates a new `RebindBuilder`. The viewport size is set to (800, 600).
    fn default() -> Self {
        Self::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    #[allow(dead_code, unused_variables, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        //input_rebind.keymap.btn_map = self.input_remappings.iter().map(|x| x.clone()).collect();

        input_rebind
    }
}

<|endoftext|>


<commit_before>//! Generate the site content.

// Standard library
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

// Third party
use glob::{glob, Paths};
use pandoc::{Pandoc, PandocOption, PandocOutput, InputFormat, OutputFormat, OutputKind};
use syntect::highlighting::ThemeSet;

// First party
use config::{Config, load};
use syntax_highlighting::syntax_highlight;

/// Load the `Paths` for all markdown files in the specified content directory.
fn glob_md_paths(site: &PathBuf, config: &Config) -> Result<Paths, String> {
    let content_glob_str =
        format!("{}/{}/**/*.md",
                site.to_str().ok_or(String::from("bad `site`"))?,
                config.directories.content.to_str().ok_or(String::from("bad content directory"))?);

    glob(&content_glob_str).map_err(|err| format!("{:?}", err))
}

/// Load the templates associated with each taxonomy.
fn load_templates(config: &Config) -> Result<Paths, String> {
    unimplemented!()
}

/// Generate content from a configuration.
pub fn build(site_directory: PathBuf) -> Result<(), String> {
    // In the vein of "MVP": let's start by just loading all the files. We'll
    // extract this all into standalone functions as necessary later.

    let config = load(&PathBuf::from(&site_directory))?;
    let markdown_paths = glob_md_paths(&site_directory, &config)?;
    // let templates = load_template(&config)?;

    // TODO: build from config. Also, extract and just do this once *not* at the
    //       top level function.
    let theme_file = PathBuf::from("data/base16-harmonic16.light.tmTheme");
    let theme = &ThemeSet::get_theme(theme_file).map_err(|err| format!("{:?}", err))?;

    for path_result in markdown_paths {
        // TODO: extract this into a nice function to call in a for loop/foreach.
        let path = path_result.map_err(|e| format!("{:?}", e))?;
        let file_name = path.to_str()
            .ok_or(format!("Could not convert path {:?} to str", path))?;

        let mut pandoc = Pandoc::new();
        pandoc.set_input_format(InputFormat::Markdown)
            .set_output_format(OutputFormat::Html5)
            .add_options(&[PandocOption::Smart, PandocOption::NoHighlight])
            .add_input(file_name)
            .set_output(OutputKind::Pipe);

        let pandoc_output = pandoc.execute()
            .map_err(|err| format!("pandoc failed on {}:\n{:?}", file_name, err))?;

        let converted = match pandoc_output {
            PandocOutput::ToFile(path_buf) => {
                let msg = format!("We wrote to a file ({}) instead of a pipe. That was weird.",
                                  path_buf.to_string_lossy());
                return Err(msg);
            }
            PandocOutput::ToBuffer(string) => string,
        };

        let highlighted = syntax_highlight(converted, theme);

        // TODO: extract this as part of the writing it out process.
        // TODO: set output location in config.
        let ff_path = Path::new(file_name);
        let dest = Path::new("./tests/output")
            .join(ff_path.file_name().ok_or(format!("invalid file: {}", file_name))?)
            .with_extension("html");

        let mut fd = match File::create(&dest) {
            Ok(file) => file,
            Err(reason) => {
                return Err(format!("Could not open {} for write: {}",
                                   dest.to_string_lossy(),
                                   reason));
            }
        };

        let result = write!(fd, "{}", highlighted);
        if let Err(reason) = result {
            return Err(format!("{:?}", reason.kind()));
        }
    }

    Ok(())
}
<commit_msg>Clarify arg names for site path in builder module.<commit_after>//! Generate the site content.

// Standard library
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

// Third party
use glob::{glob, Paths};
use pandoc::{Pandoc, PandocOption, PandocOutput, InputFormat, OutputFormat, OutputKind};
use syntect::highlighting::ThemeSet;

// First party
use config::{Config, load};
use syntax_highlighting::syntax_highlight;

/// Load the `Paths` for all markdown files in the specified content directory.
fn glob_md_paths(site_directory: &PathBuf, config: &Config) -> Result<Paths, String> {
    let content_glob_str =
        format!("{}/{}/**/*.md",
                site_directory.to_str().ok_or(String::from("bad `site`"))?,
                config.directories.content.to_str().ok_or(String::from("bad content directory"))?);

    glob(&content_glob_str).map_err(|err| format!("{:?}", err))
}

/// Load the templates associated with each taxonomy.
fn load_templates(site_directory: &PathBuf, config: &Config) -> Result<Paths, String> {
    unimplemented!()
}

/// Generate content from a configuration.
pub fn build(site_directory: PathBuf) -> Result<(), String> {
    // In the vein of "MVP": let's start by just loading all the files. We'll
    // extract this all into standalone functions as necessary later.

    let config = load(&PathBuf::from(&site_directory))?;
    let markdown_paths = glob_md_paths(&site_directory, &config)?;
    let templates = load_templates(&site_directory, &config)?;

    // TODO: build from config. Also, extract and just do this once *not* at the
    //       top level function.
    let theme_file = PathBuf::from("data/base16-harmonic16.light.tmTheme");
    let theme = &ThemeSet::get_theme(theme_file).map_err(|err| format!("{:?}", err))?;

    for path_result in markdown_paths {
        // TODO: extract this into a nice function to call in a for loop/foreach.
        let path = path_result.map_err(|e| format!("{:?}", e))?;
        let file_name = path.to_str()
            .ok_or(format!("Could not convert path {:?} to str", path))?;

        let mut pandoc = Pandoc::new();
        pandoc.set_input_format(InputFormat::Markdown)
            .set_output_format(OutputFormat::Html5)
            .add_options(&[PandocOption::Smart, PandocOption::NoHighlight])
            .add_input(file_name)
            .set_output(OutputKind::Pipe);

        let pandoc_output = pandoc.execute()
            .map_err(|err| format!("pandoc failed on {}:\n{:?}", file_name, err))?;

        let converted = match pandoc_output {
            PandocOutput::ToFile(path_buf) => {
                let msg = format!("We wrote to a file ({}) instead of a pipe. That was weird.",
                                  path_buf.to_string_lossy());
                return Err(msg);
            }
            PandocOutput::ToBuffer(string) => string,
        };

        let highlighted = syntax_highlight(converted, theme);

        // TODO: extract this as part of the writing it out process.
        // TODO: set output location in config.
        let ff_path = Path::new(file_name);
        let dest = Path::new("./tests/output")
            .join(ff_path.file_name().ok_or(format!("invalid file: {}", file_name))?)
            .with_extension("html");

        let mut fd = match File::create(&dest) {
            Ok(file) => file,
            Err(reason) => {
                return Err(format!("Could not open {} for write: {}",
                                   dest.to_string_lossy(),
                                   reason));
            }
        };

        let result = write!(fd, "{}", highlighted);
        if let Err(reason) = result {
            return Err(format!("{:?}", reason.kind()));
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! Minimal wrapper around internal Perl API.

use std::mem;
use std::os::raw::{ c_int };
use perl_sys::funcs::*;

pub use perl_sys::types::*;
pub use perl_sys::consts::*;

pub type Stack = OuroborosStack;

#[derive(Copy, Clone)]
pub struct Interpreter (PerlThreadContext);

impl Interpreter {
    pub unsafe fn new(pthx: PerlThreadContext) -> Interpreter { Interpreter(pthx) }
}

// Contains result of failed JMPENV_PUSH() while propagating through Rust.
struct Xcpt(c_int);

// Wrapper to safely call potentially dying perl functions (which is pretty much all of them because
// magic and ties).
//
// Put actual call into Perl inside a closure. `ouroboros_xcpt_try()` will call the closure while
// capturing Perl exceptions, so they don't skip Rust code except the closure itself, which should
// be fine, since closure is FnMut and has no destructors.
//
// If it returns zero, we assume that closure did not die and `v` was assigned to, making it safe to
// return, otherwise we panic and possibly uninitialized `v` is not revealed.
//
// This only works with POD return types and non-panicing $body, which should always be the case for
// Perl API.
macro_rules! xcpt_try {
    ( $se:ident, $( $body:stmt )* ) => {{
        let mut v = mem::uninitialized();
        {
            let mut callback: &mut FnMut() = &mut || v = { $( $body )* };
            let rc = ouroboros_xcpt_try($se.0,
                                        mem::transmute(xcpt_bouncer as extern "C" fn(_)),
                                        mem::transmute(&mut callback));
            if rc != 0 {
                panic!(Xcpt(rc));
            }
        }
        v
    }}
}

// Helper function for calling Rust closure from C.
extern "C" fn xcpt_bouncer(closure: &mut &mut FnMut()) {
    (*closure)();
}

macro_rules! perl_api_inner {
    (
        pub unsafe fn $name:ident ( & $se:ident, $( $pname:ident : $ptype:ty ),* ) {
            $( $body:stmt )*
        }
        $( $rest:tt )*
    ) => (
        perl_api_inner! {
            pub unsafe fn $name ( & $se, $( $pname : $ptype ),* ) -> () { $( $body )* }
            $( $rest )*
        }
    );

    (
        pub unsafe fn $name:ident ( & $se:ident, $( $pname:ident : $ptype:ty ),* ) -> $rtype:ty {
            $( $body:stmt )*
        }
        $( $rest:tt )*
    ) => (
        pub unsafe fn $name ( & $se, $( $pname : $ptype ),* ) -> $rtype {
            xcpt_try! { $se, $( $body )* }
        }
        perl_api_inner! { $( $rest )* }
    );

    () => ();
}

macro_rules! perl_api {
    ( $( $tt:tt )* ) => ( impl Interpreter { perl_api_inner! { $( $tt )* } } )
}

perl_api! {
    pub unsafe fn sv_iv(&self, sv: *mut SV) -> IV { ouroboros_sv_iv(self.0, sv) }
    pub unsafe fn sv_uv(&self, sv: *mut SV) -> UV { ouroboros_sv_uv(self.0, sv) }
    pub unsafe fn sv_nv(&self, sv: *mut SV) -> NV { ouroboros_sv_nv(self.0, sv) }

    pub unsafe fn sv_refcnt_inc(&self, sv: *mut SV) { ouroboros_sv_refcnt_inc_void_nn(self.0, sv) }
    pub unsafe fn sv_refcnt_dec(&self, sv: *mut SV) { ouroboros_sv_refcnt_dec_nn(self.0, sv) }

    pub unsafe fn av_fetch(&self, av: *mut AV, key: SSize_t) -> *mut *mut SV { Perl_av_fetch(self.0, av, key, 0) }
    pub unsafe fn av_fetch_lvalue(&self, av: *mut AV, key: SSize_t) -> *mut *mut SV { Perl_av_fetch(self.0, av, key, 1) }
    pub unsafe fn av_store(&self, av: *mut AV, key: SSize_t, sv: *mut SV) -> *mut *mut SV { Perl_av_store(self.0, av, key, sv) }

    pub unsafe fn st_init(&self, stack: &mut Stack) { ouroboros_stack_init(self.0, stack) }
    pub unsafe fn st_prepush(&self, stack: &mut Stack) { ouroboros_stack_prepush(self.0, stack) }
    pub unsafe fn st_putback(&self, stack: &mut Stack) { ouroboros_stack_putback(self.0, stack) }
    pub unsafe fn st_extend(&self, stack: &mut Stack, len: Size_t) { ouroboros_stack_extend(self.0, stack, len) }

    pub unsafe fn st_fetch(&self, stack: &mut Stack, idx: SSize_t) -> *mut SV { ouroboros_stack_fetch(self.0, stack, idx) }
    pub unsafe fn st_push(&self, stack: &mut Stack, val: *mut SV) { ouroboros_stack_push_sv(self.0, stack, val) }
    pub unsafe fn st_push_iv(&self, stack: &mut Stack, val: IV) { ouroboros_stack_push_iv(self.0, stack, val) }
    pub unsafe fn st_push_uv(&self, stack: &mut Stack, val: UV) { ouroboros_stack_push_uv(self.0, stack, val) }
    pub unsafe fn st_push_nv(&self, stack: &mut Stack, val: NV) { ouroboros_stack_push_nv(self.0, stack, val) }

    pub unsafe fn call_pv(&self, name: *const i8, flags: I32) -> I32 { Perl_call_pv(self.0, name, flags) }

    pub unsafe fn new_xs(&self, name: *const i8, func: XSUBADDR_t, file: *const i8) -> *mut CV {
        Perl_newXS(self.0, name, func, file)
    }
    pub unsafe fn new_sv(&self, len: STRLEN) -> *mut SV { Perl_newSV(self.0, len) }
    pub unsafe fn new_sv_iv(&self, val: IV) -> *mut SV { Perl_newSViv(self.0, val) }
    pub unsafe fn new_sv_uv(&self, val: UV) -> *mut SV { Perl_newSVuv(self.0, val) }
    pub unsafe fn new_sv_nv(&self, val: NV) -> *mut SV { Perl_newSVnv(self.0, val) }
    pub unsafe fn new_sv_pvn(&self, val: *const i8, len: STRLEN, flags: U32) -> *mut SV { Perl_newSVpvn_flags(self.0, val, len, flags) }

    pub unsafe fn get_av(&self, name: *const i8) -> *mut AV { Perl_get_av(self.0, name, 0) }
}
<commit_msg>Use ptr::write to write to un-initialized value.<commit_after>//! Minimal wrapper around internal Perl API.

use std::ptr;
use std::mem;
use std::os::raw::{ c_int };
use perl_sys::funcs::*;

pub use perl_sys::types::*;
pub use perl_sys::consts::*;

pub type Stack = OuroborosStack;

#[derive(Copy, Clone)]
pub struct Interpreter (PerlThreadContext);

impl Interpreter {
    pub unsafe fn new(pthx: PerlThreadContext) -> Interpreter { Interpreter(pthx) }
}

// Contains result of failed JMPENV_PUSH() while propagating through Rust.
struct Xcpt(c_int);

// Wrapper to safely call potentially dying perl functions (which is pretty much all of them because
// magic and ties).
//
// Put actual call into Perl inside a closure. `ouroboros_xcpt_try()` will call the closure while
// capturing Perl exceptions, so they don't skip Rust code except the closure itself, which should
// be fine, since closure is FnMut and has no destructors.
//
// If it returns zero, we assume that closure did not die and `v` was assigned to, making it safe to
// return, otherwise we panic and possibly uninitialized `v` is not revealed.
//
// This only works with POD return types and non-panicing $body, which should always be the case for
// Perl API.
macro_rules! xcpt_try {
    ( $se:ident, $( $body:stmt )* ) => {{
        let mut v = mem::uninitialized();
        {
            let mut callback: &mut FnMut() = &mut || ptr::write(&mut v, { $( $body )* });
            let rc = ouroboros_xcpt_try($se.0,
                                        mem::transmute(xcpt_bouncer as extern "C" fn(_)),
                                        mem::transmute(&mut callback));
            if rc != 0 {
                panic!(Xcpt(rc));
            }
        }
        v
    }}
}

// Helper function for calling Rust closure from C.
extern "C" fn xcpt_bouncer(closure: &mut &mut FnMut()) {
    (*closure)();
}

macro_rules! perl_api_inner {
    (
        pub unsafe fn $name:ident ( & $se:ident, $( $pname:ident : $ptype:ty ),* ) {
            $( $body:stmt )*
        }
        $( $rest:tt )*
    ) => (
        perl_api_inner! {
            pub unsafe fn $name ( & $se, $( $pname : $ptype ),* ) -> () { $( $body )* }
            $( $rest )*
        }
    );

    (
        pub unsafe fn $name:ident ( & $se:ident, $( $pname:ident : $ptype:ty ),* ) -> $rtype:ty {
            $( $body:stmt )*
        }
        $( $rest:tt )*
    ) => (
        pub unsafe fn $name ( & $se, $( $pname : $ptype ),* ) -> $rtype {
            xcpt_try! { $se, $( $body )* }
        }
        perl_api_inner! { $( $rest )* }
    );

    () => ();
}

macro_rules! perl_api {
    ( $( $tt:tt )* ) => ( impl Interpreter { perl_api_inner! { $( $tt )* } } )
}

perl_api! {
    pub unsafe fn sv_iv(&self, sv: *mut SV) -> IV { ouroboros_sv_iv(self.0, sv) }
    pub unsafe fn sv_uv(&self, sv: *mut SV) -> UV { ouroboros_sv_uv(self.0, sv) }
    pub unsafe fn sv_nv(&self, sv: *mut SV) -> NV { ouroboros_sv_nv(self.0, sv) }

    pub unsafe fn sv_refcnt_inc(&self, sv: *mut SV) { ouroboros_sv_refcnt_inc_void_nn(self.0, sv) }
    pub unsafe fn sv_refcnt_dec(&self, sv: *mut SV) { ouroboros_sv_refcnt_dec_nn(self.0, sv) }

    pub unsafe fn av_fetch(&self, av: *mut AV, key: SSize_t) -> *mut *mut SV { Perl_av_fetch(self.0, av, key, 0) }
    pub unsafe fn av_fetch_lvalue(&self, av: *mut AV, key: SSize_t) -> *mut *mut SV { Perl_av_fetch(self.0, av, key, 1) }
    pub unsafe fn av_store(&self, av: *mut AV, key: SSize_t, sv: *mut SV) -> *mut *mut SV { Perl_av_store(self.0, av, key, sv) }

    pub unsafe fn st_init(&self, stack: &mut Stack) { ouroboros_stack_init(self.0, stack) }
    pub unsafe fn st_prepush(&self, stack: &mut Stack) { ouroboros_stack_prepush(self.0, stack) }
    pub unsafe fn st_putback(&self, stack: &mut Stack) { ouroboros_stack_putback(self.0, stack) }
    pub unsafe fn st_extend(&self, stack: &mut Stack, len: Size_t) { ouroboros_stack_extend(self.0, stack, len) }

    pub unsafe fn st_fetch(&self, stack: &mut Stack, idx: SSize_t) -> *mut SV { ouroboros_stack_fetch(self.0, stack, idx) }
    pub unsafe fn st_push(&self, stack: &mut Stack, val: *mut SV) { ouroboros_stack_push_sv(self.0, stack, val) }
    pub unsafe fn st_push_iv(&self, stack: &mut Stack, val: IV) { ouroboros_stack_push_iv(self.0, stack, val) }
    pub unsafe fn st_push_uv(&self, stack: &mut Stack, val: UV) { ouroboros_stack_push_uv(self.0, stack, val) }
    pub unsafe fn st_push_nv(&self, stack: &mut Stack, val: NV) { ouroboros_stack_push_nv(self.0, stack, val) }

    pub unsafe fn call_pv(&self, name: *const i8, flags: I32) -> I32 { Perl_call_pv(self.0, name, flags) }

    pub unsafe fn new_xs(&self, name: *const i8, func: XSUBADDR_t, file: *const i8) -> *mut CV {
        Perl_newXS(self.0, name, func, file)
    }
    pub unsafe fn new_sv(&self, len: STRLEN) -> *mut SV { Perl_newSV(self.0, len) }
    pub unsafe fn new_sv_iv(&self, val: IV) -> *mut SV { Perl_newSViv(self.0, val) }
    pub unsafe fn new_sv_uv(&self, val: UV) -> *mut SV { Perl_newSVuv(self.0, val) }
    pub unsafe fn new_sv_nv(&self, val: NV) -> *mut SV { Perl_newSVnv(self.0, val) }
    pub unsafe fn new_sv_pvn(&self, val: *const i8, len: STRLEN, flags: U32) -> *mut SV { Perl_newSVpvn_flags(self.0, val, len, flags) }

    pub unsafe fn get_av(&self, name: *const i8) -> *mut AV { Perl_get_av(self.0, name, 0) }
}
<|endoftext|>


<commit_before>use rustc_serialize::Decodable;

#[derive(Debug, RustcDecodable)]
pub struct User {
  pub id: String,
  pub username: String,
  pub url: String,
  pub imageUrl: String
}

#[derive(Debug, RustcDecodable)]
pub struct Data<D: Decodable> {
  pub data: D
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum ContentFormat {
  html, markdown
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum PublishStatus {
  public, draft, unlisted
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum License {
  cc40
}

#[derive(Debug, RustcEncodable)]
pub struct Post<'a> {
  title: &'a str,
//  #[serde(rename(json="contentFormat"))]
  content_format: ContentFormat,
  content: &'a str,
  tags: Option<Vec<&'a str>>,
 // #[serde(rename(json="canonicalUrl"))]
  canonicalUrl: Option<&'a str>,
 // #[serde(rename(json="publishStatus"))]
  publishStatus: Option<PublishStatus>,
  license: Option<License>
}
<commit_msg>more licences<commit_after>use rustc_serialize::Decodable;

#[derive(Debug, RustcDecodable)]
pub struct User {
  pub id: String,
  pub username: String,
  pub url: String,
  pub imageUrl: String
}

#[derive(Debug, RustcDecodable)]
pub struct Data<D: Decodable> {
  pub data: D
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum ContentFormat {
  html, markdown
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum PublishStatus {
  public, draft, unlisted
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub enum License {
  cc40by,
  cc40bySa,
  cc40byNd,
  cc40byNc,
  cc40byNcNd,
  cc40byNcSa,
  cc40zero,
  publicDomain,
  allRightsReserved
}

#[derive(Debug, RustcEncodable)]
pub struct Post<'a> {
  title: &'a str,
//  #[serde(rename(json="contentFormat"))]
  content_format: ContentFormat,
  content: &'a str,
  tags: Option<Vec<&'a str>>,
 // #[serde(rename(json="canonicalUrl"))]
  canonicalUrl: Option<&'a str>,
 // #[serde(rename(json="publishStatus"))]
  publishStatus: Option<PublishStatus>,
  license: Option<License>
}
<|endoftext|>