<commit_before>use token::{Token, Lexeme};
use tokenizer::Tokenizer;
use ast::expression::Expression;
use ast::root::Root;
use ast::node::Node;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use sass::comment::SassComment;
use error::{Result, SassError, ErrorKind};

pub struct Parser<'a> {
    pub tokenizer: Tokenizer<'a>,
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<Root>;

    fn next(&mut self) -> Option<Result<Root>> {
        let mut current_sass_rule = SassRule::new();
        let mut ambiguous_holding_pen = vec![];

        while let Some(Ok(lexeme)) = self.tokenizer.next() {
            match lexeme.token {
                Token::String(ref s) if s.starts_with("$") => {
                    let variable_name = lexeme.clone();

                    if let Err(e) = Parser::expect_token(
                        Token::Colon,
                        self.tokenizer.next()
                    ) { return Some(Err(e)) };

                    let variable_value = match Expression::parse(&mut self.tokenizer) {
                        Ok(e) => e,
                        Err(e) => return Some(Err(e)),
                    };
                    return Some(Ok(Root::Variable(
                        SassVariable {
                            name: variable_name,
                            value: variable_value
                        }
                    )))
                },
                Token::LeftCurlyBrace => {
                    current_sass_rule.selectors
                                     .extend_from_slice(&ambiguous_holding_pen);
                    current_sass_rule.children = match self.parse_body() {
                        Ok(body) => body,
                        Err(e) => return Some(Err(e)),
                    };
                    return Some(Ok(Root::Rule(current_sass_rule)))
                },
                Token::Comma => {
                    current_sass_rule.selectors.extend_from_slice(&ambiguous_holding_pen);
                    ambiguous_holding_pen = vec![];
                },
                Token::Comment(_) => {
                    return Some(Ok(Root::Comment(SassComment { content: lexeme })))
                },
                _ => {
                    match ambiguous_holding_pen.pop() {
                        Some(held_lexeme) => {
                            ambiguous_holding_pen.push(
                                held_lexeme.combine(&lexeme)
                            );
                        },
                        None => {
                           ambiguous_holding_pen = vec![lexeme];
                        },
                    }
                }
            }
        }
        None
    }
}

impl<'a> Parser<'a> {
    pub fn new(text: &str) -> Parser {
        Parser {
            tokenizer: Tokenizer::new(&text),
        }
    }

    pub fn expect_token(expected: Token, actual: Option<Result<Lexeme>>) -> Result<Lexeme> {
        match actual {
            None => Err(SassError {
                offset: 0,
                kind: ErrorKind::UnexpectedEof,
                message: format!(
                    "Expected to see `{}`, instead reached EOF.",
                    expected,
                ),
            }),
            Some(res) => {
                match res {
                    Err(e) => Err(e),
                    Ok(actual) => {
                        if actual.token == expected {
                            Ok(actual)
                        } else {
                            Err(SassError {
                                offset: actual.offset.unwrap_or(0),
                                kind: ErrorKind::ParserError,
                                message: format!(
                                    "Expected to see `{}`, instead saw `{}`.",
                                    expected,
                                    actual.token,
                                ),
                            })
                        }

                    }
                }
            }
        }
    }

