<commit_before>#![feature(core, core_prelude, core_slice_ext, custom_derive)]
#![no_std]
#![crate_name = "elfloader"]
#![crate_type = "lib"]

#[cfg(test)]
#[macro_use]
extern crate std;

pub mod elf;
use core::fmt;
use core::mem::{size_of, transmute};

pub type PAddr = u64;
pub type VAddr = usize;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    name: &'s str,
    region: &'s [u8],
    header: &'s elf::FileHeader,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ {} {} [", self.name, self.header)?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

/// Implement this for ELF loading.
pub trait ElfLoader {
    /// Allocates a virtual region of size amount of bytes.
    fn allocate(&mut self, base: VAddr, size: usize, flags: elf::ProgFlag);

    /// Copies the region into the base.
    fn load(&mut self, base: VAddr, region: &'static [u8]);
}

// T must be a POD for this to be safe
unsafe fn slice_pod<T>(region: &[u8], offset: usize, count: usize) -> &[T] {
    assert!(region.len() - offset >= count * size_of::<T>());
    core::slice::from_raw_parts(region[offset..].as_ptr() as *const T, count)
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    /// Makes sure that the provided region has valid ELF magic byte sequence
    /// and is big enough to contain at least the ELF file header
    /// otherwise it will return None.
    pub fn new(name: &'s str, region: &'s [u8]) -> Option<ElfBinary<'s>> {
        if region.len() >= size_of::<elf::FileHeader>() && region.starts_with(elf::ELF_MAGIC) {
            let header: &elf::FileHeader = unsafe { &slice_pod(region, 0, 1)[0] };
            return Some(ElfBinary {
                name: name,
                region: region,
                header: header,
            });
        }

        None
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> &'s [elf::ProgramHeader] {
        let correct_header_size = self.header.phentsize as usize == size_of::<elf::ProgramHeader>();
        let pheader_region_size = self.header.phoff as usize
            + self.header.phnum as usize * self.header.phentsize as usize;
        let big_enough_region = self.region.len() >= pheader_region_size;

        if self.header.phoff == 0 || !correct_header_size || !big_enough_region {
            return &[];
        }

        unsafe {
            slice_pod(
                self.region,
                self.header.phoff as usize,
                self.header.phnum as usize,
            )
        }
    }

