<commit_before>//
//  rss reader - main program
//
//
extern crate hyper;
extern crate xml;
extern crate chrono;
use std::convert;
use std::io;
use std::io::Read;
use std::fmt;
use std::env;

//
//  xml::Element utility functions.
//  These should be in the XML module.
//
//
//  find_all --  find all elements for which test function returns true.
//
//  Someday this will accept unboxed lambdas for testfn, but that's not working yet in Rust 1.0 pre-alpha
//
pub fn find_all<'a>(tree: &'a xml::Element, testfn: fn(&xml::Element) -> bool, finds: &mut Vec<&'a xml::Element>, recurse: bool) {
    if testfn(tree)                                         // if match
    {   finds.push(tree);                                   // save if match on name
        if recurse == false { return }                      // don't explore within finds if non-recursive
    }
    for child in tree.children.iter() {                     // for all children
        match *child {                                      // child is an Xml enum
            xml::Xml::ElementNode(ref childelt) => { find_all(childelt, testfn, finds, recurse ); }, // do ElementNode recursively
            _ => ()                                         // ignore non ElementNode children
        }                                                   // end match child
    }
}
//
//  find_all_text -- find all text below a tag.
//
pub fn find_all_text<'a>(tree: &'a xml::Element, s: &mut String, recurse: bool) {
    for child in tree.children.iter() {                     // for all children
        match *child {                                      // child is an Xml enum
            xml::Xml::ElementNode(ref childelt) => {
                if recurse {
                    find_all_text(childelt, s, recurse);    // do ElementNode recursively
                    }
                },
            xml::Xml::CharacterNode(ref text) => { s.push_str(&text) }  // append text
            _ => ()                                         // ignore non ElementNode children
        }                                                   // end match child
    }
}
//
//  find_tag_text -- find text of single tag for which test function returns true.
//
//  No find, or multiple finds. return an empty string.
//
//  Yes, it's somewhat inefficient to create a Vec and collect all the items to find only one.
//
pub fn find_tag_text(tree: &xml::Element, testfn: fn(&xml::Element) -> bool, s: &mut String) {
    let mut itemelts = Vec::<&xml::Element>::new();         // accumulate tag elements
    find_all(tree, testfn, &mut itemelts, false);           // find item elts
    if itemelts.len() != 1 { return }                       // should be a singleton
    find_all_text(itemelts[0], s, false);                   // find text
}

//
//  Return type and its error handling
//
pub type FeedResult<T> = Result<T, FeedError>;

/// A set of errors that can occur handling RSS or Atom feeds.
//#[derive(Debug, PartialEq, Clone)] // crank out default functions
pub enum FeedError {
    /// Error detected at the I/O level
    FeedIoError(io::Error),
    /// Error detected at the HTTP level
    FeedHTTPError(hyper::HttpError),
    /// Error detected at the XML parsing level
    FeedXMLParseError(xml::BuilderError),
    /// Error detected at the date parsing level
    FeedDateParseError(chrono::format::ParseError),
    /// XML, but feed type not recognized,
    FeedUnknownFeedTypeError,
    /// Required feed field missing or invalid
    FeedFieldError(String),
    /// Got an HTML page instead of an XML page
    FeedWasHTMLError(String)
}
//
//  Encapsulate errors from each of the lower level error types
//
impl convert::From<hyper::HttpError> for FeedError {
    fn from(err: hyper::HttpError) -> FeedError {
        FeedError::FeedHTTPError(err)
    }
}
impl convert::From<io::Error> for FeedError {
    fn from(err: io::Error) -> FeedError {
        FeedError::FeedIoError(err)
    }
}

impl convert::From<xml::BuilderError> for FeedError {
    fn from(err: xml::BuilderError) -> FeedError {
        FeedError::FeedXMLParseError(err)
    }
}

impl convert::From<chrono::format::ParseError> for FeedError {
    fn from(err: chrono::format::ParseError) -> FeedError {
        FeedError::FeedDateParseError(err)
    }
}

impl fmt::Display for FeedError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FeedError::FeedIoError(ref xerr) => xerr.fmt(f),   // I/O error
            &FeedError::FeedHTTPError(ref xerr) => xerr.fmt(f), // HTTP error
            &FeedError::FeedXMLParseError(ref xerr) => xerr.fmt(f), // XML parse error
            &FeedError::FeedDateParseError(ref xerr) => xerr.fmt(f), // Date parse error
            &FeedError::FeedUnknownFeedTypeError => write!(f, "Unknown feed type."),
            &FeedError::FeedFieldError(ref s) => write!(f, "Required field \"{}\" missing from RSS/Atom feed.", s),
            &FeedError::FeedWasHTMLError(ref s) => write!(f, "Expected an RSS/ATOM feed but received a web page \"{}\".", s)
            //_(ref xerr) => xerr.fmt(f) // would be convenient, but not allowed in Rust.
        }
    }
}

//
//  FeedChannel --  an RSS or Atom channel
//
pub struct FeedChannel {
    title: String,                                          // channel title
    link: String,                                           // URL of channel (from RSS channel)
    description: String,                                    // channel description if present
    }
//
//  FeedItem - a RSS or Atom feed item
//
pub struct FeedItem {
    title: String,                                          // the title
    description: String,                                    // the description, i.e. content, as HTML
    author: String,                                         // name of author
    pubdate: chrono::DateTime<chrono::FixedOffset>          // publication date
    }
//
//  FeedReply -- data returned from a feed
//
pub struct FeedReply {
    channel: FeedChannel,                                   // channel info
    items: Vec<FeedItem>                                    // items found
    }

impl FeedItem {
    pub fn dump(&self){                                     // dump content for debug
        println!("Feed Item");
        println!(" Title: {}", self.title);
        println!(" Author: {}", self.author);
        println!(" Publication date: {}", self.pubdate);
        println!(" Description: {}", self.description);
        println!("");
    }
}

impl FeedChannel {
    pub fn new() -> FeedChannel {
        FeedChannel{title: String::new(), link: String::new(), description: String::new()}
    }