    pub fn parse_body(&mut self) -> Result<Vec<Node>> {
        let mut rule_stack = vec![];
        let mut body = vec![];
        let mut ambiguous_holding_pen = vec![];

        while let Some(Ok(lexeme)) = self.tokenizer.next() {
            match lexeme.token {
                Token::RightCurlyBrace => {
                    if rule_stack.is_empty() {
                        return Ok(body);
                    } else {
                        let tmp_rule = rule_stack.pop().unwrap();

                        if rule_stack.is_empty() {
                            body.push(Node::Rule(tmp_rule));
                        } else {
                            // TODO: mut ref to last?
                            let mut rule = rule_stack.pop().unwrap();
                            rule.children.push(Node::Rule(tmp_rule));
                            rule_stack.push(rule);
                        }
                    }
                },
                Token::LeftCurlyBrace => {
                    let mut rule = SassRule::new();
                    rule.selectors = ambiguous_holding_pen;
                    ambiguous_holding_pen = vec![];
                    rule_stack.push(rule);
                },
                Token::Colon => {
                    let value = try!(Expression::parse(&mut self.tokenizer));

                    let child = match ambiguous_holding_pen.pop() {
                        Some(name_lexeme) => {
                            match name_lexeme.token {
                                Token::String(ref s) if s.starts_with("$") => {
                                    Node::Variable(
                                        SassVariable {
                                            name: name_lexeme.clone(),
                                            value: value,
                                        }
                                    )
                                },
                                Token::String(_) => {
                                    Node::Property(name_lexeme, value)
                                },
                                other => {
                                    return Err(SassError {
                                        offset: name_lexeme.offset.unwrap_or(0),
                                        kind: ErrorKind::ParserError,
                                        message: format!(
                                            "Expected to have seen a property or variable name, instead saw {:?}", other
                                        ),
                                    })
                                },
                            }
                        },
                        None => {
                            return Err(SassError {
                                offset: lexeme.offset.unwrap_or(0),
                                kind: ErrorKind::ParserError,
                                message: String::from(
                                    "Expected to have seen a property or \
                                     variable name, did not see any"
                                ),
                            })
                        }
                    };
                    if rule_stack.is_empty() {
                        body.push(child);
                    } else {
                        // TODO: mut ref to last?
                        let mut rule = rule_stack.pop().unwrap();
                        rule.children.push(child);
                        rule_stack.push(rule);
                    }
                },
                Token::Comma => {
                    // TODO: else return error
                    if let Some(Ok(after_comma)) = self.tokenizer.next() {
                        ambiguous_holding_pen.push(after_comma);
                    }
                },
                Token::Comment(_) => {
                    body.push(
                        Node::Comment(
                            SassComment { content: lexeme }
                        )
                    );
                },
                _ => {
                    match ambiguous_holding_pen.pop() {
                        Some(held_lexeme) => {
                            ambiguous_holding_pen.push(
                                held_lexeme.combine(&lexeme)
                            );
                        },
                        None => {
                           ambiguous_holding_pen = vec![lexeme];
                        },
                    }
                },
            }
        }

        Err(SassError {
            offset: 0,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected to see rule body ending in `}`, instead reached EOF."
            ),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::rule::SassRule;
    use sass::comment::SassComment;
    use sass::variable::SassVariable;
    use ast::expression::Expression;
    use ast::root::Root;
    use ast::node::Node;
    use token::{Token, Lexeme};
    use error::{SassError, ErrorKind};

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut parser = Parser::new("");
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_a_rule() {
        let mut parser = Parser::new("a { color: blue; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![Lexeme { token: Token::String("a".into()), offset: Some(0) }],
                children: vec![Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(4) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(11) }
                    ),
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_nested_rules() {
        let mut parser = Parser::new("div { span img, span a { color: blue; } }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
                children: vec![Node::Rule(
                    SassRule {
                        selectors: vec![
                            Lexeme {
                                token: Token::String("span img".into()),
                                offset: Some(6)
                            },
                            Lexeme {
                                token: Token::String("span a".into()),
                                offset: Some(16)
                            },
                        ],
                        children: vec![Node::Property(
                            Lexeme { token: Token::String("color".into()), offset: Some(25) },
                            Expression::String(
                                Lexeme { token: Token::String("blue".into()), offset: Some(32) }
                            ),
                        )],
                    }
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_rules_without_properties() {
        let mut parser = Parser::new("div { empty { span { color: red; } } }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme {
                        token: Token::String("div".into()),
                        offset: Some(0)
                    },
                ],
                children: vec![
                    Node::Rule(
                        SassRule {
                            selectors: vec![
                                Lexeme {
                                    token: Token::String("empty".into()),
                                    offset: Some(6)
                                },
                            ],
                            children: vec![
                                Node::Rule(
                                    SassRule {
                                        selectors: vec![
                                            Lexeme {
                                                token: Token::String("span".into()),
                                                offset: Some(14)
                                            }
                                        ],
                                        children: vec![
                                            Node::Property(
                                                Lexeme {
                                                    token: Token::String("color".into()),
                                                    offset: Some(21)
                                                },
                                                Expression::String(
                                                    Lexeme {
                                                        token: Token::String("red".into()),
                                                        offset: Some(28)
                                                    }
                                                )
                                            )
                                        ],
                                    }
                                ),
                            ],
                        }
                    ),
                ]
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_rules_with_multiple_selectors() {
        let mut parser = Parser::new("a, b c { color: red; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme { token: Token::String("a".into()), offset: Some(0) },
                    Lexeme { token: Token::String("b c".into()), offset: Some(3) },
                ],
                children: vec![Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(9) },
                    Expression::String(
                        Lexeme { token: Token::String("red".into()), offset: Some(16) }
                    ),
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_variable_setting_statements() {
        let mut parser = Parser::new("$color: red;");
        assert_eq!(parser.next(), Some(Ok(Root::Variable(SassVariable {
            name: Lexeme { token: Token::String("$color".into()), offset: Some(0) },
            value: Expression::String(
                Lexeme { token: Token::String("red".into()), offset: Some(8) }
            ),
        }))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_variables_set_within_rules() {
        let mut parser = Parser::new("a { $foo: red; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme { token: Token::String("a".into()), offset: Some(0) },
                ],
                children: vec![Node::Variable(SassVariable {
                    name: Lexeme { token: Token::String("$foo".into()), offset: Some(4) },
                    value: Expression::String(
                        Lexeme { token: Token::String("red".into()), offset: Some(10) }
                    ),
                })],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_a_comment() {
        let mut parser = Parser::new("/* hi */");
        assert_eq!(parser.next(), Some(Ok(Root::Comment(SassComment {
            content: Lexeme { token: Token::Comment("/* hi */".into()), offset: Some(0) },
        }))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_errors_with_malformed_variable_declaration() {
        let mut parser = Parser::new("$var no-colon;");
        assert_eq!(parser.next(), Some(Err(SassError {
            offset: 5,
            kind: ErrorKind::ParserError,
            message: String::from("Expected to see `:`, instead saw `no-colon`."),
        })));
    }
}
<commit_msg>Fix comments in body<commit_after>use token::{Token, Lexeme};
use tokenizer::Tokenizer;
use ast::expression::Expression;
use ast::root::Root;
use ast::node::Node;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use sass::comment::SassComment;
use error::{Result, SassError, ErrorKind};

pub struct Parser<'a> {
    pub tokenizer: Tokenizer<'a>,
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<Root>;

    fn next(&mut self) -> Option<Result<Root>> {
        let mut current_sass_rule = SassRule::new();
        let mut ambiguous_holding_pen = vec![];

        while let Some(Ok(lexeme)) = self.tokenizer.next() {
            match lexeme.token {
                Token::String(ref s) if s.starts_with("$") => {
                    let variable_name = lexeme.clone();

                    if let Err(e) = Parser::expect_token(
                        Token::Colon,
                        self.tokenizer.next()
                    ) { return Some(Err(e)) };

                    let variable_value = match Expression::parse(&mut self.tokenizer) {
                        Ok(e) => e,
                        Err(e) => return Some(Err(e)),
                    };
                    return Some(Ok(Root::Variable(
                        SassVariable {
                            name: variable_name,
                            value: variable_value
                        }
                    )))
                },
                Token::LeftCurlyBrace => {
                    current_sass_rule.selectors
                                     .extend_from_slice(&ambiguous_holding_pen);
                    current_sass_rule.children = match self.parse_body() {
                        Ok(body) => body,
                        Err(e) => return Some(Err(e)),
                    };
                    return Some(Ok(Root::Rule(current_sass_rule)))
                },
                Token::Comma => {
                    current_sass_rule.selectors.extend_from_slice(&ambiguous_holding_pen);
                    ambiguous_holding_pen = vec![];
                },
                Token::Comment(_) => {
                    return Some(Ok(Root::Comment(SassComment { content: lexeme })))
                },
                _ => {
                    match ambiguous_holding_pen.pop() {
                        Some(held_lexeme) => {
                            ambiguous_holding_pen.push(
                                held_lexeme.combine(&lexeme)
                            );
                        },
                        None => {
                           ambiguous_holding_pen = vec![lexeme];
                        },
                    }
                }
            }
        }
        None
    }
}

impl<'a> Parser<'a> {
    pub fn new(text: &str) -> Parser {
        Parser {
            tokenizer: Tokenizer::new(&text),
        }
    }

    pub fn expect_token(expected: Token, actual: Option<Result<Lexeme>>) -> Result<Lexeme> {
        match actual {
            None => Err(SassError {
                offset: 0,
                kind: ErrorKind::UnexpectedEof,
                message: format!(
                    "Expected to see `{}`, instead reached EOF.",
                    expected,
                ),
            }),
            Some(res) => {
                match res {
                    Err(e) => Err(e),
                    Ok(actual) => {
                        if actual.token == expected {
                            Ok(actual)
                        } else {
                            Err(SassError {
                                offset: actual.offset.unwrap_or(0),
                                kind: ErrorKind::ParserError,
                                message: format!(
                                    "Expected to see `{}`, instead saw `{}`.",
                                    expected,
                                    actual.token,
                                ),
                            })
                        }

                    }
                }
            }
        }
    }

    pub fn parse_body(&mut self) -> Result<Vec<Node>> {
        let mut rule_stack = vec![];
        let mut body = vec![];
        let mut ambiguous_holding_pen = vec![];

        while let Some(Ok(lexeme)) = self.tokenizer.next() {
            match lexeme.token {
                Token::RightCurlyBrace => {
                    if rule_stack.is_empty() {
                        return Ok(body);
                    } else {
                        let tmp_rule = rule_stack.pop().unwrap();

                        if rule_stack.is_empty() {
                            body.push(Node::Rule(tmp_rule));
                        } else {
                            // TODO: mut ref to last?
                            let mut rule = rule_stack.pop().unwrap();
                            rule.children.push(Node::Rule(tmp_rule));
                            rule_stack.push(rule);
                        }
                    }
                },
                Token::LeftCurlyBrace => {
                    let mut rule = SassRule::new();
                    rule.selectors = ambiguous_holding_pen;
                    ambiguous_holding_pen = vec![];
                    rule_stack.push(rule);
                },
                Token::Colon => {
                    let value = try!(Expression::parse(&mut self.tokenizer));

                    let child = match ambiguous_holding_pen.pop() {
                        Some(name_lexeme) => {
                            match name_lexeme.token {
                                Token::String(ref s) if s.starts_with("$") => {
                                    Node::Variable(
                                        SassVariable {
                                            name: name_lexeme.clone(),
                                            value: value,
                                        }
                                    )
                                },
                                Token::String(_) => {
                                    Node::Property(name_lexeme, value)
                                },
                                other => {
                                    return Err(SassError {
                                        offset: name_lexeme.offset.unwrap_or(0),
                                        kind: ErrorKind::ParserError,
                                        message: format!(
                                            "Expected to have seen a property or variable name, instead saw {:?}", other
                                        ),
                                    })
                                },
                            }
                        },
                        None => {
                            return Err(SassError {
                                offset: lexeme.offset.unwrap_or(0),
                                kind: ErrorKind::ParserError,
                                message: String::from(
                                    "Expected to have seen a property or \
                                     variable name, did not see any"
                                ),
                            })
                        }
                    };
                    if rule_stack.is_empty() {
                        body.push(child);
                    } else {
                        // TODO: mut ref to last?
                        let mut rule = rule_stack.pop().unwrap();
                        rule.children.push(child);
                        rule_stack.push(rule);
                    }
                },
                Token::Comma => {
                    // TODO: else return error
                    if let Some(Ok(after_comma)) = self.tokenizer.next() {
                        ambiguous_holding_pen.push(after_comma);
                    }
                },
                Token::Comment(_) => {
                    if rule_stack.is_empty() {
                        body.push(
                            Node::Comment(
                                SassComment { content: lexeme }
                            )
                        );
                    } else {
                        // TODO: mut ref to last?
                        let mut rule = rule_stack.pop().unwrap();
                        rule.children.push(Node::Comment(
                            SassComment { content: lexeme }
                        ));
                        rule_stack.push(rule);
                    }
                },
                _ => {
                    match ambiguous_holding_pen.pop() {
                        Some(held_lexeme) => {
                            ambiguous_holding_pen.push(
                                held_lexeme.combine(&lexeme)
                            );
                        },
                        None => {
                           ambiguous_holding_pen = vec![lexeme];
                        },
                    }
                },
            }
        }

        Err(SassError {
            offset: 0,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected to see rule body ending in `}`, instead reached EOF."
            ),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::rule::SassRule;
    use sass::comment::SassComment;
    use sass::variable::SassVariable;
    use ast::expression::Expression;
    use ast::root::Root;
    use ast::node::Node;
    use token::{Token, Lexeme};
    use error::{SassError, ErrorKind};

    #[test]
    fn it_returns_none_for_empty_string() {
        let mut parser = Parser::new("");
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_a_rule() {
        let mut parser = Parser::new("a { color: blue; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![Lexeme { token: Token::String("a".into()), offset: Some(0) }],
                children: vec![Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(4) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(11) }
                    ),
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_nested_rules() {
        let mut parser = Parser::new("div { span img, span a { color: blue; } }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
                children: vec![Node::Rule(
                    SassRule {
                        selectors: vec![
                            Lexeme {
                                token: Token::String("span img".into()),
                                offset: Some(6)
                            },
                            Lexeme {
                                token: Token::String("span a".into()),
                                offset: Some(16)
                            },
                        ],
                        children: vec![Node::Property(
                            Lexeme { token: Token::String("color".into()), offset: Some(25) },
                            Expression::String(
                                Lexeme { token: Token::String("blue".into()), offset: Some(32) }
                            ),
                        )],
                    }
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_rules_without_properties() {
        let mut parser = Parser::new("div { empty { span { color: red; } } }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme {
                        token: Token::String("div".into()),
                        offset: Some(0)
                    },
                ],
                children: vec![
                    Node::Rule(
                        SassRule {
                            selectors: vec![
                                Lexeme {
                                    token: Token::String("empty".into()),
                                    offset: Some(6)
                                },
                            ],
                            children: vec![
                                Node::Rule(
                                    SassRule {
                                        selectors: vec![
                                            Lexeme {
                                                token: Token::String("span".into()),
                                                offset: Some(14)
                                            }
                                        ],
                                        children: vec![
                                            Node::Property(
                                                Lexeme {
                                                    token: Token::String("color".into()),
                                                    offset: Some(21)
                                                },
                                                Expression::String(
                                                    Lexeme {
                                                        token: Token::String("red".into()),
                                                        offset: Some(28)
                                                    }
                                                )
                                            )
                                        ],
                                    }
                                ),
                            ],
                        }
                    ),
                ]
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_rules_with_multiple_selectors() {
        let mut parser = Parser::new("a, b c { color: red; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme { token: Token::String("a".into()), offset: Some(0) },
                    Lexeme { token: Token::String("b c".into()), offset: Some(3) },
                ],
                children: vec![Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(9) },
                    Expression::String(
                        Lexeme { token: Token::String("red".into()), offset: Some(16) }
                    ),
                )],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_variable_setting_statements() {
        let mut parser = Parser::new("$color: red;");
        assert_eq!(parser.next(), Some(Ok(Root::Variable(SassVariable {
            name: Lexeme { token: Token::String("$color".into()), offset: Some(0) },
            value: Expression::String(
                Lexeme { token: Token::String("red".into()), offset: Some(8) }
            ),
        }))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_variables_set_within_rules() {
        let mut parser = Parser::new("a { $foo: red; }");
        assert_eq!(parser.next(), Some(Ok(Root::Rule(
            SassRule {
                selectors: vec![
                    Lexeme { token: Token::String("a".into()), offset: Some(0) },
                ],
                children: vec![Node::Variable(SassVariable {
                    name: Lexeme { token: Token::String("$foo".into()), offset: Some(4) },
                    value: Expression::String(
                        Lexeme { token: Token::String("red".into()), offset: Some(10) }
                    ),
                })],
            }
        ))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_returns_a_comment() {
        let mut parser = Parser::new("/* hi */");
        assert_eq!(parser.next(), Some(Ok(Root::Comment(SassComment {
            content: Lexeme { token: Token::Comment("/* hi */".into()), offset: Some(0) },
        }))));
        assert_eq!(parser.next(), None);
    }

    #[test]
    fn it_errors_with_malformed_variable_declaration() {
        let mut parser = Parser::new("$var no-colon;");
        assert_eq!(parser.next(), Some(Err(SassError {
            offset: 5,
            kind: ErrorKind::ParserError,
            message: String::from("Expected to see `:`, instead saw `no-colon`."),
        })));
    }
}
<|endoftext|>


<commit_before>use crate::plugins;
use comrak::{
    format_html,
    nodes::{AstNode, NodeHtmlBlock, NodeValue},
    parse_document, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions,
    ComrakRenderOptions,
};
use std::string::FromUtf8Error;
use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex},
};
use syntect::{
    highlighting::{Color, ThemeSet},
    html::highlighted_html_for_string,
    parsing::SyntaxSet,
};
use thiserror::Error;
use typed_arena::Arena;
use url_path::UrlPath;

#[derive(Error, Debug)]
pub enum ParseError {
    #[error("Utf8Error: `{0}`")]
    Utf8Error(#[from] FromUtf8Error),
    #[error("Error getting lock from embed file")]
    EmbedFileLockError,
    #[error("Error parsing md file")]
    MdParseError,
}

#[derive(Debug)]
pub struct Html {
    pub title: Option<String>,
    pub content: String,
}

pub struct Settings {
    /// add a base directory for all links to other md files
    base_dir: Option<String>,
    /// if true, external links that doesn't end with `.md` will be linked as is
    link_non_md_external: bool,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            base_dir: None,
            link_non_md_external: true,
        }
    }
}

pub fn parse(arg: &str) -> Result<Html, ParseError> {
    let referred_files = pre_parse_get_embedded_files(arg);
    let embed_files = if let Ok(referred_files) = referred_files {
        let file_contents = plugins::fetch_file_contents(referred_files);
        Some(file_contents)
    } else {
        None
    };
    parse_with_settings(arg, &embed_files, &Settings::default())
}

pub fn parse_with_base_dir(
    arg: &str,
    base_dir: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
) -> Result<Html, ParseError> {
    let settings = Settings {
        base_dir: Some(base_dir.to_string()),
        ..Default::default()
    };
    parse_with_settings(arg, &embed_files, &settings)
}

pub fn parse_with_settings(
    arg: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
    settings: &Settings,
) -> Result<Html, ParseError> {
    let html = parse_via_comrak(arg, &embed_files, settings);
    html
}

fn get_comrak_options() -> ComrakOptions {
    ComrakOptions {
        extension: ComrakExtensionOptions {
            strikethrough: true,
            tagfilter: false,
            table: true,
            autolink: true,
            tasklist: true,
            superscript: false,
            header_ids: None,
            footnotes: true,
            description_lists: true,
        },
        parse: ComrakParseOptions {
            smart: false,
            default_info_string: None,
        },
        render: ComrakRenderOptions {
            hardbreaks: true,
            github_pre_lang: true,
            width: 0,
            unsafe_: true,
            escape: false,
        },
    }
}

fn iter_nodes<'a, F>(
    node: &'a AstNode<'a>,
    is_heading: Arc<Mutex<bool>>,
    title: Arc<Mutex<Option<String>>>,
    f: &F,
) -> Result<(), ParseError>
where
    F: Fn(&'a AstNode<'a>) -> Result<(), ParseError>,
{
    f(node)?;
    for c in node.children() {
        iter_nodes(c, is_heading.clone(), title.clone(), f)?;
    }
    Ok(())
}

fn pre_iter_nodes<'a, F>(
    node: &'a AstNode<'a>,
    files: Arc<Mutex<Vec<String>>>,
    f: &F,
) -> Result<(), ParseError>
where
    F: Fn(&'a AstNode<'a>) -> Result<(), ParseError>,
{
    f(node)?;
    for c in node.children() {
        pre_iter_nodes(c, files.clone(), f)?;
    }
    Ok(())
}
///
/// Extract the embeded files in img image and make it as a lookup
pub fn pre_parse_get_embedded_files(
    arg: &str,
) -> Result<Vec<String>, ParseError> {
    // The returned nodes are created in the supplied Arena, and are bound by its lifetime.
    let arena = Arena::new();
    let option = get_comrak_options();
    let root = parse_document(&arena, arg, &option);
    let embed_files: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));

    pre_iter_nodes(root, embed_files.clone(), &|node| {
        let ref mut value = node.data.borrow_mut().value;
        let new_value = match value {
            &mut NodeValue::Image(ref link) => {
                let link_url = String::from_utf8(link.url.clone())?;
                if let Ok(mut embed_files) = embed_files.lock() {
                    embed_files.push(link_url);
                }
                value.clone()
            }
            _ => value.clone(),
        };
        *value = new_value;
        Ok(())
    })?;
    let embedded = match embed_files.lock() {
        Ok(files) => Ok((*files).to_owned()),
        Err(_e) => Err(ParseError::EmbedFileLockError),
    };
    embedded
}

fn parse_via_comrak(
    arg: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
    settings: &Settings,
) -> Result<Html, ParseError> {
    // The returned nodes are created in the supplied Arena, and are bound by its lifetime.
    let arena = Arena::new();
    let option = get_comrak_options();
    let title: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
    let is_heading: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
    let root = parse_document(&arena, arg, &option);

    iter_nodes(root, is_heading.clone(), title.clone(), &|node| {
        let ref mut value = node.data.borrow_mut().value;
        let new_value = match value {
            &mut NodeValue::CodeBlock(ref codeblock) => {
                let codeblock_info =
                    String::from_utf8(codeblock.info.to_owned())
                        .expect("error converting to string");
                let codeblock_literal =
                    String::from_utf8(codeblock.literal.to_owned())
                        .expect("error converting to string");
                if let Ok(out) = plugins::plugin_executor(
                    &codeblock_info,
                    &codeblock_literal,
                ) {
                    NodeValue::HtmlBlock(NodeHtmlBlock {
                        literal: out.into_bytes(),
                        block_type: 0,
                    })
                } else if let Some(code_block_html) =
                    format_source_code(&codeblock_info, &codeblock_literal)
                {
                    code_block_html
                } else {
                    value.clone()
                }
            }
            &mut NodeValue::Link(ref nodelink) => {
                if let Ok(url) = String::from_utf8(nodelink.url.clone()) {
                    if let Some(ref base_dir) = settings.base_dir {
                        let url1 = UrlPath::new(&url);
                        let url2 = url1.normalize();
                        let url3 = if url1.is_external() {
                            url2
                        } else if url1.is_absolute() {
                            url2
                        } else {
                            format!("{}/{}", base_dir, url)
                        };
                        let url4 = UrlPath::new(&url3);
                        let url5 = url4.normalize();
                        let url6 = if url4.is_external()
                            && !url4.is_extension("md")
                            && settings.link_non_md_external
                        {
                            // leave as it
                            url5
                        } else {
                            format!("/#{}", url5)
                        };
                        log::info!("url6: {}", url6);
                        let mut new_nodelink = nodelink.clone();
                        new_nodelink.url = url6.into_bytes();
                        NodeValue::Link(new_nodelink)
                    } else {
                        value.clone()
                    }
                } else {
                    value.clone()
                }
            }
            &mut NodeValue::Heading(ref heading) => {
                if heading.level == 1 {
                    if let Ok(mut is_heading) = is_heading.lock() {
                        *is_heading = true;
                    }
                }
                value.clone()
            }
            &mut NodeValue::Text(ref text) => {
                if let Ok(is_heading) = is_heading.lock() {
                    if *is_heading {
                        let txt = String::from_utf8(text.to_owned())
                            .expect("Unable to convert to string");
                        if let Ok(mut title) = title.lock() {
                            if title.is_none() {
                                // only when unset
                                *title = Some(txt.to_string());
                            }
                        }
                    }
                }
                value.clone()
            }
            &mut NodeValue::Image(ref link) => {
                let link_url = String::from_utf8(link.url.clone())
                    .expect("unable to convert to string");
                match plugins::embed_handler(&link_url, embed_files) {
                    Ok(html) => NodeValue::HtmlBlock(NodeHtmlBlock {
                        literal: html.into_bytes(),
                        block_type: 0,
                    }),
                    Err(e) => {
                        log::error!("error: {:#?}", e);
                        value.clone()
                    }
                }
            }
            _ => value.clone(),
        };
        *value = new_value;
        Ok(())
    })?;

    let mut html = vec![];

    if let Ok(()) = format_html(root, &option, &mut html) {
        let render_html = String::from_utf8(html)?;
        let title = if let Ok(got) = title.lock() {
            if let Some(ref got) = *got {
                Some(got.to_string())
            } else {
                None
            }
        } else {
            None
        };
        Ok(Html {
            title,
            content: render_html,
        })
    } else {
        Err(ParseError::MdParseError)
    }
}

fn format_source_code(lang: &str, literal: &str) -> Option<NodeValue> {
    let lang_name = match lang {
        "rust" => "Rust",
        _ => "text",
    };

    let ss = SyntaxSet::load_defaults_newlines();
    let ts = ThemeSet::load_defaults();
    let theme = &ts.themes["base16-ocean.light"];
    let _c = theme.settings.background.unwrap_or(Color::WHITE);

    if let Some(syntax) = ss.find_syntax_by_name(lang_name) {
        let html = highlighted_html_for_string(literal, &ss, &syntax, theme);
        Some(NodeValue::HtmlBlock(NodeHtmlBlock {
            literal: html.into_bytes(),
            block_type: 0,
        }))
    } else {
        None
    }
}
<commit_msg>remove useless field settings<commit_after>use crate::plugins;
use comrak::{
    format_html,
    nodes::{AstNode, NodeHtmlBlock, NodeValue},
    parse_document, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions,
    ComrakRenderOptions,
};
use std::string::FromUtf8Error;
use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex},
};
use syntect::{
    highlighting::{Color, ThemeSet},
    html::highlighted_html_for_string,
    parsing::SyntaxSet,
};
use thiserror::Error;
use typed_arena::Arena;
use url_path::UrlPath;

#[derive(Error, Debug)]
pub enum ParseError {
    #[error("Utf8Error: `{0}`")]
    Utf8Error(#[from] FromUtf8Error),
    #[error("Error getting lock from embed file")]
    EmbedFileLockError,
    #[error("Error parsing md file")]
    MdParseError,
}

#[derive(Debug)]
pub struct Html {
    pub title: Option<String>,
    pub content: String,
}

pub struct Settings {
    /// add a base directory for all links to other md files
    base_dir: Option<String>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings { base_dir: None }
    }
}

pub fn parse(arg: &str) -> Result<Html, ParseError> {
    let referred_files = pre_parse_get_embedded_files(arg);
    let embed_files = if let Ok(referred_files) = referred_files {
        let file_contents = plugins::fetch_file_contents(referred_files);
        Some(file_contents)
    } else {
        None
    };
    parse_with_settings(arg, &embed_files, &Settings::default())
}

pub fn parse_with_base_dir(
    arg: &str,
    base_dir: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
) -> Result<Html, ParseError> {
    let settings = Settings {
        base_dir: Some(base_dir.to_string()),
        ..Default::default()
    };
    parse_with_settings(arg, &embed_files, &settings)
}

pub fn parse_with_settings(
    arg: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
    settings: &Settings,
) -> Result<Html, ParseError> {
    let html = parse_via_comrak(arg, &embed_files, settings);
    html
}

fn get_comrak_options() -> ComrakOptions {
    ComrakOptions {
        extension: ComrakExtensionOptions {
            strikethrough: true,
            tagfilter: false,
            table: true,
            autolink: true,
            tasklist: true,
            superscript: false,
            header_ids: None,
            footnotes: true,
            description_lists: true,
        },
        parse: ComrakParseOptions {
            smart: false,
            default_info_string: None,
        },
        render: ComrakRenderOptions {
            hardbreaks: true,
            github_pre_lang: true,
            width: 0,
            unsafe_: true,
            escape: false,
        },
    }
}

fn iter_nodes<'a, F>(
    node: &'a AstNode<'a>,
    is_heading: Arc<Mutex<bool>>,
    title: Arc<Mutex<Option<String>>>,
    f: &F,
) -> Result<(), ParseError>
where
    F: Fn(&'a AstNode<'a>) -> Result<(), ParseError>,
{
    f(node)?;
    for c in node.children() {
        iter_nodes(c, is_heading.clone(), title.clone(), f)?;
    }
    Ok(())
}

fn pre_iter_nodes<'a, F>(
    node: &'a AstNode<'a>,
    files: Arc<Mutex<Vec<String>>>,
    f: &F,
) -> Result<(), ParseError>
where
    F: Fn(&'a AstNode<'a>) -> Result<(), ParseError>,
{
    f(node)?;
    for c in node.children() {
        pre_iter_nodes(c, files.clone(), f)?;
    }
    Ok(())
}
///
/// Extract the embeded files in img image and make it as a lookup
pub fn pre_parse_get_embedded_files(
    arg: &str,
) -> Result<Vec<String>, ParseError> {
    // The returned nodes are created in the supplied Arena, and are bound by its lifetime.
    let arena = Arena::new();
    let option = get_comrak_options();
    let root = parse_document(&arena, arg, &option);
    let embed_files: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));

    pre_iter_nodes(root, embed_files.clone(), &|node| {
        let ref mut value = node.data.borrow_mut().value;
        let new_value = match value {
            &mut NodeValue::Image(ref link) => {
                let link_url = String::from_utf8(link.url.clone())?;
                if let Ok(mut embed_files) = embed_files.lock() {
                    embed_files.push(link_url);
                }
                value.clone()
            }
            _ => value.clone(),
        };
        *value = new_value;
        Ok(())
    })?;
    let embedded = match embed_files.lock() {
        Ok(files) => Ok((*files).to_owned()),
        Err(_e) => Err(ParseError::EmbedFileLockError),
    };
    embedded
}

fn parse_via_comrak(
    arg: &str,
    embed_files: &Option<BTreeMap<String, Vec<u8>>>,
    settings: &Settings,
) -> Result<Html, ParseError> {
    // The returned nodes are created in the supplied Arena, and are bound by its lifetime.
    let arena = Arena::new();
    let option = get_comrak_options();
    let title: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
    let is_heading: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
    let root = parse_document(&arena, arg, &option);

    iter_nodes(root, is_heading.clone(), title.clone(), &|node| {
        let ref mut value = node.data.borrow_mut().value;
        let new_value = match value {
            &mut NodeValue::CodeBlock(ref codeblock) => {
                let codeblock_info =
                    String::from_utf8(codeblock.info.to_owned())
                        .expect("error converting to string");
                let codeblock_literal =
                    String::from_utf8(codeblock.literal.to_owned())
                        .expect("error converting to string");
                if let Ok(out) = plugins::plugin_executor(
                    &codeblock_info,
                    &codeblock_literal,
                ) {
                    NodeValue::HtmlBlock(NodeHtmlBlock {
                        literal: out.into_bytes(),
                        block_type: 0,
                    })
                } else if let Some(code_block_html) =
                    format_source_code(&codeblock_info, &codeblock_literal)
                {
                    code_block_html
                } else {
                    value.clone()
                }
            }
            &mut NodeValue::Link(ref nodelink) => {
                if let Ok(url) = String::from_utf8(nodelink.url.clone()) {
                    if let Some(ref base_dir) = settings.base_dir {
                        let url1 = UrlPath::new(&url);
                        let url2 = url1.normalize();
                        let url3 = if url1.is_external() {
                            url2
                        } else if url1.is_absolute() {
                            url2
                        } else {
                            format!("{}/{}", base_dir, url)
                        };
                        let url4 = UrlPath::new(&url3);
                        let url5 = url4.normalize();
                        let url6 =
                            if url4.is_external() && !url4.is_extension("md") {
                                // leave as it
                                url5
                            } else {
                                format!("/#{}", url5)
                            };
                        log::info!("url6: {}", url6);
                        let mut new_nodelink = nodelink.clone();
                        new_nodelink.url = url6.into_bytes();
                        NodeValue::Link(new_nodelink)
                    } else {
                        value.clone()
                    }
                } else {
                    value.clone()
                }
            }
            &mut NodeValue::Heading(ref heading) => {
                if heading.level == 1 {
                    if let Ok(mut is_heading) = is_heading.lock() {
                        *is_heading = true;
                    }
                }
                value.clone()
            }
            &mut NodeValue::Text(ref text) => {
                if let Ok(is_heading) = is_heading.lock() {
                    if *is_heading {
                        let txt = String::from_utf8(text.to_owned())
                            .expect("Unable to convert to string");
                        if let Ok(mut title) = title.lock() {
                            if title.is_none() {
                                // only when unset
                                *title = Some(txt.to_string());
                            }
                        }
                    }
                }
                value.clone()
            }
            &mut NodeValue::Image(ref link) => {
                let link_url = String::from_utf8(link.url.clone())
                    .expect("unable to convert to string");
                match plugins::embed_handler(&link_url, embed_files) {
                    Ok(html) => NodeValue::HtmlBlock(NodeHtmlBlock {
                        literal: html.into_bytes(),
                        block_type: 0,
                    }),
                    Err(e) => {
                        log::error!("error: {:#?}", e);
                        value.clone()
                    }
                }
            }
            _ => value.clone(),
        };
        *value = new_value;
        Ok(())
    })?;

    let mut html = vec![];

    if let Ok(()) = format_html(root, &option, &mut html) {
        let render_html = String::from_utf8(html)?;
        let title = if let Ok(got) = title.lock() {
            if let Some(ref got) = *got {
                Some(got.to_string())
            } else {
                None
            }
        } else {
            None
        };
        Ok(Html {
            title,
            content: render_html,
        })
    } else {
        Err(ParseError::MdParseError)
    }
}

fn format_source_code(lang: &str, literal: &str) -> Option<NodeValue> {
    let lang_name = match lang {
        "rust" => "Rust",
        _ => "text",
    };

    let ss = SyntaxSet::load_defaults_newlines();
    let ts = ThemeSet::load_defaults();
    let theme = &ts.themes["base16-ocean.light"];
    let _c = theme.settings.background.unwrap_or(Color::WHITE);

    if let Some(syntax) = ss.find_syntax_by_name(lang_name) {
        let html = highlighted_html_for_string(literal, &ss, &syntax, theme);
        Some(NodeValue::HtmlBlock(NodeHtmlBlock {
            literal: html.into_bytes(),
            block_type: 0,
        }))
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use ::bert_parser::BertTerm;
use ::bert_parser::BertTerm::*;

const INDENT_WIDTH: usize = 2;

pub fn print(term: &BertTerm, indent_level: usize) {
    indent(indent_level);
    match *term {
        Int(n) => {
            print!("{}", n);
        }

        BigInt(ref n) => {
            print!("{}", n);
        }

        Float(f) => {
            print!("{}", f);
        }

        Atom(ref a) => {
            print!("{}", a);
        }

        Tuple(ref terms) => {
            println!("{{");
            let mut is_first = true;
            for term in terms {
                if !is_first { println!(",") }
                print(term, indent_level + 1);
                is_first = false;
            }
            println!();
            indent(indent_level);
            print!("}}");
        }

        List(ref terms) => {
            println!("[");
            let mut is_first = true;
            for term in terms {
                if !is_first { println!(",") }
                print(term, indent_level + 1);
                is_first = false;
            }
            println!();
            indent(indent_level);
            print!("]");
        }

        String(ref bytes) => {
            print!("\"");
            for b in bytes {
                print!("{}", *b as char);
            }
            println!("\"");
        }

        Binary(ref bytes) => {
            if bytes.iter().all(|b| is_printable(*b)) {
                print!("<<\"");
                for b in bytes {
                    print!("{}", *b as char);
                }
                println!("\">>");
            } else {
                print!("<<");
                for b in bytes {
                    print!("{:x}", *b);
                    print!(",");
                }
                println!(">>");
            }
        }
    }
}

fn indent(level: usize) {
    for _ in 0 .. level * INDENT_WIDTH {
        print!(" ");
    }
}

fn is_printable(b: u8) -> bool {
    b >= 0x20 && b <= 0x7e
}
<commit_msg>Group terms of small tuples/lists on a single line<commit_after>use ::bert_parser::BertTerm;
use ::bert_parser::BertTerm::*;

const INDENT_WIDTH: usize = 2;
const MAX_TERMS_PER_LINE: usize = 4;

pub fn print(term: &BertTerm, indent_level: usize) {
    match *term {
        Int(n) => {
            print!("{}", n);
        }

        BigInt(ref n) => {
            print!("{}", n);
        }

        Float(f) => {
            print!("{}", f);
        }

        Atom(ref a) => {
            print!("{}", a);
        }

        Tuple(ref terms) => {
            print_collection(terms, indent_level, '{', '}');
        }

        List(ref terms) => {
            print_collection(terms, indent_level, '[', ']');
        }

        String(ref bytes) => {
            print!("\"");
            for b in bytes {
                print!("{}", *b as char);
            }
            print!("\"");
        }

        Binary(ref bytes) => {
            if bytes.iter().all(|b| is_printable(*b)) {
                print!("<<\"");
                for b in bytes {
                    print!("{}", *b as char);
                }
                print!("\">>");
            } else {
                print!("<<");
                for b in bytes {
                    print!("{:x}", *b);
                    print!(",");
                }
                print!(">>");
            }
        }
    }
}

fn print_collection(terms: &[BertTerm], level: usize, open: char, close: char) {
    let is_single_line = print_on_single_line(terms);

    print!("{}", open);

    let mut is_first = true;
    for t in terms {
        if !is_first { print!(", "); }
        if !is_single_line {
            println!();
            indent(level + 1);
            print(t, level + 1);
        } else {
            print(t, level + 1);
        }
        is_first = false;
    }

    if !is_single_line { println!(); indent(level); }
    print!("{}", close);
}

fn indent(level: usize) {
    for _ in 0 .. level * INDENT_WIDTH {
        print!(" ");
    }
}

fn is_printable(b: u8) -> bool {
    b >= 0x20 && b <= 0x7e
}

fn is_basic(t: &BertTerm) -> bool {
    match *t {
        Int(_) | BigInt(_) | Float(_) | Atom(_) | String(_) | Binary(_) => true,
        List(_) | Tuple(_) => false
    }
}

fn print_on_single_line(terms: &[BertTerm]) -> bool {
    terms.len() <= MAX_TERMS_PER_LINE &&
        terms.iter().all(is_basic)
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fmt;
use std::from_str::FromStr;
use std::iter;
use std::slice;

use serialize::{Decodable, Decoder};

use csv;

#[deriving(Clone)]
pub struct SelectColumns {
    selectors: Vec<Selector>,
    invert: bool,
}

// This parser is super basic at the moment. Field names cannot contain [-,].
impl SelectColumns {
    pub fn selection(&self, first_record: &[csv::ByteString], use_names: bool)
                    -> Result<Selection, String> {
        if self.selectors.is_empty() {
            return Ok(Selection(Vec::from_fn(first_record.len(), |i| i)));
        }

        let mut map = vec![];
        for sel in self.selectors.iter() {
            let idxs = sel.indices(first_record, use_names);
            map.extend(try!(idxs).into_iter());
        }
        if self.invert {
            let set: HashSet<_> = map.into_iter().collect();
            let mut map = vec![];
            for i in range(0, first_record.len()) {
                if !set.contains(&i) {
                    map.push(i);
                }
            }
            return Ok(Selection(map));
        }
        Ok(Selection(map))
    }

    fn parse(mut s: &str) -> Result<SelectColumns, String> {
        let mut scols = SelectColumns { selectors: vec![], invert: false };
        if s.is_empty() { return Ok(scols); }
        if s.as_bytes()[0] == b'!' {
            scols.invert = true;
            s = s[1..];
        }
        for sel in s.split(',') {
            scols.selectors.push(try!(SelectColumns::parse_selector(sel)));
        }
        Ok(scols)
    }

    fn parse_selector(sel: &str) -> Result<Selector, String> {
        if sel.contains_char('-') {
            let pieces: Vec<&str> = sel.splitn(1, '-').collect();
            let start =
                if pieces[0].is_empty() {
                    SelStart
                } else {
                    try!(SelectColumns::parse_one_selector(pieces[0]))
                };
            let end =
                if pieces[1].is_empty() {
                    SelEnd
                } else {
                    try!(SelectColumns::parse_one_selector(pieces[1]))
                };
            Ok(SelRange(start, end))
        } else {
            SelectColumns::parse_one_selector(sel).map(SelOne)
        }
    }

    fn parse_one_selector(sel: &str) -> Result<OneSelector, String> {
        if sel.contains_char('-') {
            return Err(format!("Illegal '-' in selector '{}'.", sel))
        }
        let idx: Option<uint> = FromStr::from_str(sel);
        Ok(match idx {
            None => SelIndexedName(sel.to_string(), 0),
            Some(idx) => SelIndex(idx),
        })
    }
}

impl fmt::Show for SelectColumns {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.selectors.is_empty() {
            write!(f, "<All>")
        } else {
            let strs: Vec<_> =
                self.selectors.iter().map(|sel| sel.to_string()).collect();
            write!(f, "{}", strs.connect(", "))
        }
    }
}

impl <E, D: Decoder<E>> Decodable<D, E> for SelectColumns {
    fn decode(d: &mut D) -> Result<SelectColumns, E> {
        SelectColumns::parse(try!(d.read_str()).as_slice())
                      .map_err(|e| d.error(e.as_slice()))
    }
}

#[deriving(Clone)]
enum Selector {
    SelOne(OneSelector),
    SelRange(OneSelector, OneSelector),
}

#[deriving(Clone)]
enum OneSelector {
    SelStart,
    SelEnd,
    SelIndex(uint),
    SelIndexedName(String, uint),
}

impl Selector {
    fn indices(&self, first_record: &[csv::ByteString], use_names: bool)
              -> Result<Vec<uint>, String> {
        match self {
            &SelOne(ref sel) => {
                sel.index(first_record, use_names).map(|i| vec![i])
            }
            &SelRange(ref sel1, ref sel2) => {
                let i1 = try!(sel1.index(first_record, use_names));
                let i2 = try!(sel2.index(first_record, use_names));
                Ok(match i1.cmp(&i2) {
                    Equal => vec!(i1),
                    Less => iter::range_inclusive(i1, i2).collect(),
                    Greater => {
                        iter::range_step_inclusive(i1 as int, i2 as int, -1)
                             .map(|i| i as uint).collect()
                    }
                })
            }
        }
    }
}

impl OneSelector {
    fn index(&self, first_record: &[csv::ByteString], use_names: bool)
            -> Result<uint, String> {
        match self {
            &SelStart => Ok(0),
            &SelEnd => Ok(first_record.len()),
            &SelIndex(i) => {
                if i < 1 || i > first_record.len() {
                    Err(format!("Selector index {} is out of \
                                 bounds. Index must be >= 1 \
                                 and <= {}.", i, first_record.len()))
                } else {
                    // Indices given by user are 1-offset. Convert them here!
                    Ok(i-1)
                }
            }
            &SelIndexedName(ref s, sidx) => {
                if !use_names {
                    return Err(format!("Cannot use names ('{}') in selection \
                                        with --no-headers set.", s));
                }
                let mut num_found = 0;
                for (i, field) in first_record.iter().enumerate() {
                    if field.equiv(s) {
                        if num_found == sidx {
                            return Ok(i);
                        }
                        num_found += 1;
                    }
                }
                if num_found == 0 {
                    return Err(format!("Selector name '{}' does not exist \
                                        as a named header in the given CSV \
                                        data.", s));
                }
                Err(format!("Selector index '{}' for name '{}' is \
                             out of bounds. Must be >= 0 and <= {}.",
                             sidx, s, num_found - 1))
            }
        }
    }
}

impl fmt::Show for Selector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SelOne(ref sel) => sel.fmt(f),
            &SelRange(ref s, ref e) => write!(f, "Range({}, {})", s, e),
        }
    }
}

impl fmt::Show for OneSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SelStart => write!(f, "Start"),
            &SelEnd => write!(f, "End"),
            &SelIndex(idx) => write!(f, "Index({:u})", idx),
            &SelIndexedName(ref s, idx) => write!(f, "IndexedName({}[{}])",
                                                  s, idx),
        }
    }
}

#[deriving(Clone, Show)]
pub struct Selection(Vec<uint>);

impl Selection {
    pub fn select<'a, 'b>(&'a self, row: &'b [csv::ByteString])
                 -> iter::Scan<&'a uint,
                               &'b [u8],
                               slice::Items<'a, uint>,
                               &'b [csv::ByteString]> {
        // This is horrifying.
        // Help me closure reform, you're my only hope.
        self.as_slice().iter().scan(row, |row, &idx| Some(row[idx].as_slice()))
    }

    pub fn normal(&self) -> NormalSelection {
        let &Selection(ref inds) = self;
        let mut normal = inds.clone();
        normal.sort();
        normal.dedup();
        let mut set = Vec::from_elem(normal[normal.len()-1] + 1, false);
        for i in normal.into_iter() {
            *set.get_mut(i) = true;
        }
        NormalSelection(set)
    }
}

impl AsSlice<uint> for Selection {
    fn as_slice(&self) -> &[uint] { self.0[] }
}

impl Collection for Selection {
    fn len(&self) -> uint {
        self.0.len()
    }
}

#[deriving(Clone, Show)]
pub struct NormalSelection(Vec<bool>);

impl NormalSelection {
    pub fn select<'a, 'b, T, I: Iterator<T>>(&'a self, row: I)
                 -> iter::FilterMap<Option<T>, T,
                                    iter::Scan<(uint, T),
                                               Option<T>,
                                               iter::Enumerate<I>,
                                               &'a [bool]>> {
        let set = self.as_slice();
        row.enumerate().scan(set, |set, (i, v)| {
            if i < set.len() && set[i] { Some(Some(v)) } else { Some(None) }
        }).filter_map(|v| v)
    }
}

impl AsSlice<bool> for NormalSelection {
    fn as_slice(&self) -> &[bool] { self.0[] }
}

impl Collection for NormalSelection {
    fn len(&self) -> uint {
        self.as_slice().iter().filter(|b| **b).count()
    }
}
<commit_msg>Implement a real parser for column selection.<commit_after>use std::collections::HashSet;
use std::fmt;
use std::from_str::FromStr;
use std::iter;
use std::slice;

use serialize::{Decodable, Decoder};

use csv;

#[deriving(Clone)]
pub struct SelectColumns {
    selectors: Vec<Selector>,
    invert: bool,
}

impl SelectColumns {
    pub fn selection(&self, first_record: &[csv::ByteString], use_names: bool)
                    -> Result<Selection, String> {
        if self.selectors.is_empty() {
            return Ok(Selection(if self.invert {
                // Inverting everything means we get nothing.
                vec![]
            } else {
                Vec::from_fn(first_record.len(), |i| i)
            }));
        }

        let mut map = vec![];
        for sel in self.selectors.iter() {
            let idxs = sel.indices(first_record, use_names);
            map.extend(try!(idxs).into_iter());
        }
        if self.invert {
            let set: HashSet<_> = map.into_iter().collect();
            let mut map = vec![];
            for i in range(0, first_record.len()) {
                if !set.contains(&i) {
                    map.push(i);
                }
            }
            return Ok(Selection(map));
        }
        Ok(Selection(map))
    }

    fn parse(mut s: &str) -> Result<SelectColumns, String> {
        let invert =
            if s.as_bytes()[0] == b'!' {
                s = s[1..];
                true
            } else {
                false
            };
        Ok(SelectColumns {
            selectors: try!(SelectorParser::new(s).parse()),
            invert: invert,
        })
    }
}

impl fmt::Show for SelectColumns {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.selectors.is_empty() {
            write!(f, "<All>")
        } else {
            let strs: Vec<_> =
                self.selectors.iter().map(|sel| sel.to_string()).collect();
            write!(f, "{}", strs.connect(", "))
        }
    }
}

impl <E, D: Decoder<E>> Decodable<D, E> for SelectColumns {
    fn decode(d: &mut D) -> Result<SelectColumns, E> {
        SelectColumns::parse(try!(d.read_str()).as_slice())
                      .map_err(|e| d.error(e.as_slice()))
    }
}

struct SelectorParser {
    chars: Vec<char>,
    pos: uint,
}

impl SelectorParser {
    fn new(s: &str) -> SelectorParser {
        SelectorParser { chars: s.chars().collect(), pos: 0 }
    }

    fn parse(&mut self) -> Result<Vec<Selector>, String> {
        let mut sels = vec![];
        loop {
            if self.cur().is_none() {
                break;
            }
            let f1: OneSelector =
                if self.cur() == Some('-') {
                    self.bump();
                    SelStart
                } else {
                    try!(self.parse_one())
                };
            let f2: Option<OneSelector> =
                if self.cur() == Some('-') {
                    self.bump();
                    Some(if self.is_end_of_selector() {
                        SelEnd
                    } else {
                        try!(self.parse_one())
                    })
                } else {
                    None
                };
            if !self.is_end_of_selector() {
                return Err(format!(
                    "Expected end of field but got '{}' instead.",
                    self.cur().unwrap()));
            }
            sels.push(match f2 {
                Some(end) => SelRange(f1, end),
                None => SelOne(f1),
            });
            self.bump();
        }
        Ok(sels)
    }

    fn parse_one(&mut self) -> Result<OneSelector, String> {
        let name =
            if self.cur() == Some('"') {
                self.bump();
                try!(self.parse_quoted_name())
            } else {
                try!(self.parse_name())
            };
        Ok(if self.cur() == Some('[') {
            let idx = try!(self.parse_index());
            SelIndexedName(name, idx)
        } else {
            match FromStr::from_str(name[]) {
                None => SelIndexedName(name, 0),
                Some(idx) => SelIndex(idx),
            }
        })
    }

    fn parse_name(&mut self) -> Result<String, String> {
        let mut name = String::new();
        loop {
            if self.is_end_of_field() || self.cur() == Some('[') {
                break;
            }
            name.push(self.cur().unwrap());
            self.bump();
        }
        Ok(name)
    }

    fn parse_quoted_name(&mut self) -> Result<String, String> {
        let mut name = String::new();
        loop {
            match self.cur() {
                None => {
                    return Err("Unclosed quote, missing closing \"."
                               .to_string());
                }
                Some('"') => {
                    self.bump();
                    if self.cur() == Some('"') {
                        self.bump();
                        name.push('"'); name.push('"');
                        continue;
                    }
                    break
                }
                Some(c) => { name.push(c); self.bump(); }
            }
        }
        Ok(name)
    }

    fn parse_index(&mut self) -> Result<uint, String> {
        assert_eq!(self.cur().unwrap(), '[');
        self.bump();

        let mut idx = String::new();
        loop {
            match self.cur() {
                None => {
                    return Err("Unclosed index bracket, missing closing ]."
                               .to_string());
                }
                Some(']') => { self.bump(); break; }
                Some(c) => { idx.push(c); self.bump(); }
            }
        }
        match FromStr::from_str(idx[]) {
            None => Err(format!("Could not convert '{}' to an integer.", idx)),
            Some(idx) => Ok(idx),
        }
    }

    fn cur(&self) -> Option<char> {
        self.chars[].get(self.pos).map(|c| *c)
    }

    fn is_end_of_field(&self) -> bool {
        self.cur().map(|c| c == ',' || c == '-').unwrap_or(true)
    }

    fn is_end_of_selector(&self) -> bool {
        self.cur().map(|c| c == ',').unwrap_or(true)
    }

    fn bump(&mut self) {
        if self.pos < self.chars.len() { self.pos += 1; }
    }
}

#[deriving(Clone)]
enum Selector {
    SelOne(OneSelector),
    SelRange(OneSelector, OneSelector),
}

#[deriving(Clone)]
enum OneSelector {
    SelStart,
    SelEnd,
    SelIndex(uint),
    SelIndexedName(String, uint),
}

impl Selector {
    fn indices(&self, first_record: &[csv::ByteString], use_names: bool)
              -> Result<Vec<uint>, String> {
        match self {
            &SelOne(ref sel) => {
                sel.index(first_record, use_names).map(|i| vec![i])
            }
            &SelRange(ref sel1, ref sel2) => {
                let i1 = try!(sel1.index(first_record, use_names));
                let i2 = try!(sel2.index(first_record, use_names));
                Ok(match i1.cmp(&i2) {
                    Equal => vec!(i1),
                    Less => iter::range_inclusive(i1, i2).collect(),
                    Greater => {
                        iter::range_step_inclusive(i1 as int, i2 as int, -1)
                             .map(|i| i as uint).collect()
                    }
                })
            }
        }
    }
}

impl OneSelector {
    fn index(&self, first_record: &[csv::ByteString], use_names: bool)
            -> Result<uint, String> {
        match self {
            &SelStart => Ok(0),
            &SelEnd => Ok(first_record.len()),
            &SelIndex(i) => {
                if i < 1 || i > first_record.len() {
                    Err(format!("Selector index {} is out of \
                                 bounds. Index must be >= 1 \
                                 and <= {}.", i, first_record.len()))
                } else {
                    // Indices given by user are 1-offset. Convert them here!
                    Ok(i-1)
                }
            }
            &SelIndexedName(ref s, sidx) => {
                if !use_names {
                    return Err(format!("Cannot use names ('{}') in selection \
                                        with --no-headers set.", s));
                }
                let mut num_found = 0;
                for (i, field) in first_record.iter().enumerate() {
                    if field.equiv(s) {
                        if num_found == sidx {
                            return Ok(i);
                        }
                        num_found += 1;
                    }
                }
                if num_found == 0 {
                    return Err(format!("Selector name '{}' does not exist \
                                        as a named header in the given CSV \
                                        data.", s));
                }
                Err(format!("Selector index '{}' for name '{}' is \
                             out of bounds. Must be >= 0 and <= {}.",
                             sidx, s, num_found - 1))
            }
        }
    }
}

impl fmt::Show for Selector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SelOne(ref sel) => sel.fmt(f),
            &SelRange(ref s, ref e) => write!(f, "Range({}, {})", s, e),
        }
    }
}

impl fmt::Show for OneSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SelStart => write!(f, "Start"),
            &SelEnd => write!(f, "End"),
            &SelIndex(idx) => write!(f, "Index({:u})", idx),
            &SelIndexedName(ref s, idx) => write!(f, "IndexedName({}[{}])",
                                                  s, idx),
        }
    }
}

#[deriving(Clone, Show)]
pub struct Selection(Vec<uint>);

impl Selection {
    pub fn select<'a, 'b>(&'a self, row: &'b [csv::ByteString])
                 -> iter::Scan<&'a uint,
                               &'b [u8],
                               slice::Items<'a, uint>,
                               &'b [csv::ByteString]> {
        // This is horrifying.
        // Help me closure reform, you're my only hope.
        self.as_slice().iter().scan(row, |row, &idx| Some(row[idx].as_slice()))
    }

    pub fn normal(&self) -> NormalSelection {
        let &Selection(ref inds) = self;
        let mut normal = inds.clone();
        normal.sort();
        normal.dedup();
        let mut set = Vec::from_elem(normal[normal.len()-1] + 1, false);
        for i in normal.into_iter() {
            *set.get_mut(i) = true;
        }
        NormalSelection(set)
    }
}

impl AsSlice<uint> for Selection {
    fn as_slice(&self) -> &[uint] { self.0[] }
}

impl Collection for Selection {
    fn len(&self) -> uint {
        self.0.len()
    }
}

#[deriving(Clone, Show)]
pub struct NormalSelection(Vec<bool>);

impl NormalSelection {
    pub fn select<'a, 'b, T, I: Iterator<T>>(&'a self, row: I)
                 -> iter::FilterMap<Option<T>, T,
                                    iter::Scan<(uint, T),
                                               Option<T>,
                                               iter::Enumerate<I>,
                                               &'a [bool]>> {
        let set = self.as_slice();
        row.enumerate().scan(set, |set, (i, v)| {
            if i < set.len() && set[i] { Some(Some(v)) } else { Some(None) }
        }).filter_map(|v| v)
    }
}

impl AsSlice<bool> for NormalSelection {
    fn as_slice(&self) -> &[bool] { self.0[] }
}

impl Collection for NormalSelection {
    fn len(&self) -> uint {
        self.as_slice().iter().filter(|b| **b).count()
    }
}
<|endoftext|>


<commit_before>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::c_void;

use gobject_ffi::{self, GCallback};
use translate::ToGlibPtr;

pub unsafe fn connect(receiver: *mut gobject_ffi::GObject, signal_name: &str, trampoline: GCallback,
                      closure: *mut Box<Fn() + 'static>) -> u64 {
    let handle = gobject_ffi::g_signal_connect_data(receiver as *mut _, signal_name.to_glib_none().0,
        trampoline, closure as *mut _, Some(destroy_closure),
        gobject_ffi::GConnectFlags::empty()) as u64;
    assert!(handle > 0);
    handle
}

unsafe extern "C" fn destroy_closure(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
    let ptr = ptr as *mut Box<Fn()>;
    // destroy
    //Box::from_raw(ptr);
    // from_raw API stability workaround
    let _: Box<Box<Fn()>> = ::std::mem::transmute(ptr);
}
<commit_msg>Remove the compatibility cast<commit_after>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::c_void;

use gobject_ffi::{self, GCallback};
use translate::ToGlibPtr;

pub unsafe fn connect(receiver: *mut gobject_ffi::GObject, signal_name: &str, trampoline: GCallback,
                      closure: *mut Box<Fn() + 'static>) -> u64 {
    let handle = gobject_ffi::g_signal_connect_data(receiver, signal_name.to_glib_none().0,
        trampoline, closure as *mut _, Some(destroy_closure),
        gobject_ffi::GConnectFlags::empty()) as u64;
    assert!(handle > 0);
    handle
}

unsafe extern "C" fn destroy_closure(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
    let ptr = ptr as *mut Box<Fn()>;
    // destroy
    //Box::from_raw(ptr);
    // from_raw API stability workaround
    let _: Box<Box<Fn()>> = ::std::mem::transmute(ptr);
}
<|endoftext|>


<commit_before>

struct Todo {
        id: int,
        todo: ~str,
        priority: char,
        finished: bool,
        raw_todo: ~str,
        contexts: & 'static [~str],
        projects: & 'static [~str]
}

impl Todo {
        fn to_str(&self) -> ~str {
                return format!("{:d} {:b} ({:c}) '{:s}' \"{:s}\"",
                                self.id, self.finished, self.priority,
                                self.todo, self.raw_todo)
        }