    // Get the string at offset str_offset in the string table strtab
    fn strtab_str(&self, strtab: &'s elf::SectionHeader, str_offset: elf::StrOffset) -> &'s str {
        assert!(strtab.shtype == elf::SHT_STRTAB);
        let data = self.section_data(strtab);
        let offset = str_offset.0 as usize;
        let mut end = offset;
        while data[end] != 0 {
            end += 1;
        }
        core::str::from_utf8(&data[offset..end]).unwrap()
    }

    // Get the name of the section
    pub fn symbol_name(&self, symbol: &'s elf::Symbol) -> &'s str {
        let strtab = self
            .section_headers()
            .iter()
            .find(|s| s.shtype == elf::SHT_STRTAB && self.section_name(s) == ".strtab")
            .unwrap();
        self.strtab_str(strtab, symbol.name)
    }

    // Get the data of the section
    pub fn section_data(&self, section: &'s elf::SectionHeader) -> &'s [u8] {
        &self.region[(section.offset as usize)..(section.offset as usize + section.size as usize)]
    }

    // Get the name of the section
    pub fn section_name(&self, section: &'s elf::SectionHeader) -> &'s str {
        self.strtab_str(
            &self.section_headers()[self.header.shstrndx as usize],
            section.name,
        )
    }

    // Get the symbols of the section
    fn section_symbols(&self, section: &'s elf::SectionHeader) -> &'s [elf::Symbol] {
        assert!(section.shtype == elf::SHT_SYMTAB);
        unsafe {
            slice_pod(
                self.section_data(section),
                0,
                section.size as usize / size_of::<elf::Symbol>(),
            )
        }
    }

    // Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s elf::Symbol)>(&self, mut func: F) {
        for sym in self
            .section_headers()
            .iter()
            .filter(|s| s.shtype == elf::SHT_SYMTAB)
            .flat_map(|s| self.section_symbols(s).iter())
        {
            func(sym);
        }
    }

    /// Create a slice of the section headers.
    pub fn section_headers(&self) -> &'s [elf::SectionHeader] {
        let correct_header_size = self.header.shentsize as usize == size_of::<elf::SectionHeader>();
        let sheader_region_size = self.header.shoff as usize
            + self.header.shnum as usize * self.header.shentsize as usize;
        let big_enough_region = self.region.len() >= sheader_region_size;

        if self.header.shoff == 0 || !correct_header_size || !big_enough_region {
            return &[];
        }

        unsafe {
            slice_pod(
                self.region,
                self.header.shoff as usize,
                self.header.shnum as usize,
            )
        }
    }

    /// Can we load the binary on our platform?
    fn _can_load(&self) -> bool {
        let correct_class = self.header.ident.class == elf::ELFCLASS64;
        let correct_elfversion = self.header.ident.version == elf::EV_CURRENT;
        let correct_data = self.header.ident.data == elf::ELFDATA2LSB;
        let correct_osabi = self.header.ident.osabi == elf::ELFOSABI_SYSV
            || self.header.ident.osabi == elf::ELFOSABI_LINUX;
        let correct_type =
            unsafe { self.header.elftype == elf::ET_EXEC || self.header.elftype == elf::ET_DYN };
        let correct_machine = unsafe { self.header.machine == elf::EM_X86_64 };

        correct_class
            && correct_data
            && correct_elfversion
            && correct_machine
            && correct_osabi
            && correct_type
    }

    fn load_header(&self, p: &elf::ProgramHeader, loader: &mut ElfLoader) {
        let big_enough_region = self.region.len() >= (p.offset + p.filesz) as usize;
        if !big_enough_region {
            //log!("Unable to load {}", p);
            return;
        }

        loader.allocate(p.vaddr, p.memsz as usize, p.flags);
        let segment: &'static [u8] = unsafe {
            core::slice::from_raw_parts(
                transmute(&self.region[p.offset as usize]),
                p.filesz as usize,
            )
        };
        loader.load(p.vaddr, segment);
    }

    pub fn load(&self, loader: &mut ElfLoader) {
        for p in self.program_headers() {
            let _ = match p.progtype {
                elf::PT_LOAD => self.load_header(p, loader),
                _ => (),
            };
        }
    }
}
<commit_msg>Deny warnings by default.<commit_after>#![feature(core, core_prelude, core_slice_ext, custom_derive)]
#![no_std]
#![crate_name = "elfloader"]
#![crate_type = "lib"]
#![deny(warnings)]

#[cfg(test)]
#[macro_use]
extern crate std;

pub mod elf;
use core::fmt;
use core::mem::{size_of, transmute};

pub type PAddr = u64;
pub type VAddr = usize;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    name: &'s str,
    region: &'s [u8],
    header: &'s elf::FileHeader,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ {} {} [", self.name, self.header)?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

/// Implement this for ELF loading.
pub trait ElfLoader {
    /// Allocates a virtual region of size amount of bytes.
    fn allocate(&mut self, base: VAddr, size: usize, flags: elf::ProgFlag);

    /// Copies the region into the base.
    fn load(&mut self, base: VAddr, region: &'static [u8]);
}

// T must be a POD for this to be safe
unsafe fn slice_pod<T>(region: &[u8], offset: usize, count: usize) -> &[T] {
    assert!(region.len() - offset >= count * size_of::<T>());
    core::slice::from_raw_parts(region[offset..].as_ptr() as *const T, count)
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    /// Makes sure that the provided region has valid ELF magic byte sequence
    /// and is big enough to contain at least the ELF file header
    /// otherwise it will return None.
    pub fn new(name: &'s str, region: &'s [u8]) -> Option<ElfBinary<'s>> {
        if region.len() >= size_of::<elf::FileHeader>() && region.starts_with(elf::ELF_MAGIC) {
            let header: &elf::FileHeader = unsafe { &slice_pod(region, 0, 1)[0] };
            return Some(ElfBinary {
                name: name,
                region: region,
                header: header,
            });
        }

        None
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> &'s [elf::ProgramHeader] {
        let correct_header_size = self.header.phentsize as usize == size_of::<elf::ProgramHeader>();
        let pheader_region_size = self.header.phoff as usize
            + self.header.phnum as usize * self.header.phentsize as usize;
        let big_enough_region = self.region.len() >= pheader_region_size;

        if self.header.phoff == 0 || !correct_header_size || !big_enough_region {
            return &[];
        }

        unsafe {
            slice_pod(
                self.region,
                self.header.phoff as usize,
                self.header.phnum as usize,
            )
        }
    }

    // Get the string at offset str_offset in the string table strtab
    fn strtab_str(&self, strtab: &'s elf::SectionHeader, str_offset: elf::StrOffset) -> &'s str {
        assert!(strtab.shtype == elf::SHT_STRTAB);
        let data = self.section_data(strtab);
        let offset = str_offset.0 as usize;
        let mut end = offset;
        while data[end] != 0 {
            end += 1;
        }
        core::str::from_utf8(&data[offset..end]).unwrap()
    }

    // Get the name of the section
    pub fn symbol_name(&self, symbol: &'s elf::Symbol) -> &'s str {
        let strtab = self
            .section_headers()
            .iter()
            .find(|s| s.shtype == elf::SHT_STRTAB && self.section_name(s) == ".strtab")
            .unwrap();
        self.strtab_str(strtab, symbol.name)
    }

    // Get the data of the section
    pub fn section_data(&self, section: &'s elf::SectionHeader) -> &'s [u8] {
        &self.region[(section.offset as usize)..(section.offset as usize + section.size as usize)]
    }

    // Get the name of the section
    pub fn section_name(&self, section: &'s elf::SectionHeader) -> &'s str {
        self.strtab_str(
            &self.section_headers()[self.header.shstrndx as usize],
            section.name,
        )
    }

    // Get the symbols of the section
    fn section_symbols(&self, section: &'s elf::SectionHeader) -> &'s [elf::Symbol] {
        assert!(section.shtype == elf::SHT_SYMTAB);
        unsafe {
            slice_pod(
                self.section_data(section),
                0,
                section.size as usize / size_of::<elf::Symbol>(),
            )
        }
    }

    // Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s elf::Symbol)>(&self, mut func: F) {
        for sym in self
            .section_headers()
            .iter()
            .filter(|s| s.shtype == elf::SHT_SYMTAB)
            .flat_map(|s| self.section_symbols(s).iter())
        {
            func(sym);
        }
    }

    /// Create a slice of the section headers.
    pub fn section_headers(&self) -> &'s [elf::SectionHeader] {
        let correct_header_size = self.header.shentsize as usize == size_of::<elf::SectionHeader>();
        let sheader_region_size = self.header.shoff as usize
            + self.header.shnum as usize * self.header.shentsize as usize;
        let big_enough_region = self.region.len() >= sheader_region_size;

        if self.header.shoff == 0 || !correct_header_size || !big_enough_region {
            return &[];
        }

        unsafe {
            slice_pod(
                self.region,
                self.header.shoff as usize,
                self.header.shnum as usize,
            )
        }
    }

    /// Can we load the binary on our platform?
    fn _can_load(&self) -> bool {
        let correct_class = self.header.ident.class == elf::ELFCLASS64;
        let correct_elfversion = self.header.ident.version == elf::EV_CURRENT;
        let correct_data = self.header.ident.data == elf::ELFDATA2LSB;
        let correct_osabi = self.header.ident.osabi == elf::ELFOSABI_SYSV
            || self.header.ident.osabi == elf::ELFOSABI_LINUX;
        let correct_type =
            unsafe { self.header.elftype == elf::ET_EXEC || self.header.elftype == elf::ET_DYN };
        let correct_machine = unsafe { self.header.machine == elf::EM_X86_64 };

        correct_class
            && correct_data
            && correct_elfversion
            && correct_machine
            && correct_osabi
            && correct_type
    }

    fn load_header(&self, p: &elf::ProgramHeader, loader: &mut ElfLoader) {
        let big_enough_region = self.region.len() >= (p.offset + p.filesz) as usize;
        if !big_enough_region {
            //log!("Unable to load {}", p);
            return;
        }

        loader.allocate(p.vaddr, p.memsz as usize, p.flags);
        let segment: &'static [u8] = unsafe {
            core::slice::from_raw_parts(
                transmute(&self.region[p.offset as usize]),
                p.filesz as usize,
            )
        };
        loader.load(p.vaddr, segment);
    }

    pub fn load(&self, loader: &mut ElfLoader) {
        for p in self.program_headers() {
            let _ = match p.progtype {
                elf::PT_LOAD => self.load_header(p, loader),
                _ => (),
            };
        }
    }
}
<|endoftext|>


<commit_before>#![feature(io)]

extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::iter::IteratorExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

fn tag_text_elem(parent: &mut Element, child_name: &'static str, child_body: &str) {
    parent.tag(elem_with_text(child_name, child_body));
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        match reader.read_to_string(&mut rss_string) {
            Ok(..) => (),
            Err(..) => return Err("Error reading string from reader"),
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            match builder.push_event(event) {
                Ok(Some(element)) => return ViaXml::from_xml(element),
                _ => (),
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    // pub language: Option<String>,
    // pub copyright: Option<String>,
    // pub managing_editor: Option<String>,
    // pub web_master: Option<String>,
    // pub pub_date: Option<String>,
    // pub last_build_date: Option<String>,
    // pub category:
    // pub generator: Option<String>,
    // pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    // pub image: Option<String>,
    // pub rating: Option<String>,
    // pub text_input:
    // pub skip_hours: Option<String>,
    // pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        tag_text_elem(&mut channel, "title", &self.title);
        tag_text_elem(&mut channel, "link", &self.link);
        tag_text_elem(&mut channel, "description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut channel: Channel = Default::default();

        match element.get_child("title", None) {
            Some(element) => channel.title = element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        }

        match element.get_child("link", None) {
            Some(element) => channel.link = element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        }

        match element.get_child("description", None) {
            Some(element) => channel.description = element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        }

        channel.items = element.get_children("item", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(channel)
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    // pub author
    // pub category
    // pub comments
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        if let Some(ref s) = self.title {
            tag_text_elem(&mut item, "title", s);
        }

        if let Some(ref s) = self.link {
            tag_text_elem(&mut item, "link", s);
        }

        if let Some(ref s) = self.description {
            tag_text_elem(&mut item, "description", s);
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut item: Item = Default::default();

        match element.get_child("title", None) {
            Some(element) => item.title = Some(element.content_str()),
            None => (),
        }

        match element.get_child("link", None) {
            Some(element) => item.link = Some(element.content_str()),
            None => (),
        }

        match element.get_child("description", None) {
            Some(element) => item.description = Some(element.content_str()),
            None => (),
        }

        Ok(item)
    }
}

#[cfg(test)]
mod test {
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let Rss(_) = Rss::from_read(&mut file).unwrap();
    }

    #[test]
    #[should_fail]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_fail]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel_with_title() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<commit_msg>Create+utilize helper function<commit_after>#![feature(io)]

extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::iter::IteratorExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

fn tag_text_elem(parent: &mut Element, child_name: &'static str, child_body: &str) {
    parent.tag(elem_with_text(child_name, child_body));
}

fn tag_text_elem_opt(parent: &mut Element, child_name: &'static str, child_body: &Option<String>) {
    if let Some(ref c) = *child_body {
        tag_text_elem(parent, child_name, &c);
    }
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        match reader.read_to_string(&mut rss_string) {
            Ok(..) => (),
            Err(..) => return Err("Error reading string from reader"),
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            match builder.push_event(event) {
                Ok(Some(element)) => return ViaXml::from_xml(element),
                _ => (),
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    // pub language: Option<String>,
    // pub copyright: Option<String>,
    // pub managing_editor: Option<String>,
    // pub web_master: Option<String>,
    // pub pub_date: Option<String>,
    // pub last_build_date: Option<String>,
    // pub category:
    // pub generator: Option<String>,
    // pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    // pub image: Option<String>,
    // pub rating: Option<String>,
    // pub text_input:
    // pub skip_hours: Option<String>,
    // pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        tag_text_elem(&mut channel, "title", &self.title);
        tag_text_elem(&mut channel, "link", &self.link);
        tag_text_elem(&mut channel, "description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut channel: Channel = Default::default();

        match element.get_child("title", None) {
            Some(element) => channel.title = element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        }

        match element.get_child("link", None) {
            Some(element) => channel.link = element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        }

        match element.get_child("description", None) {
            Some(element) => channel.description = element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        }

        channel.items = element.get_children("item", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(channel)
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    // pub author
    // pub category
    // pub comments
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        tag_text_elem_opt(&mut item, "title", &self.title);
        tag_text_elem_opt(&mut item, "link", &self.link);
        tag_text_elem_opt(&mut item, "description", &self.description);

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut item: Item = Default::default();

        match element.get_child("title", None) {
            Some(element) => item.title = Some(element.content_str()),
            None => (),
        }

        match element.get_child("link", None) {
            Some(element) => item.link = Some(element.content_str()),
            None => (),
        }

        match element.get_child("description", None) {
            Some(element) => item.description = Some(element.content_str()),
            None => (),
        }

        Ok(item)
    }
}

#[cfg(test)]
mod test {
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let Rss(_) = Rss::from_read(&mut file).unwrap();
    }

    #[test]
    #[should_fail]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_fail]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel_with_title() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<|endoftext|>


<commit_before>//! This crate parses and processes `hyper::server::Request` data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};

use hyper::header::{ContentType, Headers};
use hyper::server::Request as HyperRequest;
use mime::{Attr, Mime, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadPlus;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
}

/// Parses and processes `hyper::server::Request` data is `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
///
/// This function returns two sets of data. The first, `Vec<(String, String)>`, are the
/// variable-value pairs from the POST-ed form. The second `Vec<(String, UploadedFile)>` are the
/// variable-file pairs from the POST-ed form, where `UploadedFile` structures describe the
/// uploaded file.
pub fn parse_multipart(request: &mut Request)
                       -> Result<(Vec<(String, String)>, Vec<(String, UploadedFile)>), Error> {
    let mut parameters: Vec<(String, String)> = Vec::new();
    let mut files: Vec<(String, UploadedFile)> = Vec::new();

    let string_boundary = try!(get_boundary(request));
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());

    let mut reader = BufReader::with_capacity(4096, request);

    enum State {
        // Discard until after initial boundary and CRLF.
        Discarding,
        // Capture headers to blank line.
        ReadingHeaders,
        // Capture value until boundary, then discard past CRLF.
        CapturingValue,
        // Capture file until boundary, then discard past CRLF.
        CapturingFile,
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!(reader.stream_until_token(&*boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok((parameters, files));
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        return Err(Error::PartialHeaders);
                    },
                    Err(err) => return Err(From::from(err)),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&*crlf_boundary, &mut buf));

                let cd: &ContentDisposition = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone(),
                };
                let val = try!(String::from_utf8(buf));

                parameters.push((key, val));

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents.
                let mut path = try!(TempDir::new("formdata")).into_path();
                path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
                let mut file = try!(File::create(path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&*crlf_boundary, &mut file));

                let cd: &ContentDisposition = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };

                let ct = match headers.get::<ContentType>() {
                    Some(ct) => (**ct).clone(),
                    None => mime!(Text/Plain; Charset=Utf8)
                };

                // TODO: Handle content-type: multipart/mixed as multiple files.
                // TODO: Handle content-transfer-encoding.

                let file = UploadedFile {
                    path: path,
                    filename: cd.filename.clone(),
                    content_type: ct,
                    size: read - crlf_boundary.len()
                };
                files.push((key, file));

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary(request: &Request) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match request.headers().get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token.
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)

}

/// A wrapper for request data to provide parsing multipart requests to any front-end that provides
/// a `hyper::header::Headers` and a `std::io::Read` of the request's entire body.
pub trait Request: Read {
    /// Returns a reference to the request's headers.
    fn headers(&self) -> &Headers;
}

impl<'a,'b> Request for HyperRequest<'a,'b> {
    fn headers(&self) -> &Headers {
        &self.headers
    }
}

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

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                  Host: example.domain\r\n\
                  Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                  Content-Length: 217\r\n\
                  \r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field1\"\r\n\
                  \r\n\
                  data1\r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                  Content-Type: image/gif\r\n\
                  \r\n\
                  This is a file\r\n\
                  with two lines\r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                  \r\n\
                  This is a file\r\n\
                  --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok((fields, files)) => {
                assert_eq!(fields.len(), 1);
                for (key, val) in fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(files.len(), 2);
                for (key, file) in files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(&file.filename.unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(&file.filename.unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<commit_msg>Clarify public API<commit_after>//! This crate parses and processes `hyper::server::Request` data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};

use hyper::header::{ContentType, Headers};
use hyper::server::Request as HyperRequest;
use mime::{Attr, Mime, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadPlus;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
}

/// The extracted text fields and uploaded files from a `multipart/form-data` request.
///
/// Use `parse_multipart` to devise this object from a request.
pub struct FormData {
    /// Key-value pairs for plain text fields. Technically, these are form data parts with no
    /// filename specified in the part's `Content-Disposition`.
    pub fields: Vec<(String, String)>,
    /// Key-value pairs for temporary files. Technically, these are form data parts with a filename
    /// specified in the part's `Content-Disposition`.
    pub files: Vec<(String, UploadedFile)>,
}

/// Parses and processes `hyper::server::Request` data that is `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
pub fn parse_multipart(request: &mut Request) -> Result<FormData, Error> {
    let mut fields: Vec<(String, String)> = Vec::new();
    let mut files: Vec<(String, UploadedFile)> = Vec::new();

    let string_boundary = try!(get_boundary(request));
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());

    let mut reader = BufReader::with_capacity(4096, request);

    enum State {
        // Discard until after initial boundary and CRLF.
        Discarding,
        // Capture headers to blank line.
        ReadingHeaders,
        // Capture value until boundary, then discard past CRLF.
        CapturingValue,
        // Capture file until boundary, then discard past CRLF.
        CapturingFile,
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!(reader.stream_until_token(&*boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok(FormData { fields: fields, files: files });
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        return Err(Error::PartialHeaders);
                    },
                    Err(err) => return Err(From::from(err)),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&*crlf_boundary, &mut buf));

                let cd: &ContentDisposition = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone(),
                };
                let val = try!(String::from_utf8(buf));

                fields.push((key, val));

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents.
                let mut path = try!(TempDir::new("formdata")).into_path();
                path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
                let mut file = try!(File::create(path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&*crlf_boundary, &mut file));

                let cd: &ContentDisposition = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };

                let ct = match headers.get::<ContentType>() {
                    Some(ct) => (**ct).clone(),
                    None => mime!(Text/Plain; Charset=Utf8)
                };

                // TODO: Handle content-type: multipart/mixed as multiple files.
                // TODO: Handle content-transfer-encoding.

                let file = UploadedFile {
                    path: path,
                    filename: cd.filename.clone(),
                    content_type: ct,
                    size: read - crlf_boundary.len()
                };
                files.push((key, file));

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary(request: &Request) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match request.headers().get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token.
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)

}

/// A wrapper for request data to provide parsing multipart requests to any front-end that provides
/// a `hyper::header::Headers` and a `std::io::Read` of the request's entire body.
pub trait Request: Read {
    /// Returns a reference to the request's headers.
    fn headers(&self) -> &Headers;
}

impl<'a,'b> Request for HyperRequest<'a,'b> {
    fn headers(&self) -> &Headers {
        &self.headers
    }
}

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

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                  Host: example.domain\r\n\
                  Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                  Content-Length: 217\r\n\
                  \r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field1\"\r\n\
                  \r\n\
                  data1\r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                  Content-Type: image/gif\r\n\
                  \r\n\
                  This is a file\r\n\
                  with two lines\r\n\
                  --abcdefg\r\n\
                  Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                  \r\n\
                  This is a file\r\n\
                  --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(&file.filename.unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(&file.filename.unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(slice_patterns)]

//! # Rules
//! Rules uses regular expressions to do pattern matching using syntax
//! based upon the Perl 6 regex grammar. The Perl 6 grammar has been heavily
//! revised from Perl 5 and should not be equated with it. This may look
//! nothing like any regex you have seen before.
//!
//! # Syntax
//!
//! Currently, this is designed for ASCII and may not behave properly
//! with Unicode.
//!
//! Whitespace is generally ignored so that a regex can be more readable
//! and less dense.
//!
//! ```plain
//! r"fred"    // Normal way
//! r"f r e d" // Completely equivalent
//! // Will match `apples_oranges` or any other deliminator
//! r"apples . oranges"
//! ```
//!
//! # Literals
//!
//! Alphanumerics, underscores (`_`), and everything enclosed within
//! quotes (`"`) and ticks (`'`) are the only literals.
//!
//! ```plain
//! hello_world   // Matches `hello_world`.
//! "carrot cake" // Matches `carrot cake`.
//! 'apple pie'   // Matches `apple pie`.
//! ```
//!
//! Everything else must be escaped with a backslash (`\*`) to literally match.
//!
//! ```plain
//! it\'s\ my\ birthday // Matches `it's my birthday`.
//! ```
//!
//! # Chevrons: `<>`
//!
//! Chevrons are considered a metacharacter grouping operator whose behaviour
//! changes depending on the first character found inside. The behavior for
//! each different character is:
//!
//! First character | Example | Result
//! ----------------|---------|-------
//! Whitespace | `< big small >` | Alternative quotes matches `[ 'big' | 'small' ]`
//! alphabetic | `<alpha>` | Named character class which capture
//! `?` | `<?before foo>` | A positive zero width assertion
//! `!` | `<!before foo>` | A negative zero width assertion
//! `[` | `<[ ab ]>` | A character class matches `[ 'a' | 'b' ]`
//! `-` | `<-[a] + [b]>` | Negated character class: `[ab]` negated
//! `+` | `<+ [a] >` | Doesn't modify the class.
//!
//! # Lookaround
//!
//! * lookahead - `foo <?after bar>` matches `foo` in `foobar`
//! * negative lookahead - `foo <!after bar>` matches `foo` in `foobaz`
//! * lookbehind - `<?before foo> bar` matches `bar` in `foobar`
//! * negative lookbehind - `<!before foo> bar` matches `bar` in `sushibar`
//!
//! An example with both:
//! `<?before foo> bar <?after baz>`  matches `bar` in `foobarbaz`
//!
//! # Set operators
//!
//! These operators can be applied to groups which will be analyzed later:
//!
//! ```plain
//! +       Union                // [123] + [345] = [12345]
//! |       Union                // Same
//! &       Intersection         // [123] & [345] = [3]
//! -       Difference           // [123] - [345] = [12]
//! ^       Symmetric difference // [123] ^ [345] = [1245]
//! ```
//!
//! # Character classes
//!
//! ## Default character classes
//!
//! Character | Matches               | Inverse
//! ----------|-----------------------|--------
//! `.`       | Any character         | N/A
//! `\d`      | Digit                 | `\D`
//! `\h`      | Horizontal whitespace | `\H`
//! `\n`      | Newline               | `\N`
//! `\s`      | Any whitespace        | `\S`
//! `\t`      | Tab                   | `\T`
//! `\w`      | Alphanumeric or `_`   | `\W`
//!
//! ## Custom character classes
//!
//! Characters inside a set of `<[ ]>` form a custom character
//! class:
//!
//! ```plain
//! // Matches `a` or `b` or `c`
//! <[ a b c ]>
//!
//! // `..` expresses a range so this matches
//! // from `a` to `g` or a digit
//! <[ a .. g \d ]>
//!
//! // The `[]` bind the sets together into (non-capturing)
//! // groups so set operators can be used.
//! <[0-9] - [13579]> // Matches an even number
//! <\d - [13579]>    // Same
//! ```
//!
//! # Comments
//!
//! Comments are allowed inside a regex.
//!
//! ```plain
//! // This matches `myregex`
//! r"my // This is a comment which goes to the end of the line
//! regex"
//! ```

pub mod collapse;
pub mod parse;
pub mod re;
pub mod range_set;
pub mod unicode;

<commit_msg>Hide everything in the docs which are internal<commit_after>#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(slice_patterns)]

//! # Rules
//! Rules uses regular expressions to do pattern matching using syntax
//! based upon the Perl 6 regex grammar. The Perl 6 grammar has been heavily
//! revised from Perl 5 and should not be equated with it. This may look
//! nothing like any regex you have seen before.
//!
//! # Syntax
//!
//! Currently, this is designed for ASCII and may not behave properly
//! with Unicode.
//!
//! Whitespace is generally ignored so that a regex can be more readable
//! and less dense.
//!
//! ```plain
//! r"fred"    // Normal way
//! r"f r e d" // Completely equivalent
//! // Will match `apples_oranges` or any other deliminator
//! r"apples . oranges"
//! ```
//!
//! # Literals
//!
//! Alphanumerics, underscores (`_`), and everything enclosed within
//! quotes (`"`) and ticks (`'`) are the only literals.
//!
//! ```plain
//! hello_world   // Matches `hello_world`.
//! "carrot cake" // Matches `carrot cake`.
//! 'apple pie'   // Matches `apple pie`.
//! ```
//!
//! Everything else must be escaped with a backslash (`\*`) to literally match.
//!
//! ```plain
//! it\'s\ my\ birthday // Matches `it's my birthday`.
//! ```
//!
//! # Chevrons: `<>`
//!
//! Chevrons are considered a metacharacter grouping operator whose behaviour
//! changes depending on the first character found inside. The behavior for
//! each different character is:
//!
//! First character | Example | Result
//! ----------------|---------|-------
//! Whitespace | `< big small >` | Alternative quotes matches `[ 'big' | 'small' ]`
//! alphabetic | `<alpha>` | Named character class which capture
//! `?` | `<?before foo>` | A positive zero width assertion
//! `!` | `<!before foo>` | A negative zero width assertion
//! `[` | `<[ ab ]>` | A character class matches `[ 'a' | 'b' ]`
//! `-` | `<-[a] + [b]>` | Negated character class: `[ab]` negated
//! `+` | `<+ [a] >` | Doesn't modify the class.
//!
//! # Lookaround
//!
//! * lookahead - `foo <?after bar>` matches `foo` in `foobar`
//! * negative lookahead - `foo <!after bar>` matches `foo` in `foobaz`
//! * lookbehind - `<?before foo> bar` matches `bar` in `foobar`
//! * negative lookbehind - `<!before foo> bar` matches `bar` in `sushibar`
//!
//! An example with both:
//! `<?before foo> bar <?after baz>`  matches `bar` in `foobarbaz`
//!
//! # Set operators
//!
//! These operators can be applied to groups which will be analyzed later:
//!
//! ```plain
//! +       Union                // [123] + [345] = [12345]
//! |       Union                // Same
//! &       Intersection         // [123] & [345] = [3]
//! -       Difference           // [123] - [345] = [12]
//! ^       Symmetric difference // [123] ^ [345] = [1245]
//! ```
//!
//! # Character classes
//!
//! ## Default character classes
//!
//! Character | Matches               | Inverse
//! ----------|-----------------------|--------
//! `.`       | Any character         | N/A
//! `\d`      | Digit                 | `\D`
//! `\h`      | Horizontal whitespace | `\H`
//! `\n`      | Newline               | `\N`
//! `\s`      | Any whitespace        | `\S`
//! `\t`      | Tab                   | `\T`
//! `\w`      | Alphanumeric or `_`   | `\W`
//!
//! ## Custom character classes
//!
//! Characters inside a set of `<[ ]>` form a custom character
//! class:
//!
//! ```plain
//! // Matches `a` or `b` or `c`
//! <[ a b c ]>
//!
//! // `..` expresses a range so this matches
//! // from `a` to `g` or a digit
//! <[ a .. g \d ]>
//!
//! // The `[]` bind the sets together into (non-capturing)
//! // groups so set operators can be used.
//! <[0-9] - [13579]> // Matches an even number
//! <\d - [13579]>    // Same
//! ```
//!
//! # Comments
//!
//! Comments are allowed inside a regex.
//!
//! ```plain
//! // This matches `myregex`
//! r"my // This is a comment which goes to the end of the line
//! regex"
//! ```

#[doc(hidden)] pub mod collapse;
#[doc(hidden)] pub mod parse;
#[doc(hidden)] pub mod range_set;
#[doc(hidden)] pub mod unicode;

pub mod re;
<|endoftext|>


<commit_before>extern crate hyper;

pub mod requests;

#[cfg(test)]
mod test {
    use super::requests;
    use hyper::status::StatusCode;

    #[test]
    fn test_get() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn test_post() {
        const URL: &'static str = "http://httpbin.org/post";
        let res = requests::post(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn test_put() {
        const URL: &'static str = "http://httpbin.org/put";
        let res = requests::put(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn test_head() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::head(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn test_delete() {
        const URL: &'static str = "http://httpbin.org/delete";
        let res = requests::delete(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn test_user_agent() {
        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        println!("{:?}", res.text());
        assert_eq!(res.reason(), "Ok");
    }
}
<commit_msg>Drop test name prefixes<commit_after>extern crate hyper;

pub mod requests;

#[cfg(test)]
mod test {
    use super::requests;
    use hyper::status::StatusCode;

    #[test]
    fn get() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn post() {
        const URL: &'static str = "http://httpbin.org/post";
        let res = requests::post(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn put() {
        const URL: &'static str = "http://httpbin.org/put";
        let res = requests::put(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn head() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::head(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn delete() {
        const URL: &'static str = "http://httpbin.org/delete";
        let res = requests::delete(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), StatusCode::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn user_agent() {
        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        println!("{:?}", res.text());
        assert_eq!(res.reason(), "Ok");
    }
}
<|endoftext|>


<commit_before>#![crate_name = "bindgen"]
#![crate_type = "dylib"]

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

// To avoid rather annoying warnings when matching with CXCursor_xxx as a
// constant.
#![allow(non_upper_case_globals)]

extern crate syntex_syntax as syntax;
extern crate aster;
extern crate quasi;
extern crate clang_sys;
extern crate libc;
extern crate regex;
#[macro_use]
extern crate log;

mod clangll;
mod clang;
mod ir;
mod parse;
mod regex_set;
mod codegen {
    include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
}

use std::borrow::Borrow;
use std::io::{self, Write};
use std::fs::OpenOptions;
use std::path::Path;
use std::collections::HashSet;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::print::pprust;
use syntax::print::pp::eof;
use syntax::ptr::P;

use ir::context::BindgenContext;
use ir::item::{Item, ItemId};
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;

#[derive(Debug,Default)]
pub struct Builder {
    options: BindgenOptions,
}

pub fn builder() -> Builder {
    Default::default()
}

impl Builder {
    pub fn header<T: Into<String>>(self, header: T) -> Builder {
        self.clang_arg(header)
    }

    pub fn match_pat<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.match_pat.insert(arg.into());
        self
    }

    pub fn hide_type<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.hidden_types.insert(arg.into());
        self
    }

    pub fn opaque_type<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.opaque_types.insert(arg.into());
        self
    }

    pub fn whitelisted_type<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_types.insert(&arg);
        self
    }

    pub fn whitelisted_function<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_functions.insert(&arg);
        self
    }

    pub fn whitelisted_var<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_vars.insert(&arg);
        self
    }

    pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.raw_lines.push(arg.into());
        self
    }

    pub fn clang_arg<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.clang_args.push(arg.into());
        self
    }

    pub fn link<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Default));
        self
    }

    pub fn link_static<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Static));
        self
    }

    pub fn link_framework<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Framework));
        self
    }

    pub fn dtor_attr<T: Into<String>>(mut self, attr: T) -> Builder {
        self.options.dtor_attrs.push(attr.into());
        self
    }

    pub fn forbid_unknown_types(mut self) -> Builder {
        self.options.fail_on_unknown_type = true;
        self
    }

    pub fn emit_builtins(mut self) -> Builder {
        self.options.builtins = true;
        self
    }

    pub fn no_bitfield_methods(mut self) -> Builder {
        self.options.gen_bitfield_methods = false;
        self
    }

    pub fn no_unstable_rust(mut self) -> Builder {
        self.options.unstable_rust = false;
        self
    }
    pub fn rust_enums(mut self, value: bool) -> Builder {
        self.options.rust_enums = value;
        self
    }

    pub fn rename_types(mut self, value: bool) -> Builder {
        self.options.rename_types = value;
        self
    }

    pub fn disable_class_constants(mut self) -> Builder {
        self.options.class_constants = false;
        self
    }

    pub fn generate(self) -> Result<Bindings, ()> {
        Bindings::generate(self.options, None)
    }
}

/// Deprecated - use a `Builder` instead
#[derive(Debug)]
pub struct BindgenOptions {
    pub match_pat: HashSet<String>,
    pub hidden_types: HashSet<String>,
    pub opaque_types: HashSet<String>,
    pub whitelisted_types: RegexSet,
    pub whitelisted_functions: RegexSet,
    pub whitelisted_vars: RegexSet,
    pub builtins: bool,
    pub rust_enums: bool,
    pub links: Vec<(String, LinkType)>,
    pub emit_ast: bool,
    pub ignore_functions: bool,
    pub ignore_methods: bool,
    pub gen_bitfield_methods: bool,
    pub fail_on_unknown_type: bool,
    pub enable_cxx_namespaces: bool,
    pub rename_types: bool,
    pub derive_debug: bool,
    /// Generate or not only stable rust.
    pub unstable_rust: bool,
    /// Whether to generate C++ class constants.
    pub class_constants: bool,
    /// Wether to generate names that are **directly** under namespaces.
    pub namespaced_constants: bool,
    /// Whether to use msvc mangling rules
    pub msvc_mangling: bool,
    pub override_enum_ty: String,
    pub raw_lines: Vec<String>,
    /// Attributes for a type with destructor
    pub dtor_attrs: Vec<String>,
    pub clang_args: Vec<String>,
}

impl Default for BindgenOptions {
    fn default() -> BindgenOptions {
        BindgenOptions {
            match_pat: Default::default(),
            hidden_types: Default::default(),
            opaque_types: Default::default(),
            whitelisted_types: Default::default(),
            whitelisted_functions: Default::default(),
            whitelisted_vars: Default::default(),
            builtins: false,
            rust_enums: true,
            links: vec![],
            emit_ast: false,
            ignore_functions: false,
            ignore_methods: false,
            gen_bitfield_methods: true,
            fail_on_unknown_type: true,
            rename_types: true,
            derive_debug: true,
            enable_cxx_namespaces: false,
            override_enum_ty: "".to_string(),
            unstable_rust: true,
            class_constants: true,
            namespaced_constants: true,
            msvc_mangling: false,
            raw_lines: vec![],
            dtor_attrs: vec![],
            clang_args: vec![],
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LinkType {
    Default,
    Static,
    Framework
}

#[derive(Debug, Clone)]
pub struct Bindings {
    module: ast::Mod,
    raw_lines: Vec<String>,
}

impl Bindings {
    /// Deprecated - use a `Builder` instead
    pub fn generate(options: BindgenOptions, span: Option<Span>) -> Result<Bindings, ()> {
        let span = span.unwrap_or(DUMMY_SP);

        let mut context = BindgenContext::new(options);
        parse(&mut context);

        let module = ast::Mod {
            inner: span,
            items: codegen::codegen(&mut context),
        };

        Ok(Bindings {
            module: module,
            raw_lines: context.options().raw_lines.clone(),
        })
    }

    pub fn into_ast(self) -> Vec<P<ast::Item>> {
        self.module.items
    }

    pub fn to_string(&self) -> String {
        let mut mod_str = vec![];
        {
            let ref_writer = Box::new(mod_str.by_ref()) as Box<Write>;
            self.write(ref_writer).expect("Could not write bindings to string");
        }
        String::from_utf8(mod_str).unwrap()
    }

    pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        let file = try!(OpenOptions::new().write(true).truncate(true).create(true).open(path));
        self.write(Box::new(file))
    }

    // https://github.com/Manishearth/rust-clippy/issues/740
    #[cfg_attr(feature = "clippy", allow(needless_lifetimes))]
    pub fn write<'a>(&self, mut writer: Box<Write + 'a>) -> io::Result<()> {
        try!(writer.write("/* automatically generated by rust-bindgen */\n\n".as_bytes()));

        for line in self.raw_lines.iter() {
            try!(writer.write(line.as_bytes()));
            try!(writer.write("\n".as_bytes()));
        }
        if !self.raw_lines.is_empty() {
            try!(writer.write("\n".as_bytes()));
        }

        let mut ps = pprust::rust_printer(writer);
        try!(ps.print_mod(&self.module, &[]));
        try!(ps.print_remaining_comments());
        try!(eof(&mut ps.s));
        ps.s.out.flush()
    }
}

/// Determines whether the given cursor is in any of the files matched by the
/// options.
fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
    let (file, _, _, _) = cursor.location().location();