    pub fn dump(&self) {
        println!("Feed Channel");
        println!(" Title: {}", self.title);
        println!(" Link: {}", self.link);
        println!(" Description: {}", self.description);
        println!("");
    }
}

impl FeedReply {
    pub fn new() -> FeedReply {
        FeedReply{channel: FeedChannel::new(), items: Vec::<FeedItem>::new()}
    }

    pub fn dump(&self) {
        self.channel.dump();
        for item in self.items.iter() {
            item.dump()
        }
    }
}
//
//  handletree  --  handle a tree generated by the XML parser
//
//  May be RSS, Atom, or HTML.
//
//  HTML is an error, usually indicating some problem with a firewall or the site.
//
pub fn handletree(tree: &xml::Element, reply: &mut FeedReply) -> FeedResult<()> {
                                                            // ***MORE***
    handlersstree(tree, reply)                              // for now, just RSS
}
//
//  handlersstree -- handle a tree of RSS items
//
pub fn handlersstree(tree: &xml::Element, reply: &mut FeedReply) -> FeedResult<()> {
    //println!("Tree: {}\n\n", tree);                       // ***TEMP***
    //  ***NEED TO COLLECT CHANNEL INFO***
    let mut itemelts = Vec::<&xml::Element>::new();         // accumulate ITEM entries
    fn isitem(e: &xml::Element) -> bool { e.name == "item" };// someday this will be an unboxed lambda
    find_all(tree, isitem, &mut itemelts, false);           // find all items
    for itemelt in itemelts.iter() {                        // extract important fields from items
        let mut authorstr = String::new();                  // title string
        fn isauthor(e: &xml::Element) -> bool { e.name == "author" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, isauthor, &mut authorstr);

        let mut pubdatestr = String::new();                 // date string
        fn ispubdate(e: &xml::Element) -> bool { e.name == "pubDate" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, ispubdate, &mut pubdatestr);
        let pubtimestamp = match chrono::DateTime::parse_from_rfc2822(&pubdatestr) {
            Ok(date) => date,
            Err(xerr) => return Err(convert::From::from(xerr))
        };

        let mut titlestr = String::new();                   // title string
        fn istitle(e: &xml::Element) -> bool { e.name == "title" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, istitle, &mut titlestr);

        let mut descriptionstr = String::new();             // description string
        fn isdescription(e: &xml::Element) -> bool { e.name == "description" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, isdescription, &mut descriptionstr);

        let feeditem = FeedItem{title: titlestr, description: descriptionstr, author: authorstr, pubdate: pubtimestamp};
        reply.items.push(feeditem);
    }
    ////println!("Item: {}", item);
    Ok(())
}
//
//  readfeed -- read from an RSS or Atom feed.
//
pub fn readfeed(url: &str, reply: &mut FeedReply, verbose: bool) -> FeedResult<()> {
    let mut client = hyper::Client::new();                  // create HTTP client from Hyper crate.
    let mut res = match client.get(url).send() {            // HTTP GET request
        Ok(res) => res,
        Err(xerr) => return Err(convert::From::from(xerr)) // fails, return error
    };
    //  Successful read of URL.  Have valid result object.
    if verbose {
        println!("Response: {}", res.status);               // debug print
        println!("Headers:\n{}", res.headers);
    }
    let mut p = xml::Parser::new();                         // get XML parser
    let mut e = xml::ElementBuilder::new();                 // get tree builder
    let mut ws = String::new();                             // get work string for XML
    match res.read_to_string(&mut ws) {                     // read the XML
        Ok(_) => (),                                        // OK, keep going
        Err(xerr) => { return Err(convert::From::from(xerr)) }   // I/O error
    };
    p.feed_str(&ws[..]);                                    // prepare XML parser
    for event in p {                                        // for each full XML tree (normally only one)
        match e.handle_event(event) {                       // process the tree
            Some(rep) => {                                  // returns an Option wrapped around a Result
                match rep {
                    Ok(ev) => match handletree(&ev, reply) {// we have an XML tree to process
                        Ok(()) => (),
                        Err(xerr) => return Err(convert::From::from(xerr))   // RSS/Atom error
                        },                                  // end match handletree
                    Err(xerr) => return Err(convert::From::from(xerr))
                    }                                       // end match rep
                }                                           // end some rep
            None => ()                                      // no full tree yet, keep going
        }                                                   // end match handle_event
    }                                                       // end for
    Ok(())
}

//
//  test1  -- temporary test function
//
//  Ask for URL of RSS feed, handle it.
//
pub fn test1() {
    let args : Vec<_> = env::args().collect();      // command line args as vector of strings
    match args.len() {
        2 => (),
        _ => {
            println!("Usage: client <url>");
            return;
        }
    };
    let url = &*args[1];
    println!("Reading \"{}\"", url);
    let mut result = FeedReply::new();            // accumulate ITEM entries
    let res = readfeed(url, &mut result, true);
    match res {
        Ok(_) => println!("OK."),
        Err(err) => println!("Error: {}", err)
        }
    result.dump();
}
//
//  Unit tests.
//
#[test]
fn testreutersrss() {
    let url = "http://feeds.reuters.com/reuters/topNews?format=xml";    // ***TEMP***
    let mut freply = FeedReply::new();            // accumulate ITEM entries
    let res = readfeed(url, &mut freply, true);
    match res {
        Ok(_) => println!("OK."),
        Err(err) => panic!("Error: {}", err)
        }
    freply.dump();
}

<commit_msg>Deal with breaking change in Hyper crate. Ref: https://github.com/hyperium/hyper/commit/9ba074d150a55a749161317405fe8b28253c5a9d<commit_after>//
//  rss reader - main program
//
//
extern crate hyper;
extern crate xml;
extern crate chrono;
use std::convert;
use std::io;
use std::io::Read;
use std::fmt;
use std::env;