        fn create(todo: ~str, id: int) -> Todo {
                let mut task = Todo{id: id, raw_todo: todo.clone(), todo: ~"",
                                        finished: false, priority: '^',
                                        contexts: &[], projects: &[]};

                if (todo[0] == "x".as_bytes()[0]) {
                        task.finished = true;
                }

                task.todo = todo.clone();

                return task;
        }
}

fn main () {

        let t = Todo::create(~"some important task", 1);
        println(t.to_str());
        let x = Todo::create(~"x some important task", 1);
        println(x.to_str());
}
<commit_msg>starting with tests<commit_after>

struct Todo {
        id: int,
        todo: ~str,
        priority: char,
        finished: bool,
        raw_todo: ~str,
        contexts: & 'static [~str],
        projects: & 'static [~str]
}

impl Todo {
        fn to_str(&self) -> ~str {
                return format!("{:d} {:b} ({:c}) '{:s}' \"{:s}\"",
                                self.id, self.finished, self.priority,
                                self.todo, self.raw_todo)
        }

        fn create(todo: ~str, id: int) -> Todo {
                let mut task = Todo{id: id, raw_todo: todo.clone(), todo: ~"",
                                        finished: false, priority: '^',
                                        contexts: &[], projects: &[]};

                if (todo[0] == "x".as_bytes()[0]) {
                        task.finished = true;
                }

                task.todo = todo.clone();

                return task;
        }
}

#[test]
fn simple_todo_create_test() {
        let t = Todo::create(~"some important task", 1);
        assert!(t.id == 1);
        assert!(t.priority == '^');
        assert!(t.finished == false);
}

fn main () {

        let t = Todo::create(~"some important task", 1);
        println(t.to_str());
        let x = Todo::create(~"x some important task", 1);
        println(x.to_str());
}
<|endoftext|>


<commit_before>
pub struct Task {
        id: int,
        todo: ~str,
        priority: char,
        finished: bool,
        raw_todo: ~str,
        create_date: ~str,
        finish_date: ~str,
        contexts: & 'static [~str],
        projects: & 'static [~str]
}

impl Task {
        pub fn to_str(&self) -> ~str {
                return format!("{:d} {:b} ({:c}) |{:s}| '{:s}' \"{:s}\"",
                                self.id, self.finished, self.priority,
                                self.create_date, self.todo, self.raw_todo)
        }