    match file.name() {
        None => ctx.options().builtins,
        Some(..) => true,
    }
}

pub fn parse_one(ctx: &mut BindgenContext,
                 cursor: clang::Cursor,
                 parent: Option<ItemId>,
                 children: &mut Vec<ItemId>) -> clangll::Enum_CXVisitorResult {
    if !filter_builtins(ctx, &cursor) {
        return CXChildVisit_Continue
    }

    use clangll::CXChildVisit_Continue;
    match Item::parse(cursor, parent, ctx) {
        Ok(id) => children.push(id),
        Err(ParseError::Continue) => {},
        Err(ParseError::Recurse) => {
            cursor.visit(|child, _| parse_one(ctx, *child, parent, children));
        }
    }
    CXChildVisit_Continue
}

fn parse(context: &mut BindgenContext) {
    use clang::Diagnostic;
    use clangll::*;

    for d in context.translation_unit().diags().iter() {
        let msg = d.format(Diagnostic::default_opts());
        let is_err = d.severity() >= CXDiagnostic_Error;
        println!("{}, err: {}", msg, is_err);
    }

    let cursor = context.translation_unit().cursor();
    if context.options().emit_ast {
        cursor.visit(|cur, _| clang::ast_dump(cur, 0));
    }

    let root = context.root_module();
    context.with_module(root, |context, children| {
        cursor.visit(|cursor, _| parse_one(context, *cursor, None, children))
    });

    assert!(context.current_module() == context.root_module(),
            "How did this happen?");
}
<commit_msg>Auto merge of #108 - fitzgen:remove-dtor-attrs, r=emilio<commit_after>#![crate_name = "bindgen"]
#![crate_type = "dylib"]

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

// To avoid rather annoying warnings when matching with CXCursor_xxx as a
// constant.
#![allow(non_upper_case_globals)]

extern crate syntex_syntax as syntax;
extern crate aster;
extern crate quasi;
extern crate clang_sys;
extern crate libc;
extern crate regex;
#[macro_use]
extern crate log;

mod clangll;
mod clang;
mod ir;
mod parse;
mod regex_set;
mod codegen {
    include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
}

use std::borrow::Borrow;
use std::io::{self, Write};
use std::fs::OpenOptions;
use std::path::Path;
use std::collections::HashSet;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::print::pprust;
use syntax::print::pp::eof;
use syntax::ptr::P;

use ir::context::BindgenContext;
use ir::item::{Item, ItemId};
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;

#[derive(Debug,Default)]
pub struct Builder {
    options: BindgenOptions,
}

pub fn builder() -> Builder {
    Default::default()
}

impl Builder {
    pub fn header<T: Into<String>>(self, header: T) -> Builder {
        self.clang_arg(header)
    }