//
//  xml::Element utility functions.
//  These should be in the XML module.
//
//
//  find_all --  find all elements for which test function returns true.
//
//  Someday this will accept unboxed lambdas for testfn, but that's not working yet in Rust 1.0 pre-alpha
//
pub fn find_all<'a>(tree: &'a xml::Element, testfn: fn(&xml::Element) -> bool, finds: &mut Vec<&'a xml::Element>, recurse: bool) {
    if testfn(tree)                                         // if match
    {   finds.push(tree);                                   // save if match on name
        if recurse == false { return }                      // don't explore within finds if non-recursive
    }
    for child in tree.children.iter() {                     // for all children
        match *child {                                      // child is an Xml enum
            xml::Xml::ElementNode(ref childelt) => { find_all(childelt, testfn, finds, recurse ); }, // do ElementNode recursively
            _ => ()                                         // ignore non ElementNode children
        }                                                   // end match child
    }
}
//
//  find_all_text -- find all text below a tag.
//
pub fn find_all_text<'a>(tree: &'a xml::Element, s: &mut String, recurse: bool) {
    for child in tree.children.iter() {                     // for all children
        match *child {                                      // child is an Xml enum
            xml::Xml::ElementNode(ref childelt) => {
                if recurse {
                    find_all_text(childelt, s, recurse);    // do ElementNode recursively
                    }
                },
            xml::Xml::CharacterNode(ref text) => { s.push_str(&text) }  // append text
            _ => ()                                         // ignore non ElementNode children
        }                                                   // end match child
    }
}
//
//  find_tag_text -- find text of single tag for which test function returns true.
//
//  No find, or multiple finds. return an empty string.
//
//  Yes, it's somewhat inefficient to create a Vec and collect all the items to find only one.
//
pub fn find_tag_text(tree: &xml::Element, testfn: fn(&xml::Element) -> bool, s: &mut String) {
    let mut itemelts = Vec::<&xml::Element>::new();         // accumulate tag elements
    find_all(tree, testfn, &mut itemelts, false);           // find item elts
    if itemelts.len() != 1 { return }                       // should be a singleton
    find_all_text(itemelts[0], s, false);                   // find text
}

//
//  Return type and its error handling
//
pub type FeedResult<T> = Result<T, FeedError>;

/// A set of errors that can occur handling RSS or Atom feeds.
//#[derive(Debug, PartialEq, Clone)] // crank out default functions
pub enum FeedError {
    /// Error detected at the I/O level
    FeedIoError(io::Error),
    /// Error detected at the HTTP level
    FeedHTTPError(hyper::Error),
    /// Error detected at the XML parsing level
    FeedXMLParseError(xml::BuilderError),
    /// Error detected at the date parsing level
    FeedDateParseError(chrono::format::ParseError),
    /// XML, but feed type not recognized,
    FeedUnknownFeedTypeError,
    /// Required feed field missing or invalid
    FeedFieldError(String),
    /// Got an HTML page instead of an XML page
    FeedWasHTMLError(String)
}
//
//  Encapsulate errors from each of the lower level error types
//
impl convert::From<hyper::Error> for FeedError {
    fn from(err: hyper::Error) -> FeedError {
        FeedError::FeedHTTPError(err)
    }
}
impl convert::From<io::Error> for FeedError {
    fn from(err: io::Error) -> FeedError {
        FeedError::FeedIoError(err)
    }
}

impl convert::From<xml::BuilderError> for FeedError {
    fn from(err: xml::BuilderError) -> FeedError {
        FeedError::FeedXMLParseError(err)
    }
}

impl convert::From<chrono::format::ParseError> for FeedError {
    fn from(err: chrono::format::ParseError) -> FeedError {
        FeedError::FeedDateParseError(err)
    }
}

impl fmt::Display for FeedError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FeedError::FeedIoError(ref xerr) => xerr.fmt(f),   // I/O error
            &FeedError::FeedHTTPError(ref xerr) => xerr.fmt(f), // HTTP error
            &FeedError::FeedXMLParseError(ref xerr) => xerr.fmt(f), // XML parse error
            &FeedError::FeedDateParseError(ref xerr) => xerr.fmt(f), // Date parse error
            &FeedError::FeedUnknownFeedTypeError => write!(f, "Unknown feed type."),
            &FeedError::FeedFieldError(ref s) => write!(f, "Required field \"{}\" missing from RSS/Atom feed.", s),
            &FeedError::FeedWasHTMLError(ref s) => write!(f, "Expected an RSS/ATOM feed but received a web page \"{}\".", s)
            //_(ref xerr) => xerr.fmt(f) // would be convenient, but not allowed in Rust.
        }
    }
}

//
//  FeedChannel --  an RSS or Atom channel
//
pub struct FeedChannel {
    title: String,                                          // channel title
    link: String,                                           // URL of channel (from RSS channel)
    description: String,                                    // channel description if present
    }
//
//  FeedItem - a RSS or Atom feed item
//
pub struct FeedItem {
    title: String,                                          // the title
    description: String,                                    // the description, i.e. content, as HTML
    author: String,                                         // name of author
    pubdate: chrono::DateTime<chrono::FixedOffset>          // publication date
    }
//
//  FeedReply -- data returned from a feed
//
pub struct FeedReply {
    channel: FeedChannel,                                   // channel info
    items: Vec<FeedItem>                                    // items found
    }

impl FeedItem {
    pub fn dump(&self){                                     // dump content for debug
        println!("Feed Item");
        println!(" Title: {}", self.title);
        println!(" Author: {}", self.author);
        println!(" Publication date: {}", self.pubdate);
        println!(" Description: {}", self.description);
        println!("");
    }
}

impl FeedChannel {
    pub fn new() -> FeedChannel {
        FeedChannel{title: String::new(), link: String::new(), description: String::new()}
    }

    pub fn dump(&self) {
        println!("Feed Channel");
        println!(" Title: {}", self.title);
        println!(" Link: {}", self.link);
        println!(" Description: {}", self.description);
        println!("");
    }
}

impl FeedReply {
    pub fn new() -> FeedReply {
        FeedReply{channel: FeedChannel::new(), items: Vec::<FeedItem>::new()}
    }