        pub fn create(text: ~str, id: int) -> Task {
                let mut task = Task{id: id, raw_todo: text.clone(), todo: text.clone(),
                                        finished: false, priority: '^',
                                        create_date: ~"", finish_date: ~"",
                                        contexts: &[], projects: &[]};

                let mut todo = text.clone();

                if (todo.slice(0, 2) == "x ") {
                        task.finished = true;
                        todo = todo.slice(2, todo.len()).to_owned();
                }

                let mut chars = todo.chars().to_owned_vec();
                if (chars[0] == '(' && chars[2] == ')' &&
                    chars[1].is_uppercase() && chars[3] == ' ') {

                        task.priority = chars[1];
                        todo = todo.slice(4, todo.len()).to_owned();
                }

                chars = todo.chars().to_owned_vec();

                if (chars[10] == ' ' && chars[4] == '-' && chars[7] == '-') {
                        task.create_date = todo.slice(0, 10).to_owned();
                        todo = todo.slice(11, todo.len()).to_owned();
                }

                task.todo = todo;

                return task;
        }
}

#[test]
fn simple_todo_create_test() {
        let t = Task::create(~"some important task", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , '^');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task");
        assert_eq!(t.raw_todo , ~"some important task");
}

#[test]
fn simple_todo_with_priority_test() {
        let t = Task::create(~"(A) some important task with priority", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , 'A');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task with priority");
        assert_eq!(t.raw_todo , ~"(A) some important task with priority");

        let ts = Task::create(~"(a) some important task with priority", 1);

        assert_eq!(ts.id , 1);
        assert_eq!(ts.priority , '^');
        assert_eq!(ts.finished , false);
        assert_eq!(ts.todo , ~"(a) some important task with priority");
        assert_eq!(ts.raw_todo , ~"(a) some important task with priority");

}

#[test]
fn simple_todo_with_date_test() {
        let t = Task::create(~"(A) 2014-03-02 some important task with priority", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , 'A');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task with priority");
        assert_eq!(t.raw_todo , ~"(A) 2014-03-02 some important task with priority");
        assert_eq!(t.create_date, ~"2014-03-02");
}

#[test]
fn todo_to_str_test() {
        let t = Task::create(~"some important task", 1);

        assert_eq!(t.to_str(), ~"1 false (^) || 'some important task' \"some important task\"")

        let x = Task::create(~"x some important task", 1);
        assert_eq!(x.to_str(), ~"1 true (^) || 'some important task' \"x some important task\"")
}
<commit_msg>finished date<commit_after>
pub struct Task {
        id: int,
        todo: ~str,
        priority: char,
        finished: bool,
        raw_todo: ~str,
        create_date: ~str,
        finish_date: ~str,
        contexts: & 'static [~str],
        projects: & 'static [~str]
}

impl Task {
        pub fn to_str(&self) -> ~str {
                return format!("{:d} {:b} ({:c}) |{:s}| '{:s}' \"{:s}\"",
                                self.id, self.finished, self.priority,
                                self.create_date, self.todo, self.raw_todo)
        }