    pub fn hide_type<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.hidden_types.insert(arg.into());
        self
    }

    pub fn opaque_type<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.opaque_types.insert(arg.into());
        self
    }

    pub fn whitelisted_type<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_types.insert(&arg);
        self
    }

    pub fn whitelisted_function<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_functions.insert(&arg);
        self
    }

    pub fn whitelisted_var<T: Borrow<str>>(mut self, arg: T) -> Builder {
        self.options.whitelisted_vars.insert(&arg);
        self
    }

    pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.raw_lines.push(arg.into());
        self
    }

    pub fn clang_arg<T: Into<String>>(mut self, arg: T) -> Builder {
        self.options.clang_args.push(arg.into());
        self
    }

    pub fn link<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Default));
        self
    }

    pub fn link_static<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Static));
        self
    }

    pub fn link_framework<T: Into<String>>(mut self, library: T) -> Builder {
        self.options.links.push((library.into(), LinkType::Framework));
        self
    }

    pub fn forbid_unknown_types(mut self) -> Builder {
        self.options.fail_on_unknown_type = true;
        self
    }

    pub fn emit_builtins(mut self) -> Builder {
        self.options.builtins = true;
        self
    }

    pub fn no_bitfield_methods(mut self) -> Builder {
        self.options.gen_bitfield_methods = false;
        self
    }

    pub fn no_unstable_rust(mut self) -> Builder {
        self.options.unstable_rust = false;
        self
    }
    pub fn rust_enums(mut self, value: bool) -> Builder {
        self.options.rust_enums = value;
        self
    }

    pub fn rename_types(mut self, value: bool) -> Builder {
        self.options.rename_types = value;
        self
    }

    pub fn disable_class_constants(mut self) -> Builder {
        self.options.class_constants = false;
        self
    }

    pub fn generate(self) -> Result<Bindings, ()> {
        Bindings::generate(self.options, None)
    }
}