    pub fn dump(&self) {
        self.channel.dump();
        for item in self.items.iter() {
            item.dump()
        }
    }
}
//
//  handletree  --  handle a tree generated by the XML parser
//
//  May be RSS, Atom, or HTML.
//
//  HTML is an error, usually indicating some problem with a firewall or the site.
//
pub fn handletree(tree: &xml::Element, reply: &mut FeedReply) -> FeedResult<()> {
                                                            // ***MORE***
    handlersstree(tree, reply)                              // for now, just RSS
}
//
//  handlersstree -- handle a tree of RSS items
//
pub fn handlersstree(tree: &xml::Element, reply: &mut FeedReply) -> FeedResult<()> {
    //println!("Tree: {}\n\n", tree);                       // ***TEMP***
    //  ***NEED TO COLLECT CHANNEL INFO***
    let mut itemelts = Vec::<&xml::Element>::new();         // accumulate ITEM entries
    fn isitem(e: &xml::Element) -> bool { e.name == "item" };// someday this will be an unboxed lambda
    find_all(tree, isitem, &mut itemelts, false);           // find all items
    for itemelt in itemelts.iter() {                        // extract important fields from items
        let mut authorstr = String::new();                  // title string
        fn isauthor(e: &xml::Element) -> bool { e.name == "author" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, isauthor, &mut authorstr);

        let mut pubdatestr = String::new();                 // date string
        fn ispubdate(e: &xml::Element) -> bool { e.name == "pubDate" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, ispubdate, &mut pubdatestr);
        let pubtimestamp = match chrono::DateTime::parse_from_rfc2822(&pubdatestr) {
            Ok(date) => date,
            Err(xerr) => return Err(convert::From::from(xerr))
        };

        let mut titlestr = String::new();                   // title string
        fn istitle(e: &xml::Element) -> bool { e.name == "title" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, istitle, &mut titlestr);

        let mut descriptionstr = String::new();             // description string
        fn isdescription(e: &xml::Element) -> bool { e.name == "description" }; // someday this will be an unboxed lambda
        find_tag_text(*itemelt, isdescription, &mut descriptionstr);

        let feeditem = FeedItem{title: titlestr, description: descriptionstr, author: authorstr, pubdate: pubtimestamp};
        reply.items.push(feeditem);
    }
    ////println!("Item: {}", item);
    Ok(())
}
//
//  readfeed -- read from an RSS or Atom feed.
//
pub fn readfeed(url: &str, reply: &mut FeedReply, verbose: bool) -> FeedResult<()> {
    let mut client = hyper::Client::new();                  // create HTTP client from Hyper crate.
    let mut res = match client.get(url).send() {            // HTTP GET request
        Ok(res) => res,
        Err(xerr) => return Err(convert::From::from(xerr)) // fails, return error
    };
    //  Successful read of URL.  Have valid result object.
    if verbose {
        println!("Response: {}", res.status);               // debug print
        println!("Headers:\n{}", res.headers);
    }
    let mut p = xml::Parser::new();                         // get XML parser
    let mut e = xml::ElementBuilder::new();                 // get tree builder
    let mut ws = String::new();                             // get work string for XML
    match res.read_to_string(&mut ws) {                     // read the XML
        Ok(_) => (),                                        // OK, keep going
        Err(xerr) => { return Err(convert::From::from(xerr)) }   // I/O error
    };
    p.feed_str(&ws[..]);                                    // prepare XML parser
    for event in p {                                        // for each full XML tree (normally only one)
        match e.handle_event(event) {                       // process the tree
            Some(rep) => {                                  // returns an Option wrapped around a Result
                match rep {
                    Ok(ev) => match handletree(&ev, reply) {// we have an XML tree to process
                        Ok(()) => (),
                        Err(xerr) => return Err(convert::From::from(xerr))   // RSS/Atom error
                        },                                  // end match handletree
                    Err(xerr) => return Err(convert::From::from(xerr))
                    }                                       // end match rep
                }                                           // end some rep
            None => ()                                      // no full tree yet, keep going
        }                                                   // end match handle_event
    }                                                       // end for
    Ok(())
}

//
//  test1  -- temporary test function
//
//  Ask for URL of RSS feed, handle it.
//
pub fn test1() {
    let args : Vec<_> = env::args().collect();      // command line args as vector of strings
    match args.len() {
        2 => (),
        _ => {
            println!("Usage: client <url>");
            return;
        }
    };
    let url = &*args[1];
    println!("Reading \"{}\"", url);
    let mut result = FeedReply::new();            // accumulate ITEM entries
    let res = readfeed(url, &mut result, true);
    match res {
        Ok(_) => println!("OK."),
        Err(err) => println!("Error: {}", err)
        }
    result.dump();
}
//
//  Unit tests.
//
#[test]
fn testreutersrss() {
    let url = "http://feeds.reuters.com/reuters/topNews?format=xml";    // ***TEMP***
    let mut freply = FeedReply::new();            // accumulate ITEM entries
    let res = readfeed(url, &mut freply, true);
    match res {
        Ok(_) => println!("OK."),
        Err(err) => panic!("Error: {}", err)
        }
    freply.dump();
}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate toml;
extern crate toml_query;

extern crate libimagbookmark;
extern crate libimagrt;
extern crate libimagerror;
extern crate libimagutil;

use std::process::exit;

use toml::Value;
use toml_query::read::TomlValueReadExt;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagbookmark::collection::BookmarkCollection;
use libimagbookmark::link::Link as BookmarkLink;
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagutil::info_result::*;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-bookmark",
                                    &version!()[..],
                                    "Bookmark collection tool",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "add"        => add(&rt),
                "collection" => collection(&rt),
                "list"       => list(&rt),
                "remove"     => remove(&rt),
                _            => {
                    debug!("Unknown command"); // More error handling
                },
            }
        });
}