        pub fn create(text: ~str, id: int) -> Task {
                let mut task = Task{id: id, raw_todo: text.clone(), todo: text.clone(),
                                        finished: false, priority: '^',
                                        create_date: ~"", finish_date: ~"",
                                        contexts: &[], projects: &[]};

                let mut todo = text.clone();

                if (todo.slice(0, 2) == "x ") {
                        task.finished = true;
                        todo = todo.slice(2, todo.len()).to_owned();
                }

                let mut chars = todo.chars().to_owned_vec();

                chars = todo.chars().to_owned_vec();
                if (chars[10] == ' ' && chars[4] == '-' && chars[7] == '-') {
                        task.finish_date = todo.slice(0, 10).to_owned();
                        todo = todo.slice(11, todo.len()).to_owned();
                }

                chars = todo.chars().to_owned_vec();
                if (chars[0] == '(' && chars[2] == ')' &&
                    chars[1].is_uppercase() && chars[3] == ' ') {

                        task.priority = chars[1];
                        todo = todo.slice(4, todo.len()).to_owned();
                }

                chars = todo.chars().to_owned_vec();
                if (chars[10] == ' ' && chars[4] == '-' && chars[7] == '-') {
                        task.create_date = todo.slice(0, 10).to_owned();
                        todo = todo.slice(11, todo.len()).to_owned();
                }

                task.todo = todo;

                return task;
        }
}

#[test]
fn simple_todo_create_test() {
        let t = Task::create(~"some important task", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , '^');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task");
        assert_eq!(t.raw_todo , ~"some important task");
}

#[test]
fn simple_todo_with_priority_test() {
        let t = Task::create(~"(A) some important task with priority", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , 'A');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task with priority");
        assert_eq!(t.raw_todo , ~"(A) some important task with priority");

        let ts = Task::create(~"(a) some important task with priority", 1);

        assert_eq!(ts.id , 1);
        assert_eq!(ts.priority , '^');
        assert_eq!(ts.finished , false);
        assert_eq!(ts.todo , ~"(a) some important task with priority");
        assert_eq!(ts.raw_todo , ~"(a) some important task with priority");

}

#[test]
fn simple_todo_with_date_test() {
        let t = Task::create(~"(A) 2014-03-02 some important task with priority", 1);

        assert_eq!(t.id , 1);
        assert_eq!(t.priority , 'A');
        assert_eq!(t.finished , false);
        assert_eq!(t.todo , ~"some important task with priority");
        assert_eq!(t.raw_todo , ~"(A) 2014-03-02 some important task with priority");
        assert_eq!(t.create_date, ~"2014-03-02");
}

#[test]
fn todo_to_str_test() {
        let t = Task::create(~"some important task", 1);

        assert_eq!(t.to_str(), ~"1 false (^) || 'some important task' \"some important task\"")

        let x = Task::create(~"x some important task", 1);
        assert_eq!(x.to_str(), ~"1 true (^) || 'some important task' \"x some important task\"")
}
<|endoftext|>