/// Deprecated - use a `Builder` instead
#[derive(Debug)]
pub struct BindgenOptions {
    pub hidden_types: HashSet<String>,
    pub opaque_types: HashSet<String>,
    pub whitelisted_types: RegexSet,
    pub whitelisted_functions: RegexSet,
    pub whitelisted_vars: RegexSet,
    pub builtins: bool,
    pub rust_enums: bool,
    pub links: Vec<(String, LinkType)>,
    pub emit_ast: bool,
    pub ignore_functions: bool,
    pub ignore_methods: bool,
    pub gen_bitfield_methods: bool,
    pub fail_on_unknown_type: bool,
    pub enable_cxx_namespaces: bool,
    pub rename_types: bool,
    pub derive_debug: bool,
    /// Generate or not only stable rust.
    pub unstable_rust: bool,
    /// Whether to generate C++ class constants.
    pub class_constants: bool,
    /// Wether to generate names that are **directly** under namespaces.
    pub namespaced_constants: bool,
    /// Whether to use msvc mangling rules
    pub msvc_mangling: bool,
    pub override_enum_ty: String,
    pub raw_lines: Vec<String>,
    pub clang_args: Vec<String>,
}

impl Default for BindgenOptions {
    fn default() -> BindgenOptions {
        BindgenOptions {
            hidden_types: Default::default(),
            opaque_types: Default::default(),
            whitelisted_types: Default::default(),
            whitelisted_functions: Default::default(),
            whitelisted_vars: Default::default(),
            builtins: false,
            rust_enums: true,
            links: vec![],
            emit_ast: false,
            ignore_functions: false,
            ignore_methods: false,
            gen_bitfield_methods: true,
            fail_on_unknown_type: true,
            rename_types: true,
            derive_debug: true,
            enable_cxx_namespaces: false,
            override_enum_ty: "".to_string(),
            unstable_rust: true,
            class_constants: true,
            namespaced_constants: true,
            msvc_mangling: false,
            raw_lines: vec![],
            clang_args: vec![],
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LinkType {
    Default,
    Static,
    Framework
}

#[derive(Debug, Clone)]
pub struct Bindings {
    module: ast::Mod,
    raw_lines: Vec<String>,
}

impl Bindings {
    /// Deprecated - use a `Builder` instead
    pub fn generate(options: BindgenOptions, span: Option<Span>) -> Result<Bindings, ()> {
        let span = span.unwrap_or(DUMMY_SP);

        let mut context = BindgenContext::new(options);
        parse(&mut context);

        let module = ast::Mod {
            inner: span,
            items: codegen::codegen(&mut context),
        };

        Ok(Bindings {
            module: module,
            raw_lines: context.options().raw_lines.clone(),
        })
    }

    pub fn into_ast(self) -> Vec<P<ast::Item>> {
        self.module.items
    }

    pub fn to_string(&self) -> String {
        let mut mod_str = vec![];
        {
            let ref_writer = Box::new(mod_str.by_ref()) as Box<Write>;
            self.write(ref_writer).expect("Could not write bindings to string");
        }
        String::from_utf8(mod_str).unwrap()
    }

    pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        let file = try!(OpenOptions::new().write(true).truncate(true).create(true).open(path));
        self.write(Box::new(file))
    }

    // https://github.com/Manishearth/rust-clippy/issues/740
    #[cfg_attr(feature = "clippy", allow(needless_lifetimes))]
    pub fn write<'a>(&self, mut writer: Box<Write + 'a>) -> io::Result<()> {
        try!(writer.write("/* automatically generated by rust-bindgen */\n\n".as_bytes()));

        for line in self.raw_lines.iter() {
            try!(writer.write(line.as_bytes()));
            try!(writer.write("\n".as_bytes()));
        }
        if !self.raw_lines.is_empty() {
            try!(writer.write("\n".as_bytes()));
        }

        let mut ps = pprust::rust_printer(writer);
        try!(ps.print_mod(&self.module, &[]));
        try!(ps.print_remaining_comments());
        try!(eof(&mut ps.s));
        ps.s.out.flush()
    }
}

/// Determines whether the given cursor is in any of the files matched by the
/// options.
fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
    let (file, _, _, _) = cursor.location().location();