fn add(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("add").unwrap();
    let coll = get_collection_name(rt, "add", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .and_then(|mut collection| {
            for url in scmd.values_of("urls").unwrap() { // unwrap saved by clap
                let _ = try!(collection.add_link(BookmarkLink::from(url)));
            }
            Ok(())
        })
        .map_err_trace()
        .map_info_str("Ready")
        .ok();
}

fn collection(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("collection").unwrap();

    if scmd.is_present("add") { // adding a new collection
        let name = scmd.value_of("add").unwrap();
        if let Ok(_) = BookmarkCollection::new(rt.store(), &name) {
            info!("Created: {}", name);
        } else {
            warn!("Creating collection {} failed", name);
            exit(1);
        }
    }

    if scmd.is_present("remove") { // remove a collection
        let name = scmd.value_of("remove").unwrap();
        if let Ok(_) = BookmarkCollection::delete(rt.store(), &name) {
            info!("Deleted: {}", name);
        } else {
            warn!("Deleting collection {} failed", name);
            exit(1);
        }
    }
}

fn list(rt: &Runtime) {
    let coll = get_collection_name(rt, "list", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .map(|collection| {
            match collection.links() {
                Ok(links) => {
                    debug!("Listing...");
                    for (i, link) in links.enumerate() {
                        match link {
                            Ok(link) => println!("{: >3}: {}", i, link),
                            Err(e)   => trace_error(&e)
                        }
                    };
                    debug!("... ready with listing");
                },
                Err(e) => trace_error_exit(&e, 1),
            }
        })
        .ok();
    info!("Ready");
}

fn remove(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("remove").unwrap();
    let coll = get_collection_name(rt, "list", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .map(|mut collection| {
            for url in scmd.values_of("urls").unwrap() { // enforced by clap
                collection.remove_link(BookmarkLink::from(url)).map_err(|e| trace_error(&e)).ok();
            }
        })
        .ok();
    info!("Ready");
}

fn get_collection_name(rt: &Runtime,
                       subcommand_name: &str,
                       collection_argument_name: &str)
    -> String
{
    rt.cli()
        .subcommand_matches(subcommand_name)
        .and_then(|scmd| scmd.value_of(collection_argument_name).map(String::from))
        .unwrap_or_else(|| {
            rt.config()
                .map(|cfg| match cfg.read("bookmark.default_collection") {
                    Err(e) => trace_error_exit(&e, 1),
                    Ok(Some(&Value::String(ref name))) => name.clone(),
                    Ok(None) => {
                        error!("Missing config: 'bookmark.default_collection'. Set or use commandline to specify.");
                        exit(1)
                    },

                    Ok(Some(_)) => {
                        error!("Type error in configuration: 'bookmark.default_collection' should be string");
                        exit(1)
                    }

                })
                .unwrap_or_else(|| {
                    error!("Failed to read configuration");
                    exit(1)
                })
        })
}

<commit_msg>Replace uses of try!() macro with "?" operator<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate toml;
extern crate toml_query;

extern crate libimagbookmark;
extern crate libimagrt;
extern crate libimagerror;
extern crate libimagutil;

use std::process::exit;

use toml::Value;
use toml_query::read::TomlValueReadExt;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagbookmark::collection::BookmarkCollection;
use libimagbookmark::link::Link as BookmarkLink;
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagutil::info_result::*;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-bookmark",
                                    &version!()[..],
                                    "Bookmark collection tool",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "add"        => add(&rt),
                "collection" => collection(&rt),
                "list"       => list(&rt),
                "remove"     => remove(&rt),
                _            => {
                    debug!("Unknown command"); // More error handling
                },
            }
        });
}

fn add(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("add").unwrap();
    let coll = get_collection_name(rt, "add", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .and_then(|mut collection| {
            for url in scmd.values_of("urls").unwrap() { // unwrap saved by clap
                let _ = collection.add_link(BookmarkLink::from(url))?;
            }
            Ok(())
        })
        .map_err_trace()
        .map_info_str("Ready")
        .ok();
}

fn collection(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("collection").unwrap();

    if scmd.is_present("add") { // adding a new collection
        let name = scmd.value_of("add").unwrap();
        if let Ok(_) = BookmarkCollection::new(rt.store(), &name) {
            info!("Created: {}", name);
        } else {
            warn!("Creating collection {} failed", name);
            exit(1);
        }
    }

    if scmd.is_present("remove") { // remove a collection
        let name = scmd.value_of("remove").unwrap();
        if let Ok(_) = BookmarkCollection::delete(rt.store(), &name) {
            info!("Deleted: {}", name);
        } else {
            warn!("Deleting collection {} failed", name);
            exit(1);
        }
    }
}

fn list(rt: &Runtime) {
    let coll = get_collection_name(rt, "list", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .map(|collection| {
            match collection.links() {
                Ok(links) => {
                    debug!("Listing...");
                    for (i, link) in links.enumerate() {
                        match link {
                            Ok(link) => println!("{: >3}: {}", i, link),
                            Err(e)   => trace_error(&e)
                        }
                    };
                    debug!("... ready with listing");
                },
                Err(e) => trace_error_exit(&e, 1),
            }
        })
        .ok();
    info!("Ready");
}

fn remove(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("remove").unwrap();
    let coll = get_collection_name(rt, "list", "collection");

    BookmarkCollection::get(rt.store(), &coll)
        .map(|mut collection| {
            for url in scmd.values_of("urls").unwrap() { // enforced by clap
                collection.remove_link(BookmarkLink::from(url)).map_err(|e| trace_error(&e)).ok();
            }
        })
        .ok();
    info!("Ready");
}

fn get_collection_name(rt: &Runtime,
                       subcommand_name: &str,
                       collection_argument_name: &str)
    -> String
{
    rt.cli()
        .subcommand_matches(subcommand_name)
        .and_then(|scmd| scmd.value_of(collection_argument_name).map(String::from))
        .unwrap_or_else(|| {
            rt.config()
                .map(|cfg| match cfg.read("bookmark.default_collection") {
                    Err(e) => trace_error_exit(&e, 1),
                    Ok(Some(&Value::String(ref name))) => name.clone(),
                    Ok(None) => {
                        error!("Missing config: 'bookmark.default_collection'. Set or use commandline to specify.");
                        exit(1)
                    },

                    Ok(Some(_)) => {
                        error!("Type error in configuration: 'bookmark.default_collection' should be string");
                        exit(1)
                    }

                })
                .unwrap_or_else(|| {
                    error!("Failed to read configuration");
                    exit(1)
                })
        })
}

<|endoftext|>


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

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::ext::quote::{
   //expand_parse_call
};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row(raw: &str, expected_columns: Option<uint>) -> str::StrSplits {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let icx : &ExtCtxt = cx;  // an immutable borrow

   let raw_name   : Entry = entries.remove(0).expect("should be given a type name");
   let raw_path   : Entry = entries.remove(0).expect("should be given a CSV file path");
   let raw_labels : Entry = entries.remove(0).expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0     = interned_to_ident(labels.clone());

   println!("Parsing given labels...");

   let labels1 = format!("{}", labels);
   let labels_iter = parse_csv_row(labels1.as_slice(), None);

   println!("Defining items in the CSV provider...");

   // TODO: iterate through call columns
   let col = quote_item!(icx,
      pub $col0: String
   ).expect("column parsing");

   let item0 = quote_item!(icx,
      pub struct $MyCsvRow {
         $col,
      }
   );

   let item1: Option<Gc<syntax::ast::Item>> = quote_item!(icx,
      pub struct $MyCsv {
         pub data: Vec<$MyCsvRow>,
      }
   );

   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(icx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<commit_msg>cleanup<commit_after>#![crate_name="CSVProvider"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::ext::quote::{
   //expand_parse_call
};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row(raw: &str, expected_columns: Option<uint>) -> str::StrSplits {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let icx : &ExtCtxt = cx;  // an immutable borrow

   let raw_name   : Entry = entries.remove(0).expect("should be given a type name");
   let raw_path   : Entry = entries.remove(0).expect("should be given a CSV file path");
   let raw_labels : Entry = entries.remove(0).expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0     = interned_to_ident(labels.clone());

   println!("Parsing given labels...");

   let labels1 = format!("{}", labels);
   let labels_iter = parse_csv_row(labels1.as_slice(), None);

   println!("Defining items in the CSV provider...");

   // TODO: iterate through call columns
   let col = quote_item!(icx,
      pub $col0: String
   ).expect("Should be able to label a sample column (for use inside a struct def)");

   let item0 = quote_item!(icx,
      pub struct $MyCsvRow {
         $col,
      }
   );

   let item1: Option<Gc<syntax::ast::Item>> = quote_item!(icx,
      pub struct $MyCsv {
         pub data: Vec<$MyCsvRow>,
      }
   );

   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(icx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0     = interned_to_ident(labels.clone());

   println!("Parsing given labels...");

   let labels1 = format!("{}", labels);
   let labels_iter = parse_csv_row(labels1.as_slice(), None);

   println!("Defining items in the CSV provider...");

   // FIXME: Why is this fn necessary?
   let define_my_csv_row = |cx0 : &mut ExtCtxt| {

      // now, nested problems with cx
      let col = quote_item!(cx0, pub $col0: String).expect("column parsing");

      return quote_item!(cx0,
         pub struct $MyCsvRow {
            $col,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<commit_msg>a breakthrough?<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::ext::quote::{
   //expand_parse_call
};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0     = interned_to_ident(labels.clone());

   println!("Parsing given labels...");

   let labels1 = format!("{}", labels);
   let labels_iter = parse_csv_row(labels1.as_slice(), None);

   println!("Defining items in the CSV provider...");

/*
   fn mut_ref_cx<'a>(context: &'a mut ExtCtxt) -> &'a mut ExtCtxt {
        let &ExtCtxt(ref mut context_prime) = context;
        return context_prime;
   }
*/

/*
   pub fn expand_quote_item(
      cx: &ExtCtxt,
      sp: Span,
      tts: &[ast::TokenTree])
      -> Box<base::MacResult>
   {
      let expanded = expand_parse_call(cx, sp, "parse_item_with_outer_attributes", vec!(), tts);
      base::MacExpr::new(expanded)
   }
*/

   let define_my_csv_row = |cx0 : &mut ExtCtxt| {

      let cx1 : &ExtCtxt = cx0;

      let col = quote_item!(cx1, pub $col0: String).expect("column parsing");

      return quote_item!(cx1,
         pub struct $MyCsvRow {
            $col,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc;

// #[cfg(test)]
// extern crate time;
//
// #[cfg(test)]
// fn mark_time(i:usize, bounds: (usize, usize), s_time: time::PreciseTime){
//     #[cfg(test)]
//     println!("Thread: {}, Bounds:({}, {}), time: {}", i, bounds.0, bounds.1, s_time.to(time::PreciseTime::now()));
// }

fn spawn_worker<T, SeqF>(seq_fun: SeqF, tx: mpsc::Sender<(usize, Vec<T>)>, min: usize, max:usize, t:usize)
    where
        T: Clone + Send + 'static,
        SeqF: Send + Sync + Copy + 'static + Fn(usize, usize) -> Vec<T>,{

    thread::spawn( move || {
        let to_send = match max-min{
            0 => vec![],
            _ => seq_fun(min, max),
        };
        //
        // #[cfg(test)]
        // mark_time(i, (min, max), start);

        tx.send((t, to_send)).unwrap();
    });
}
pub fn to_concurrent_on_section<T, SeqF>(seq_fun: SeqF,
            min_num: usize, max_num: usize, threads: usize, from_zero_speed_factor: usize) -> Vec<T>
            where
                T: Clone + Send + 'static,
                SeqF: Send + Sync + Copy + 'static + Fn(usize, usize) -> Vec<T>, {

    let mut res_vec:  Vec<Vec<T>> = vec![vec![]; threads];
    let (start_seg_end, seg_size) = match min_num{
        0 => {
            let seg_size = (max_num)/(threads*from_zero_speed_factor);
            (max_num - seg_size*(threads-1), seg_size)
        },
        _ => {
            let range = max_num - min_num;
            let seg_size = range/threads;
            (min_num + seg_size + range%threads, seg_size)
        },
    };

    let (tx, rx) = mpsc::channel();
    //
    // #[cfg(test)]
    // let start = time::PreciseTime::now();

    spawn_worker(seq_fun, tx.clone(), min_num, start_seg_end, 0);
    for i in 1..threads{
        spawn_worker(seq_fun, tx.clone(),
                    start_seg_end + seg_size*(i-1), start_seg_end + seg_size*i,
                    i);
    }

    for _ in 0..threads{
        let (i, p_sec) = match rx.recv(){
            Ok(mes) => mes,
            Err(e) => panic!(e.to_string()),
        };
        println!("Primes in sec {}: {}", start_seg_end + seg_size*(i) , p_sec.len());
        res_vec[i] = p_sec;
    }
    res_vec.concat()
}
<commit_msg>Revert "What section is there a miscount?"<commit_after>use std::thread;
use std::sync::mpsc;

// #[cfg(test)]
// extern crate time;
//
// #[cfg(test)]
// fn mark_time(i:usize, bounds: (usize, usize), s_time: time::PreciseTime){
//     #[cfg(test)]
//     println!("Thread: {}, Bounds:({}, {}), time: {}", i, bounds.0, bounds.1, s_time.to(time::PreciseTime::now()));
// }

fn spawn_worker<T, SeqF>(seq_fun: SeqF, tx: mpsc::Sender<(usize, Vec<T>)>, min: usize, max:usize, t:usize)
    where
        T: Clone + Send + 'static,
        SeqF: Send + Sync + Copy + 'static + Fn(usize, usize) -> Vec<T>,{

    thread::spawn( move || {
        let to_send = match max-min{
            0 => vec![],
            _ => seq_fun(min, max),
        };
        //
        // #[cfg(test)]
        // mark_time(i, (min, max), start);

        tx.send((t, to_send)).unwrap();
    });
}
pub fn to_concurrent_on_section<T, SeqF>(seq_fun: SeqF,
            min_num: usize, max_num: usize, threads: usize, from_zero_speed_factor: usize) -> Vec<T>
            where
                T: Clone + Send + 'static,
                SeqF: Send + Sync + Copy + 'static + Fn(usize, usize) -> Vec<T>, {

    let mut res_vec:  Vec<Vec<T>> = vec![vec![]; threads];
    let (start_seg_end, seg_size) = match min_num{
        0 => {
            let seg_size = (max_num)/(threads*from_zero_speed_factor);
            (max_num - seg_size*(threads-1), seg_size)
        },
        _ => {
            let range = max_num - min_num;
            let seg_size = range/threads;
            (min_num + seg_size + range%threads, seg_size)
        },
    };

    let (tx, rx) = mpsc::channel();
    //
    // #[cfg(test)]
    // let start = time::PreciseTime::now();

    spawn_worker(seq_fun, tx.clone(), min_num, start_seg_end, 0);
    for i in 1..threads{
        spawn_worker(seq_fun, tx.clone(),
                    start_seg_end + seg_size*(i-1), start_seg_end + seg_size*i,
                    i);
    }

    for _ in 0..threads{
        let (i, p_sec) = match rx.recv(){
            Ok(mes) => mes,
            Err(e) => panic!(e.to_string()),
        };
        res_vec[i] = p_sec;
    }
    res_vec.concat()
}
<|endoftext|>


<commit_before>// DOCS

#![allow(unstable)]
#![feature(plugin)]
#[plugin] extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

pub struct Property {
    /// Parameters.
    pub params: HashMap<String, String>,

    /// Value as unparsed string.
    pub raw_value: String,

    /// Property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub prop_group: Option<String>
}

impl Property {
    pub fn new(params: HashMap<String, String>, raw_value: String, prop_group: Option<String>) -> Property {
        Property {
            params: params,
            raw_value: raw_value,
            prop_group: prop_group
        }
    }

    /// Create property from unescaped string.
    pub fn new_from_string(s: &str) -> Property {
        Property::new(HashMap::new(), escape_chars(s), None)
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.raw_value.as_slice())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    pub fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    /// Retrieve one property (from many) by key. Returns `None` if nothing is found.
    pub fn single_prop(&self, key: &str) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    /// Retrieve a mutable vector of properties for this key. Creates one (and inserts it into the
    /// component) if none exists.
    pub fn all_props_mut(&mut self, key: &str) -> &mut Vec<Property> {
        match self.props.entry(String::from_str(key)) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.insert(vec![])
        }
    }

    /// Retrieve properties by key. Returns an empty slice if key doesn't exist.
    pub fn all_props(&self, key: &str) -> &[Property] {
        static EMPTY: &'static [Property] = &[];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v.to_string() }

component_end -> String
    = "END:" v:value __ { v.to_string() }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

prop -> (&'input str, Property)
    = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
        (k, Property::new(p, v, g))
    }

group -> String
    = g:group_name "." { g }

group_name -> String
    = group_char+ { match_str.to_string() }

name -> &'input str
    = iana_token+ { match_str }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k.to_string(), v.to_string());
        };
        rv
    }

param -> (&'input str, &'input str)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => ""
        })
    }

param_name -> &'input str
    = iana_token+ { match_str }

param_value -> &'input str
    = x:(quoted_string / param_text) { x }

param_text -> &'input str
    = safe_char* { match_str }

value -> String
    = value_char+ { match_str.to_string() }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

iana_token = ([a-zA-Z0-9] / "-")+
group_char = ([a-zA-Z0-9] / "-")
qsafe_char = !dquote !ctl value_char
safe_char = !";" !":" qsafe_char

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+

// Taken from vCard. vCalendar's is a subset. Together with the definition of "qsafe_char" this
// might reject a bunch of valid iCalendars, but I can't imagine one.
ctl = [\u{00}-\u{1F}] / "\u{7F}"

whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &str) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = unfold_lines(s);
    parser::component(unfolded.as_slice())
}