    match file.name() {
        None => ctx.options().builtins,
        Some(..) => true,
    }
}

pub fn parse_one(ctx: &mut BindgenContext,
                 cursor: clang::Cursor,
                 parent: Option<ItemId>,
                 children: &mut Vec<ItemId>) -> clangll::Enum_CXVisitorResult {
    if !filter_builtins(ctx, &cursor) {
        return CXChildVisit_Continue
    }

    use clangll::CXChildVisit_Continue;
    match Item::parse(cursor, parent, ctx) {
        Ok(id) => children.push(id),
        Err(ParseError::Continue) => {},
        Err(ParseError::Recurse) => {
            cursor.visit(|child, _| parse_one(ctx, *child, parent, children));
        }
    }
    CXChildVisit_Continue
}

fn parse(context: &mut BindgenContext) {
    use clang::Diagnostic;
    use clangll::*;

    for d in context.translation_unit().diags().iter() {
        let msg = d.format(Diagnostic::default_opts());
        let is_err = d.severity() >= CXDiagnostic_Error;
        println!("{}, err: {}", msg, is_err);
    }

    let cursor = context.translation_unit().cursor();
    if context.options().emit_ast {
        cursor.visit(|cur, _| clang::ast_dump(cur, 0));
    }

    let root = context.root_module();
    context.with_module(root, |context, children| {
        cursor.visit(|cursor, _| parse_one(context, *cursor, None, children))
    });

    assert!(context.current_module() == context.root_module(),
            "How did this happen?");
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;
use std::io::Read;

const LF: u8 = b'\n';
const CR: u8 = b'\r';
const QUOTE: u8 = b'"';
const COMMA: u8 = b',';

// Error messages
const UNEXPECTED_EOL: &str = "Unexpected end of line";
const UNEXPECTED_CHAR: &str = "Unexpected character after quote";
const EXPECTED_LF: &str = "Expected linefeed after carriage return";

enum CSVState {
    Start,
    NonQuotedValue,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, &'static str>;

trait ParseByte {
    fn parse_byte(&self, byte: u8) -> CSVResult;
}

impl ParseByte for CSVState {
    fn parse_byte(&self, byte: u8) -> CSVResult {
        match (self, byte) {
            (CSVState::Start, QUOTE) => Ok(CSVState::QuotedValue),
            (CSVState::Start, COMMA) => Ok(CSVState::Start),
            (CSVState::Start, _) => Ok(CSVState::NonQuotedValue),

            (CSVState::NonQuotedValue, COMMA) => Ok(CSVState::Start),
            (CSVState::NonQuotedValue, LF) => Ok(CSVState::Start),
            (CSVState::NonQuotedValue, CR) => Ok(CSVState::ExpectLF),
            (CSVState::NonQuotedValue, _) => Ok(CSVState::NonQuotedValue),

            (CSVState::QuotedValue, QUOTE) => Ok(CSVState::QuoteQuote),
            (CSVState::QuotedValue, CR) => Err(UNEXPECTED_EOL),
            (CSVState::QuotedValue, LF) => Err(UNEXPECTED_EOL),
            (CSVState::QuotedValue, _) => Ok(CSVState::QuotedValue),

            (CSVState::QuoteQuote, QUOTE) => Ok(CSVState::QuotedValue),
            (CSVState::QuoteQuote, COMMA) => Ok(CSVState::Start),
            (CSVState::QuoteQuote, LF) => Ok(CSVState::Start),
            (CSVState::QuoteQuote, CR) => Ok(CSVState::ExpectLF),
            (CSVState::QuoteQuote, _) => Err(UNEXPECTED_CHAR),

            (CSVState::ExpectLF, LF) => Ok(CSVState::Start),
            (CSVState::ExpectLF, _) => Err(EXPECTED_LF),

            (CSVState::Error, LF) => Ok(CSVState::Start),
            (CSVState::Error, _) => Ok(CSVState::Error),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: u32,
    pub col: u32,
    pub text: &'static str,
}

impl fmt::Display for CSVError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{} {}", self.line, self.col, self.text)
    }
}

pub fn csv_report<'a>(reader: &'a mut Read) -> impl Iterator<Item = CSVError> + 'a {
    let mut line = 1;
    let mut col = 0;
    let mut state = CSVState::Start;

    reader.bytes().filter_map(move |b| {
        let byte = b.unwrap();

        match state.parse_byte(byte) {
            Ok(new_state) => {
                state = new_state;
                if byte == LF {
                    line += 1;
                    col = 0;
                } else {
                    col += 1;
                }
                None
            }
            Err(error) => {
                let err = CSVError {
                    line: line,
                    col: col,
                    text: error,
                };
                if byte == LF {
                    line += 1;
                    col = 0;
                } else {
                    col += 1;
                };
                state = match error {
                    UNEXPECTED_EOL => CSVState::Start,
                    _ => CSVState::Error,
                };
                Some(err)
            }
        }
    })
}

#[cfg(test)]
mod tests {
    use super::{csv_report, CSVError, UNEXPECTED_CHAR, UNEXPECTED_EOL};

    #[test]
    fn finds_errors_in_csv() {
        use std::error::Error;
        use std::fs::File;

        let mut file = match File::open("tests/test.csv") {
            Err(why) => panic!("couldn't open: {}", Error::description(&why)),
            Ok(file) => file,
        };

        let mut report = csv_report(&mut file);

        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 2,
                col: 13,
                text: UNEXPECTED_CHAR,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 3,
                col: 28,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 5,
                col: 31,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 8,
                col: 14,
                text: UNEXPECTED_CHAR,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 11,
                col: 39,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 12,
                col: 28,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(report.next(), None);
    }
}
<commit_msg>The ParseByte trait was an unnecessary diversion<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;
use std::io::Read;

const LF: u8 = b'\n';
const CR: u8 = b'\r';
const QUOTE: u8 = b'"';
const COMMA: u8 = b',';

// Error messages
const UNEXPECTED_EOL: &str = "Unexpected end of line";
const UNEXPECTED_CHAR: &str = "Unexpected character after quote";
const EXPECTED_LF: &str = "Expected linefeed after carriage return";

enum CSVState {
    Start,
    NonQuotedValue,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, &'static str>;

impl CSVState {
    fn parse_byte(&self, byte: u8) -> CSVResult {
        match (self, byte) {
            (CSVState::Start, QUOTE) => Ok(CSVState::QuotedValue),
            (CSVState::Start, COMMA) => Ok(CSVState::Start),
            (CSVState::Start, _) => Ok(CSVState::NonQuotedValue),

            (CSVState::NonQuotedValue, COMMA) => Ok(CSVState::Start),
            (CSVState::NonQuotedValue, LF) => Ok(CSVState::Start),
            (CSVState::NonQuotedValue, CR) => Ok(CSVState::ExpectLF),
            (CSVState::NonQuotedValue, _) => Ok(CSVState::NonQuotedValue),

            (CSVState::QuotedValue, QUOTE) => Ok(CSVState::QuoteQuote),
            (CSVState::QuotedValue, CR) => Err(UNEXPECTED_EOL),
            (CSVState::QuotedValue, LF) => Err(UNEXPECTED_EOL),
            (CSVState::QuotedValue, _) => Ok(CSVState::QuotedValue),

            (CSVState::QuoteQuote, QUOTE) => Ok(CSVState::QuotedValue),
            (CSVState::QuoteQuote, COMMA) => Ok(CSVState::Start),
            (CSVState::QuoteQuote, LF) => Ok(CSVState::Start),
            (CSVState::QuoteQuote, CR) => Ok(CSVState::ExpectLF),
            (CSVState::QuoteQuote, _) => Err(UNEXPECTED_CHAR),

            (CSVState::ExpectLF, LF) => Ok(CSVState::Start),
            (CSVState::ExpectLF, _) => Err(EXPECTED_LF),

            (CSVState::Error, LF) => Ok(CSVState::Start),
            (CSVState::Error, _) => Ok(CSVState::Error),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: u32,
    pub col: u32,
    pub text: &'static str,
}

impl fmt::Display for CSVError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{} {}", self.line, self.col, self.text)
    }
}

pub fn csv_report<'a>(reader: &'a mut Read) -> impl Iterator<Item = CSVError> + 'a {
    let mut line = 1;
    let mut col = 0;
    let mut state = CSVState::Start;

    reader.bytes().filter_map(move |b| {
        let byte = b.unwrap();

        match state.parse_byte(byte) {
            Ok(new_state) => {
                state = new_state;
                if byte == LF {
                    line += 1;
                    col = 0;
                } else {
                    col += 1;
                }
                None
            }
            Err(error) => {
                let err = CSVError {
                    line: line,
                    col: col,
                    text: error,
                };
                if byte == LF {
                    line += 1;
                    col = 0;
                } else {
                    col += 1;
                };
                state = match error {
                    UNEXPECTED_EOL => CSVState::Start,
                    _ => CSVState::Error,
                };
                Some(err)
            }
        }
    })
}