/// Write a component. The error value is a human-readable message.
pub fn write_component(c: &Component) -> String {
    fn inner(buf: &mut String, c: &Component) {
        buf.push_str("BEGIN:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");

        for (prop_name, props) in c.props.iter() {
            for prop in props.iter() {
                match prop.prop_group {
                    Some(ref x) => { buf.push_str(x.as_slice()); buf.push('.'); },
                    None => ()
                };
                buf.push_str(prop_name.as_slice());
                for (param_key, param_value) in prop.params.iter() {
                    buf.push(';');
                    buf.push_str(param_key.as_slice());
                    buf.push('=');
                    buf.push_str(param_value.as_slice());
                };
                buf.push(':');
                buf.push_str(fold_line(prop.raw_value.as_slice()).as_slice());
            };
        };

        for subcomponent in c.subcomponents.iter() {
            inner(buf, subcomponent);
        };

        buf.push_str("END:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");
    }

    let mut buf = String::new();
    inner(&mut buf, c);
    buf
}

/// Escape text for a VObject property value.
pub fn escape_chars(s: &str) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

/// Unescape text from a VObject property value.
pub fn unescape_chars(s: &str) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}

/// Unfold contentline.
pub fn unfold_lines(s: &str) -> String {
    s
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r ", "").replace("\r\t", "")
}

/// Fold contentline to 75 chars. This function assumes the input to be unfolded, which means no
/// '\n' or '\r' in it.
pub fn fold_line(s: &str) -> String {
    let mut rv = String::new();
    for (i, c) in s.chars().enumerate() {
        rv.push(c);
        if i % 75 == 0 {
            rv.push_str("\r\n ");
        };
    };
    rv
}
<commit_msg>Generate newline<commit_after>// DOCS

#![allow(unstable)]
#![feature(plugin)]
#[plugin] extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

pub struct Property {
    /// Parameters.
    pub params: HashMap<String, String>,

    /// Value as unparsed string.
    pub raw_value: String,

    /// Property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub prop_group: Option<String>
}

impl Property {
    pub fn new(params: HashMap<String, String>, raw_value: String, prop_group: Option<String>) -> Property {
        Property {
            params: params,
            raw_value: raw_value,
            prop_group: prop_group
        }
    }

    /// Create property from unescaped string.
    pub fn new_from_string(s: &str) -> Property {
        Property::new(HashMap::new(), escape_chars(s), None)
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.raw_value.as_slice())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    pub fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    /// Retrieve one property (from many) by key. Returns `None` if nothing is found.
    pub fn single_prop(&self, key: &str) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    /// Retrieve a mutable vector of properties for this key. Creates one (and inserts it into the
    /// component) if none exists.
    pub fn all_props_mut(&mut self, key: &str) -> &mut Vec<Property> {
        match self.props.entry(String::from_str(key)) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.insert(vec![])
        }
    }

    /// Retrieve properties by key. Returns an empty slice if key doesn't exist.
    pub fn all_props(&self, key: &str) -> &[Property] {
        static EMPTY: &'static [Property] = &[];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v.to_string() }

component_end -> String
    = "END:" v:value __ { v.to_string() }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

prop -> (&'input str, Property)
    = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
        (k, Property::new(p, v, g))
    }

group -> String
    = g:group_name "." { g }

group_name -> String
    = group_char+ { match_str.to_string() }

name -> &'input str
    = iana_token+ { match_str }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k.to_string(), v.to_string());
        };
        rv
    }

param -> (&'input str, &'input str)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => ""
        })
    }

param_name -> &'input str
    = iana_token+ { match_str }

param_value -> &'input str
    = x:(quoted_string / param_text) { x }

param_text -> &'input str
    = safe_char* { match_str }

value -> String
    = value_char+ { match_str.to_string() }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

iana_token = ([a-zA-Z0-9] / "-")+
group_char = ([a-zA-Z0-9] / "-")
qsafe_char = !dquote !ctl value_char
safe_char = !";" !":" qsafe_char

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+

// Taken from vCard. vCalendar's is a subset. Together with the definition of "qsafe_char" this
// might reject a bunch of valid iCalendars, but I can't imagine one.
ctl = [\u{00}-\u{1F}] / "\u{7F}"

whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &str) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = unfold_lines(s);
    parser::component(unfolded.as_slice())
}

/// Write a component. The error value is a human-readable message.
pub fn write_component(c: &Component) -> String {
    fn inner(buf: &mut String, c: &Component) {
        buf.push_str("BEGIN:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");

        for (prop_name, props) in c.props.iter() {
            for prop in props.iter() {
                match prop.prop_group {
                    Some(ref x) => { buf.push_str(x.as_slice()); buf.push('.'); },
                    None => ()
                };
                buf.push_str(prop_name.as_slice());
                for (param_key, param_value) in prop.params.iter() {
                    buf.push(';');
                    buf.push_str(param_key.as_slice());
                    buf.push('=');
                    buf.push_str(param_value.as_slice());
                };
                buf.push(':');
                buf.push_str(fold_line(prop.raw_value.as_slice()).as_slice());
                buf.push_str("\r\n");
            };
        };

        for subcomponent in c.subcomponents.iter() {
            inner(buf, subcomponent);
        };

        buf.push_str("END:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");
    }

    let mut buf = String::new();
    inner(&mut buf, c);
    buf
}

/// Escape text for a VObject property value.
pub fn escape_chars(s: &str) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

/// Unescape text from a VObject property value.
pub fn unescape_chars(s: &str) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}

/// Unfold contentline.
pub fn unfold_lines(s: &str) -> String {
    s
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r ", "").replace("\r\t", "")
}

/// Fold contentline to 75 chars. This function assumes the input to be unfolded, which means no
/// '\n' or '\r' in it.
pub fn fold_line(s: &str) -> String {
    let mut rv = String::new();
    for (i, c) in s.chars().enumerate() {
        rv.push(c);
        if i % 75 == 0 {
            rv.push_str("\r\n ");
        };
    };
    rv
}
<|endoftext|>