#[cfg(test)]
mod tests {
    use super::{csv_report, CSVError, UNEXPECTED_CHAR, UNEXPECTED_EOL};

    #[test]
    fn finds_errors_in_csv() {
        use std::error::Error;
        use std::fs::File;

        let mut file = match File::open("tests/test.csv") {
            Err(why) => panic!("couldn't open: {}", Error::description(&why)),
            Ok(file) => file,
        };

        let mut report = csv_report(&mut file);

        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 2,
                col: 13,
                text: UNEXPECTED_CHAR,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 3,
                col: 28,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 5,
                col: 31,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 8,
                col: 14,
                text: UNEXPECTED_CHAR,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 11,
                col: 39,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(
            report.next(),
            Some(CSVError {
                line: 12,
                col: 28,
                text: UNEXPECTED_EOL,
            })
        );
        assert_eq!(report.next(), None);
    }
}
<|endoftext|>


<commit_before>extern crate libc;
#[macro_use] extern crate bitflags;

// #[macro_use]
// extern crate va_list;

#[macro_use] pub mod imgui;
pub use imgui::structs::*;
pub use imgui::enums::*;
pub use imgui::api::*;

pub use imgui::raw::types::{
	ImU32,
	ImWchar,
	ImTextureID,
	ImGuiID,
	ImDrawIdx,
	ImGuiTextEditCallback,
	ImGuiSizeConstraintCallback,
	ImDrawCallback
};

pub fn vec2(x: f32, y: f32) -> ImVec2 {
	ImVec2::new(x, y)
}

pub fn vec4(x: f32, y: f32, z: f32, w: f32) -> ImVec4 {
	ImVec4::new(x, y, z, w)
}

macro_rules! c_string {
	($e:tt) => ({
		concat!($e, "\0")
	})
}

#[cfg(test)]
mod tests {
	use super::imgui::api as imgui;
	use std::ffi::CStr;

	#[test]
	fn filename_check() {
		let io = imgui::get_io();
		let ini_filename = unsafe { CStr::from_ptr(io.ini_filename) };
		let log_filename = unsafe { CStr::from_ptr(io.log_filename) };
		assert_eq!(ini_filename.to_str(), Ok("imgui.ini"));
		assert_eq!(log_filename.to_str(), Ok("imgui_log.txt"));
		imgui::shutdown();
	}

	#[test]
	fn version_check() {
		let version = imgui::get_version();
		assert_eq!(version.to_str(), Ok("1.48")); // Will break whenever the cimgui version changes.
		imgui::shutdown();
	}
}
<commit_msg>Some cleanup<commit_after>extern crate libc;
#[macro_use]
extern crate bitflags;

#[macro_use]
pub mod imgui;

pub use imgui::structs::*;
pub use imgui::enums::*;
pub use imgui::api::*;

pub use imgui::raw::types::{
	ImU32,
	ImWchar,
	ImTextureID,
	ImGuiID,
	ImDrawIdx,
	ImGuiTextEditCallback,
	ImGuiSizeConstraintCallback,
	ImDrawCallback
};

pub fn vec2(x: f32, y: f32) -> ImVec2 {
	ImVec2::new(x, y)
}

pub fn vec4(x: f32, y: f32, z: f32, w: f32) -> ImVec4 {
	ImVec4::new(x, y, z, w)
}

macro_rules! c_string {
	($e:tt) => ({
		concat!($e, "\0")
	})
}

#[cfg(test)]
mod tests {
	use super::imgui::api as imgui;
	use std::ffi::CStr;

	#[test]
	fn filename_check() {
		let io = imgui::get_io();
		let ini_filename = unsafe { CStr::from_ptr(io.ini_filename) };
		let log_filename = unsafe { CStr::from_ptr(io.log_filename) };
		assert_eq!(ini_filename.to_str(), Ok("imgui.ini"));
		assert_eq!(log_filename.to_str(), Ok("imgui_log.txt"));
		imgui::shutdown();
	}

	#[test]
	fn version_check() {
		let version = imgui::get_version();
		assert_eq!(version.to_str(), Ok("1.48")); // Will break whenever the cimgui version changes.
		imgui::shutdown();
	}
}
<|endoftext|>