<commit_before>//! Available languages and related data.

use std::fmt;

macro_rules! fiant_linguae {
    ( $($lang:ident, $bounds:expr, $code:expr;)* ) => {
        fiant_linguae! { $($lang, $bounds, $code);* }
    };
    ( $($lang:ident, $bounds:expr, $code:expr);* ) => {
        /// The set of languages available for hyphenation.
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
        pub enum Language {
            $( $lang, )*
        }

        impl Language {
            /// The default number of characters from the start and end of a word
            /// where breaks may not occur.
            pub fn minima(&self) -> (usize, usize) {
                match *self {
                    $( Language::$lang => $bounds, )*
                }
            }

            /// The [BCP 47](https://tools.ietf.org/html/bcp47) tag for this language.
            pub fn code(&self) -> &'static str {
                match *self {
                    $( Language::$lang => $code, )*
                }
            }
        }

        impl fmt::Display for Language {
            fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", *self)
            }
        }
    }
}

// NOTE: These hyphenation bounds were taken directly from the relevant TeX
// packages, but it is not entirely clear how well they map to the notion of
// Unicode `char` in Rust.
//
// In the worst case, a language featuring graphemes larger than 1 `char` may
// set boundaries mid-grapheme. This should be of no practical consequence,
// since well-formed hyphenation patterns only match full graphemes; moreover,
// well-behaved hyphenators are expected to validate hyphenation opportunities,
// discarding any which arise outside `char` boundaries.
fiant_linguae! {
    Afrikaans,              (1, 2),     "af";
    Albanian,               (2, 2),     "sq";
    Armenian,               (1, 2),     "hy";
    Assamese,               (1, 1),     "as";
    Basque,                 (2, 2),     "eu";
    Belarusian,             (2, 2),     "be";
    Bengali,                (1, 1),     "bn";
    Bulgarian,              (2, 2),     "bg";
    Catalan,                (2, 2),     "ca";
    Chinese,                (1, 1),     "zh-latn-pinyin";
    Coptic,                 (1, 1),     "cop";
    Croatian,               (2, 2),     "hr";
    Czech,                  (2, 3),     "cs";
    Danish,                 (2, 2),     "da";
    Dutch,                  (2, 2),     "nl";
    EnglishGB,              (2, 3),     "en-gb";
    EnglishUS,              (2, 3),     "en-us";
    Esperanto,              (2, 2),     "eo";
    Estonian,               (2, 3),     "et";
    Ethiopic,               (1, 1),     "mul-ethi";
    Finnish,                (2, 2),     "fi";
    FinnishScholastic,      (1, 1),     "fi-x-school";
    French,                 (2, 3),     "fr";
    Friulan,                (2, 2),     "fur";
    Galician,               (2, 2),     "gl";
    Georgian,               (1, 2),     "ka";
    German1901,             (2, 2),     "de-1901";
    German1996,             (2, 2),     "de-1996";
    GermanSwiss,            (2, 2),     "de-ch-1901";
    GreekAncient,           (1, 1),     "grc";
    GreekMono,              (1, 1),     "el-monoton";
    GreekPoly,              (1, 1),     "el-polyton";
    Gujarati,               (1, 1),     "gu";
    Hindi,                  (1, 1),     "hi";
    Hungarian,              (2, 2),     "hu";
    Icelandic,              (2, 2),     "is";
    Indonesian,             (2, 2),     "id";
    Interlingua,            (2, 2),     "ia";
    Irish,                  (2, 3),     "ga";
    Italian,                (2, 2),     "it";
    Kannada,                (1, 1),     "kn";
    Kurmanji,               (2, 2),     "kmr";
    Latin,                  (2, 2),     "la";
    LatinClassic,           (2, 2),     "la-x-classic";
    LatinLiturgical,        (2, 2),     "la-x-liturgic";
    Latvian,                (2, 2),     "lv";
    Lithuanian,             (2, 2),     "lt";
    Macedonian,             (2, 2),     "mk";
    Malayalam,              (1, 1),     "ml";
    Marathi,                (1, 1),     "mr";
    Mongolian,              (2, 2),     "mn-cyrl";
    NorwegianBokmal,        (2, 2),     "nb";
    NorwegianNynorsk,       (2, 2),     "nn";
    Occitan,                (2, 2),     "oc";
    Oriya,                  (1, 1),     "or";
    Pali,                   (1, 2),     "pi";
    Panjabi,                (1, 1),     "pa";
    Piedmontese,            (2, 2),     "pms";
    Polish,                 (2, 2),     "pl";
    Portuguese,             (2, 3),     "pt";
    Romanian,               (2, 2),     "ro";
    Romansh,                (2, 2),     "rm";
    Russian,                (2, 2),     "ru";
    Sanskrit,               (1, 3),     "sa";
    SerbianCyrillic,        (2, 2),     "sr-cyrl";
    SerbocroatianCyrillic,  (2, 2),     "sh-cyrl";
    SerbocroatianLatin,     (2, 2),     "sh-latn";
    SlavonicChurch,         (1, 2),     "cu";
    Slovak,                 (2, 3),     "sk";
    Slovenian,              (2, 2),     "sl";
    Spanish,                (2, 2),     "es";
    Swedish,                (2, 2),     "sv";
    Tamil,                  (1, 1),     "ta";
    Telugu,                 (1, 1),     "te";
    Thai,                   (2, 3),     "th";
    Turkish,                (2, 2),     "tr";
    Turkmen,                (2, 2),     "tk";
    Ukrainian,              (2, 2),     "uk";
    Uppersorbian,           (2, 2),     "hsb";
    Welsh,                  (2, 3),     "cy";
}
<commit_msg>Add try_from_code to Language<commit_after>//! Available languages and related data.

use std::fmt;

macro_rules! fiant_linguae {
    ( $($lang:ident, $bounds:expr, $code:expr;)* ) => {
        fiant_linguae! { $($lang, $bounds, $code);* }
    };
    ( $($lang:ident, $bounds:expr, $code:expr);* ) => {
        /// The set of languages available for hyphenation.
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
        pub enum Language {
            $( $lang, )*
        }

        impl Language {
            /// The default number of characters from the start and end of a word
            /// where breaks may not occur.
            pub fn minima(&self) -> (usize, usize) {
                match *self {
                    $( Language::$lang => $bounds, )*
                }
            }

            /// The [BCP 47](https://tools.ietf.org/html/bcp47) tag for this language.
            pub fn code(&self) -> &'static str {
                match *self {
                    $( Language::$lang => $code, )*
                }
            }

            /// Try and construct a Language from a [BCP 47](https://tools.ietf.org/html/bcp47) tag.
            pub fn try_from_code<T: AsRef<str>>(code: T) -> Option<Language> {
                match code.as_ref() {
                    $( $code => Some(Language::$lang), )*
                    _ => None
                }
            }
        }

        impl fmt::Display for Language {
            fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", *self)
            }
        }
    }
}

// NOTE: These hyphenation bounds were taken directly from the relevant TeX
// packages, but it is not entirely clear how well they map to the notion of
// Unicode `char` in Rust.
//
// In the worst case, a language featuring graphemes larger than 1 `char` may
// set boundaries mid-grapheme. This should be of no practical consequence,
// since well-formed hyphenation patterns only match full graphemes; moreover,
// well-behaved hyphenators are expected to validate hyphenation opportunities,
// discarding any which arise outside `char` boundaries.
fiant_linguae! {
    Afrikaans,              (1, 2),     "af";
    Albanian,               (2, 2),     "sq";
    Armenian,               (1, 2),     "hy";
    Assamese,               (1, 1),     "as";
    Basque,                 (2, 2),     "eu";
    Belarusian,             (2, 2),     "be";
    Bengali,                (1, 1),     "bn";
    Bulgarian,              (2, 2),     "bg";
    Catalan,                (2, 2),     "ca";
    Chinese,                (1, 1),     "zh-latn-pinyin";
    Coptic,                 (1, 1),     "cop";
    Croatian,               (2, 2),     "hr";
    Czech,                  (2, 3),     "cs";
    Danish,                 (2, 2),     "da";
    Dutch,                  (2, 2),     "nl";
    EnglishGB,              (2, 3),     "en-gb";
    EnglishUS,              (2, 3),     "en-us";
    Esperanto,              (2, 2),     "eo";
    Estonian,               (2, 3),     "et";
    Ethiopic,               (1, 1),     "mul-ethi";
    Finnish,                (2, 2),     "fi";
    FinnishScholastic,      (1, 1),     "fi-x-school";
    French,                 (2, 3),     "fr";
    Friulan,                (2, 2),     "fur";
    Galician,               (2, 2),     "gl";
    Georgian,               (1, 2),     "ka";
    German1901,             (2, 2),     "de-1901";
    German1996,             (2, 2),     "de-1996";
    GermanSwiss,            (2, 2),     "de-ch-1901";
    GreekAncient,           (1, 1),     "grc";
    GreekMono,              (1, 1),     "el-monoton";
    GreekPoly,              (1, 1),     "el-polyton";
    Gujarati,               (1, 1),     "gu";
    Hindi,                  (1, 1),     "hi";
    Hungarian,              (2, 2),     "hu";
    Icelandic,              (2, 2),     "is";
    Indonesian,             (2, 2),     "id";
    Interlingua,            (2, 2),     "ia";
    Irish,                  (2, 3),     "ga";
    Italian,                (2, 2),     "it";
    Kannada,                (1, 1),     "kn";
    Kurmanji,               (2, 2),     "kmr";
    Latin,                  (2, 2),     "la";
    LatinClassic,           (2, 2),     "la-x-classic";
    LatinLiturgical,        (2, 2),     "la-x-liturgic";
    Latvian,                (2, 2),     "lv";
    Lithuanian,             (2, 2),     "lt";
    Macedonian,             (2, 2),     "mk";
    Malayalam,              (1, 1),     "ml";
    Marathi,                (1, 1),     "mr";
    Mongolian,              (2, 2),     "mn-cyrl";
    NorwegianBokmal,        (2, 2),     "nb";
    NorwegianNynorsk,       (2, 2),     "nn";
    Occitan,                (2, 2),     "oc";
    Oriya,                  (1, 1),     "or";
    Pali,                   (1, 2),     "pi";
    Panjabi,                (1, 1),     "pa";
    Piedmontese,            (2, 2),     "pms";
    Polish,                 (2, 2),     "pl";
    Portuguese,             (2, 3),     "pt";
    Romanian,               (2, 2),     "ro";
    Romansh,                (2, 2),     "rm";
    Russian,                (2, 2),     "ru";
    Sanskrit,               (1, 3),     "sa";
    SerbianCyrillic,        (2, 2),     "sr-cyrl";
    SerbocroatianCyrillic,  (2, 2),     "sh-cyrl";
    SerbocroatianLatin,     (2, 2),     "sh-latn";
    SlavonicChurch,         (1, 2),     "cu";
    Slovak,                 (2, 3),     "sk";
    Slovenian,              (2, 2),     "sl";
    Spanish,                (2, 2),     "es";
    Swedish,                (2, 2),     "sv";
    Tamil,                  (1, 1),     "ta";
    Telugu,                 (1, 1),     "te";
    Thai,                   (2, 3),     "th";
    Turkish,                (2, 2),     "tr";
    Turkmen,                (2, 2),     "tk";
    Ukrainian,              (2, 2),     "uk";
    Uppersorbian,           (2, 2),     "hsb";
    Welsh,                  (2, 3),     "cy";
}
<|endoftext|>


<commit_before>//! Parser
//!
//! This module contains functions than can be used for writing plugins
//! but should be ignored for simple usage.

use std::slice::Iter;
use std::collections::HashSet;
use std::iter::FromIterator;

use error::{Error, Result};

use interpreter::Renderable;
use interpreter::Text;
use interpreter::Variable;
use interpreter::{Output, FilterPrototype};
use super::Element;
use super::LiquidOptions;
use super::ParseBlock;
use super::ParseTag;
use super::Token;
use value::Index;

fn coerce(index: f32) -> Option<isize> {
    // at the first condition only is_normal is not enough
    // because zero is not counted normal
    if (index != 0f32 && !index.is_normal()) || index.round() > (::std::isize::MAX as f32) ||
       index.round() < (::std::isize::MIN as f32) {
        None
    } else {
        Some(index.round() as isize)
    }
}

/// Parses the provided elements into a number of Renderable items
/// This is the internal version of parse that accepts Elements tokenized
/// by `lexer::tokenize` and does not register built-in blocks. The main use
/// for this function is for writing custom blocks.
///
/// For parsing from a String you should refer to `liquid::parse`.
pub fn parse(elements: &[Element], options: &LiquidOptions) -> Result<Vec<Box<Renderable>>> {
    let mut ret = vec![];
    let mut iter = elements.iter();
    let mut token = iter.next();
    while token.is_some() {
        match *token.unwrap() {
            Element::Expression(ref tokens, _) => ret.push(try!(parse_expression(tokens, options))),
            Element::Tag(ref tokens, _) => ret.push(try!(parse_tag(&mut iter, tokens, options))),
            Element::Raw(ref x) => ret.push(Box::new(Text::new(x))),
        }
        token = iter.next();
    }
    Ok(ret)
}

// creates an expression, which wraps everything that gets rendered
fn parse_expression(tokens: &[Token], options: &LiquidOptions) -> Result<Box<Renderable>> {
    match tokens[0] {
        Token::Identifier(ref x) if tokens.len() > 1 &&
                                    (tokens[1] == Token::Dot || tokens[1] == Token::OpenSquare) => {
            let indexes = parse_indexes(&tokens[1..])?;
            let mut result = Variable::new(x.clone());
            result.extend(indexes);
            Ok(Box::new(result))
        }
        Token::Identifier(ref x) if options.tags.contains_key(x) => {
            options.tags[x].parse(x, &tokens[1..], options)
        }
        _ => {
            let output = parse_output(tokens)?;
            Ok(Box::new(output))
        }
    }
}

pub fn parse_indexes(mut tokens: &[Token]) -> Result<Vec<Index>> {
    let mut indexes: Vec<Index> = Vec::new();

    let mut rest = 0;
    while tokens.len() > rest {
        tokens = &tokens[rest..];
        rest = match tokens[0] {
            Token::Dot if tokens.len() > 1 => {
                match tokens[1] {
                    Token::Identifier(ref x) => indexes.push(Index::with_key(x.as_ref())),
                    _ => {
                        return Error::parser("identifier", Some(&tokens[0]));
                    }
                };
                2
            }
            Token::OpenSquare if tokens.len() > 2 => {
                let index = match tokens[1] {
                    Token::StringLiteral(ref x) => Index::with_key(x.as_ref()),
                    Token::NumberLiteral(ref x) => {
                        let x = coerce(*x)
                            .ok_or_else(|| Error::Parser(format!("Invalid index {}", x)))?;
                        Index::with_index(x)
                    }
                    _ => {
                        return Error::parser("number | string", Some(&tokens[0]));
                    }
                };
                indexes.push(index);

                if tokens[2] != Token::CloseSquare {
                    return Error::parser("]", Some(&tokens[1]));
                }
                3
            }
            _ => return Ok(indexes),
        };
    }

    Ok(indexes)
}

/// Creates an Output, a wrapper around values, variables and filters
/// used internally, from a list of Tokens. This is mostly useful
/// for correctly parsing complex expressions with filters.
pub fn parse_output(tokens: &[Token]) -> Result<Output> {
    let entry = tokens[0].to_arg()?;

    let mut filters = vec![];
    let mut iter = tokens.iter().peekable();
    iter.next();

    while iter.peek() != None {
        expect(&mut iter, &Token::Pipe)?;

        let name = match iter.next() {
            Some(&Token::Identifier(ref name)) => name,
            x => {
                return Error::parser("an identifier", x);
            }
        };
        let mut args = vec![];

        match iter.peek() {
            Some(&&Token::Pipe) |
            None => {
                filters.push(FilterPrototype::new(name, args));
                continue;
            }
            _ => (),
        }

        expect(&mut iter, &Token::Colon)?;

        // loops through the argument list after the filter name
        while iter.peek() != None && iter.peek().unwrap() != &&Token::Pipe {
            args.push(iter.next().unwrap().to_arg()?);

            // ensure that the next token is either a Comma or a Pipe
            match iter.peek() {
                Some(&&Token::Comma) => {
                    let _ = iter.next().unwrap();
                    continue;
                }
                Some(&&Token::Pipe) |
                None => break,
                _ => {
                    return Error::parser("a comma or a pipe", Some(iter.next().unwrap()));
                }
            }
        }

        filters.push(FilterPrototype::new(name, args));
    }

    Ok(Output::new(entry, filters))
}

// a tag can be either a single-element tag or a block, which can contain other
// elements and is delimited by a closing tag named {{end +
// the_name_of_the_tag}}. Tags do not get rendered, but blocks may contain
// renderable expressions
fn parse_tag(iter: &mut Iter<Element>,
             tokens: &[Token],
             options: &LiquidOptions)
             -> Result<Box<Renderable>> {
    let tag = &tokens[0];
    match *tag {
        // is a tag
        Token::Identifier(ref x) if options.tags.contains_key(x) => {
            options.tags[x].parse(x, &tokens[1..], options)
        }

        // is a block
        Token::Identifier(ref x) if options.blocks.contains_key(x) => {
            // Collect all the inner elements of this block until we find a
            // matching "end<blockname>" tag. Note that there may be nested blocks
            // of the same type (and hence have the same closing delimiter) *inside*
            // the body of the block, which would premauturely stop the element
            // collection early if we did a nesting-unaware search for the
            // closing tag.
            //
            // The whole nesting count machinery below is to ensure we only stop
            // collecting elements when we have an un-nested closing tag.

            let end_tag = Token::Identifier("end".to_owned() + x);
            let mut children = vec![];
            let mut nesting_depth = 0;
            for t in iter {
                if let Element::Tag(ref tokens, _) = *t {
                    match tokens[0] {
                        ref n if n == tag => {
                            nesting_depth += 1;
                        }
                        ref n if n == &end_tag && nesting_depth > 0 => {
                            nesting_depth -= 1;
                        }
                        ref n if n == &end_tag && nesting_depth == 0 => break,
                        _ => {}
                    }
                };
                children.push(t.clone())
            }
            options.blocks[x].parse(x, &tokens[1..], &children, options)
        }

        ref x => Err(Error::Parser(format!("parse_tag: {:?} not implemented", x))),
    }
}

/// Confirm that the next token in a token stream is what you want it
/// to be. The token iterator is moved to the next token in the stream.
pub fn expect<'a, T>(tokens: &mut T, expected: &Token) -> Result<&'a Token>
    where T: Iterator<Item = &'a Token>
{
    match tokens.next() {
        Some(x) if x == expected => Ok(x),
        x => Error::parser(&expected.to_string(), x),
    }
}

/// Extracts a token from the token stream that can be used to express a
/// value. For our purposes, this is either a string literal, number literal
/// or an identifier that might refer to a variable.
pub fn consume_value_token(tokens: &mut Iter<Token>) -> Result<Token> {
    match tokens.next() {
        Some(t) => value_token(t.clone()),
        None => Error::parser("string | number | identifier", None),
    }
}

/// Recognises a value token, returning an error if a non-value token
/// is presented.
pub fn value_token(t: Token) -> Result<Token> {
    match t {
        v @ Token::StringLiteral(_) |
        v @ Token::NumberLiteral(_) |
        v @ Token::BooleanLiteral(_) |
        v @ Token::Identifier(_) => Ok(v),
        x => Error::parser("string | number | boolean | identifier", Some(&x)),
    }
}

/// Describes the optional trailing part of a block split.
pub struct BlockSplit<'a> {
    pub delimiter: String,
    pub args: &'a [Token],
    pub trailing: &'a [Element],
}

/// A sub-block aware splitter that will only split the token stream
/// when it finds a delimter at the top level of the token stream,
/// ignoring any it finds in nested blocks.
///
/// Returns a slice contaiing all elements before the delimiter, and
/// an optional `BlockSplit` struct describing the delimiter and
/// trailing elements.
pub fn split_block<'a>(tokens: &'a [Element],
                       delimiters: &[&str],
                       options: &LiquidOptions)
                       -> (&'a [Element], Option<BlockSplit<'a>>) {
    // construct a fast-lookup cache of the delimiters, as we're going to be
    // consulting the delimiter list a *lot*.
    let delims: HashSet<&str> = HashSet::from_iter(delimiters.iter().map(|x| *x));
    let mut stack: Vec<String> = Vec::new();

    for (i, t) in tokens.iter().enumerate() {
        if let Element::Tag(ref args, _) = *t {
            match args[0] {
                Token::Identifier(ref name) if options.blocks.contains_key(name) => {
                    stack.push("end".to_owned() + name);
                }

                Token::Identifier(ref name) if Some(name) == stack.last() => {
                    stack.pop();
                }

                Token::Identifier(ref name) if stack.is_empty() &&
                                               delims.contains(name.as_str()) => {
                    let leading = &tokens[0..i];
                    let split = BlockSplit {
                        delimiter: name.clone(),
                        args: args,
                        trailing: &tokens[i..],
                    };
                    return (leading, Some(split));
                }
                _ => {}
            }
        }
    }

    (&tokens[..], None)
}

#[cfg(test)]
mod test_parse_output {
    use super::*;
    use value::Value;
    use super::super::lexer::granularize;
    use interpreter::Argument;

    #[test]
    fn parses_filters() {
        let tokens = granularize("abc | def:'1',2,'3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap(),
                   Output::new(Argument::Var(Variable::new("abc")),
                               vec![FilterPrototype::new("def",
                                                         vec![Argument::Val(Value::str("1")),
                                                              Argument::Val(Value::Num(2.0)),
                                                              Argument::Val(Value::str("3"))]),
                                    FilterPrototype::new("blabla", vec![])]));
    }

    #[test]
    fn requires_filter_names() {
        let tokens = granularize("abc | '1','2','3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected an identifier, found 1");
    }

    #[test]
    fn fails_on_missing_pipes() {
        let tokens = granularize("abc | def:'1',2,'3' blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected a comma or a pipe, found blabla");
    }

    #[test]
    fn fails_on_missing_colons() {
        let tokens = granularize("abc | def '1',2,'3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected :, found 1");
    }
}

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

    #[test]
    fn rejects_unexpected_token() {
        let token_vec = vec![Token::Pipe, Token::Dot, Token::Colon];
        let mut tokens = token_vec.iter();

        assert!(expect(&mut tokens, &Token::Pipe).is_ok());
        assert!(expect(&mut tokens, &Token::Dot).is_ok());
        assert!(expect(&mut tokens, &Token::Comma).is_err());
    }
}

#[cfg(test)]
mod test_split_block {
    use std::collections::HashMap;
    use super::*;
    use super::super::tokenize;
    use super::super::split_block;
    use super::super::BoxedBlockParser;
    use super::super::FnParseBlock;
    use interpreter::Renderable;
    use interpreter::Context;

    #[derive(Debug)]
    struct NullBlock;

    impl Renderable for NullBlock {
        fn render(&self, _context: &mut Context) -> Result<Option<String>> {
            Ok(None)
        }
    }

    pub fn null_block(_tag_name: &str,
                      _arguments: &[Token],
                      _tokens: &[Element],
                      _options: &LiquidOptions)
                      -> Result<Box<Renderable>> {
        Ok(Box::new(NullBlock))
    }

    fn options() -> LiquidOptions {
        let mut options = LiquidOptions::default();
        let blocks: HashMap<String, BoxedBlockParser> = ["comment", "for", "if"]
            .into_iter()
            .map(|name| (name.to_string(), (null_block as FnParseBlock).into()))
            .collect();
        options.blocks = blocks;
        options
    }

    #[test]
    fn handles_nonmatching_stream() {
        // A stream of tokens with lots of `else`s in it, but only one at the
        // top level, which is where it should split.
        let tokens = tokenize("{% comment %}A{%endcomment%} bunch of {{text}} with {{no}} \
                                   else tag")
            .unwrap();

        // note that we need an options block that has been initilaised with
        // the supported block list; otherwise the split_tag function won't know
        // which things start a nested block.
        let options = options();
        let (_, trailing) = split_block(&tokens[..], &["else"], &options);
        assert!(trailing.is_none());
    }

    #[test]
    fn honours_nesting() {
        // A stream of tokens with lots of `else`s in it, but only one at the
        // top level, which is where it should split.
        let tokens = tokenize(concat!("{% for x in (1..10) %}",
                                      "{% if x == 2 %}",
                                      "{% for y (2..10) %}{{y}}{% else %} zz {% endfor %}",
                                      "{% else %}",
                                      "c",
                                      "{% endif %}",
                                      "{% else %}",
                                      "something",
                                      "{% endfor %}",
                                      "{% else %}",
                                      "trailing tags"))
            .unwrap();

        // note that we need an options block that has been initilaised with
        // the supported block list; otherwise the split_tag function won't know
        // which things start a nested block.
        let options = options();
        let (_, trailing) = split_block(&tokens[..], &["else"], &options);
        match trailing {
            Some(split) => {
                assert_eq!(split.delimiter, "else");
                assert_eq!(split.args, &[Token::Identifier("else".to_owned())]);
                assert_eq!(split.trailing,
                           &[Element::Tag(vec![Token::Identifier("else".to_owned())],
                                          "{% else %}".to_owned()),
                             Element::Raw("trailing tags".to_owned())]);
            }
            None => panic!("split failed"),
        }
    }
}
<commit_msg>fix(parse): Error on empty expressions<commit_after>//! Parser
//!
//! This module contains functions than can be used for writing plugins
//! but should be ignored for simple usage.

use std::slice::Iter;
use std::collections::HashSet;
use std::iter::FromIterator;

use error::{Error, Result};

use interpreter::Renderable;
use interpreter::Text;
use interpreter::Variable;
use interpreter::{Output, FilterPrototype};
use super::Element;
use super::LiquidOptions;
use super::ParseBlock;
use super::ParseTag;
use super::Token;
use value::Index;

fn coerce(index: f32) -> Option<isize> {
    // at the first condition only is_normal is not enough
    // because zero is not counted normal
    if (index != 0f32 && !index.is_normal()) || index.round() > (::std::isize::MAX as f32) ||
       index.round() < (::std::isize::MIN as f32) {
        None
    } else {
        Some(index.round() as isize)
    }
}

/// Parses the provided elements into a number of Renderable items
/// This is the internal version of parse that accepts Elements tokenized
/// by `lexer::tokenize` and does not register built-in blocks. The main use
/// for this function is for writing custom blocks.
///
/// For parsing from a String you should refer to `liquid::parse`.
pub fn parse(elements: &[Element], options: &LiquidOptions) -> Result<Vec<Box<Renderable>>> {
    let mut ret = vec![];
    let mut iter = elements.iter();
    let mut token = iter.next();
    while token.is_some() {
        let render = match *token.unwrap() {
            Element::Expression(ref tokens, _) => parse_expression(tokens, options)?,
            Element::Tag(ref tokens, _) => parse_tag(&mut iter, tokens, options)?,
            Element::Raw(ref x) => Box::new(Text::new(x)),
        };
        ret.push(render);
        token = iter.next();
    }
    Ok(ret)
}

// creates an expression, which wraps everything that gets rendered
fn parse_expression(tokens: &[Token], options: &LiquidOptions) -> Result<Box<Renderable>> {
    match tokens.get(0) {
        Some(&Token::Identifier(ref x)) if tokens.len() > 1 &&
                                           (tokens[1] == Token::Dot ||
                                            tokens[1] == Token::OpenSquare) => {
            let indexes = parse_indexes(&tokens[1..])?;
            let mut result = Variable::new(x.clone());
            result.extend(indexes);
            Ok(Box::new(result))
        }
        Some(&Token::Identifier(ref x)) if options.tags.contains_key(x) => {
            options.tags[x].parse(x, &tokens[1..], options)
        }
        None => Error::parser("expression", None),
        _ => {
            let output = parse_output(tokens)?;
            Ok(Box::new(output))
        }
    }
}

pub fn parse_indexes(mut tokens: &[Token]) -> Result<Vec<Index>> {
    let mut indexes: Vec<Index> = Vec::new();

    let mut rest = 0;
    while tokens.len() > rest {
        tokens = &tokens[rest..];
        rest = match tokens[0] {
            Token::Dot if tokens.len() > 1 => {
                match tokens[1] {
                    Token::Identifier(ref x) => indexes.push(Index::with_key(x.as_ref())),
                    _ => {
                        return Error::parser("identifier", Some(&tokens[0]));
                    }
                };
                2
            }
            Token::OpenSquare if tokens.len() > 2 => {
                let index = match tokens[1] {
                    Token::StringLiteral(ref x) => Index::with_key(x.as_ref()),
                    Token::NumberLiteral(ref x) => {
                        let x = coerce(*x)
                            .ok_or_else(|| Error::Parser(format!("Invalid index {}", x)))?;
                        Index::with_index(x)
                    }
                    _ => {
                        return Error::parser("number | string", Some(&tokens[0]));
                    }
                };
                indexes.push(index);

                if tokens[2] != Token::CloseSquare {
                    return Error::parser("]", Some(&tokens[1]));
                }
                3
            }
            _ => return Ok(indexes),
        };
    }

    Ok(indexes)
}

/// Creates an Output, a wrapper around values, variables and filters
/// used internally, from a list of Tokens. This is mostly useful
/// for correctly parsing complex expressions with filters.
pub fn parse_output(tokens: &[Token]) -> Result<Output> {
    let entry = tokens[0].to_arg()?;

    let mut filters = vec![];
    let mut iter = tokens.iter().peekable();
    iter.next();

    while iter.peek() != None {
        expect(&mut iter, &Token::Pipe)?;

        let name = match iter.next() {
            Some(&Token::Identifier(ref name)) => name,
            x => {
                return Error::parser("an identifier", x);
            }
        };
        let mut args = vec![];

        match iter.peek() {
            Some(&&Token::Pipe) |
            None => {
                filters.push(FilterPrototype::new(name, args));
                continue;
            }
            _ => (),
        }

        expect(&mut iter, &Token::Colon)?;

        // loops through the argument list after the filter name
        while iter.peek() != None && iter.peek().unwrap() != &&Token::Pipe {
            args.push(iter.next().unwrap().to_arg()?);

            // ensure that the next token is either a Comma or a Pipe
            match iter.peek() {
                Some(&&Token::Comma) => {
                    let _ = iter.next().unwrap();
                    continue;
                }
                Some(&&Token::Pipe) |
                None => break,
                _ => {
                    return Error::parser("a comma or a pipe", Some(iter.next().unwrap()));
                }
            }
        }

        filters.push(FilterPrototype::new(name, args));
    }

    Ok(Output::new(entry, filters))
}

// a tag can be either a single-element tag or a block, which can contain other
// elements and is delimited by a closing tag named {{end +
// the_name_of_the_tag}}. Tags do not get rendered, but blocks may contain
// renderable expressions
fn parse_tag(iter: &mut Iter<Element>,
             tokens: &[Token],
             options: &LiquidOptions)
             -> Result<Box<Renderable>> {
    let tag = &tokens[0];
    match *tag {
        // is a tag
        Token::Identifier(ref x) if options.tags.contains_key(x) => {
            options.tags[x].parse(x, &tokens[1..], options)
        }

        // is a block
        Token::Identifier(ref x) if options.blocks.contains_key(x) => {
            // Collect all the inner elements of this block until we find a
            // matching "end<blockname>" tag. Note that there may be nested blocks
            // of the same type (and hence have the same closing delimiter) *inside*
            // the body of the block, which would premauturely stop the element
            // collection early if we did a nesting-unaware search for the
            // closing tag.
            //
            // The whole nesting count machinery below is to ensure we only stop
            // collecting elements when we have an un-nested closing tag.

            let end_tag = Token::Identifier("end".to_owned() + x);
            let mut children = vec![];
            let mut nesting_depth = 0;
            for t in iter {
                if let Element::Tag(ref tokens, _) = *t {
                    match tokens[0] {
                        ref n if n == tag => {
                            nesting_depth += 1;
                        }
                        ref n if n == &end_tag && nesting_depth > 0 => {
                            nesting_depth -= 1;
                        }
                        ref n if n == &end_tag && nesting_depth == 0 => break,
                        _ => {}
                    }
                };
                children.push(t.clone())
            }
            options.blocks[x].parse(x, &tokens[1..], &children, options)
        }

        ref x => Err(Error::Parser(format!("parse_tag: {:?} not implemented", x))),
    }
}

/// Confirm that the next token in a token stream is what you want it
/// to be. The token iterator is moved to the next token in the stream.
pub fn expect<'a, T>(tokens: &mut T, expected: &Token) -> Result<&'a Token>
    where T: Iterator<Item = &'a Token>
{
    match tokens.next() {
        Some(x) if x == expected => Ok(x),
        x => Error::parser(&expected.to_string(), x),
    }
}

/// Extracts a token from the token stream that can be used to express a
/// value. For our purposes, this is either a string literal, number literal
/// or an identifier that might refer to a variable.
pub fn consume_value_token(tokens: &mut Iter<Token>) -> Result<Token> {
    match tokens.next() {
        Some(t) => value_token(t.clone()),
        None => Error::parser("string | number | identifier", None),
    }
}

/// Recognises a value token, returning an error if a non-value token
/// is presented.
pub fn value_token(t: Token) -> Result<Token> {
    match t {
        v @ Token::StringLiteral(_) |
        v @ Token::NumberLiteral(_) |
        v @ Token::BooleanLiteral(_) |
        v @ Token::Identifier(_) => Ok(v),
        x => Error::parser("string | number | boolean | identifier", Some(&x)),
    }
}

/// Describes the optional trailing part of a block split.
pub struct BlockSplit<'a> {
    pub delimiter: String,
    pub args: &'a [Token],
    pub trailing: &'a [Element],
}

/// A sub-block aware splitter that will only split the token stream
/// when it finds a delimter at the top level of the token stream,
/// ignoring any it finds in nested blocks.
///
/// Returns a slice contaiing all elements before the delimiter, and
/// an optional `BlockSplit` struct describing the delimiter and
/// trailing elements.
pub fn split_block<'a>(tokens: &'a [Element],
                       delimiters: &[&str],
                       options: &LiquidOptions)
                       -> (&'a [Element], Option<BlockSplit<'a>>) {
    // construct a fast-lookup cache of the delimiters, as we're going to be
    // consulting the delimiter list a *lot*.
    let delims: HashSet<&str> = HashSet::from_iter(delimiters.iter().map(|x| *x));
    let mut stack: Vec<String> = Vec::new();

    for (i, t) in tokens.iter().enumerate() {
        if let Element::Tag(ref args, _) = *t {
            match args[0] {
                Token::Identifier(ref name) if options.blocks.contains_key(name) => {
                    stack.push("end".to_owned() + name);
                }

                Token::Identifier(ref name) if Some(name) == stack.last() => {
                    stack.pop();
                }

                Token::Identifier(ref name) if stack.is_empty() &&
                                               delims.contains(name.as_str()) => {
                    let leading = &tokens[0..i];
                    let split = BlockSplit {
                        delimiter: name.clone(),
                        args: args,
                        trailing: &tokens[i..],
                    };
                    return (leading, Some(split));
                }
                _ => {}
            }
        }
    }

    (&tokens[..], None)
}

#[cfg(test)]
mod test_parse_output {
    use super::*;
    use value::Value;
    use super::super::lexer::granularize;
    use interpreter::Argument;

    #[test]
    fn parses_filters() {
        let tokens = granularize("abc | def:'1',2,'3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap(),
                   Output::new(Argument::Var(Variable::new("abc")),
                               vec![FilterPrototype::new("def",
                                                         vec![Argument::Val(Value::str("1")),
                                                              Argument::Val(Value::Num(2.0)),
                                                              Argument::Val(Value::str("3"))]),
                                    FilterPrototype::new("blabla", vec![])]));
    }

    #[test]
    fn requires_filter_names() {
        let tokens = granularize("abc | '1','2','3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected an identifier, found 1");
    }

    #[test]
    fn fails_on_missing_pipes() {
        let tokens = granularize("abc | def:'1',2,'3' blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected a comma or a pipe, found blabla");
    }

    #[test]
    fn fails_on_missing_colons() {
        let tokens = granularize("abc | def '1',2,'3' | blabla").unwrap();

        let result = parse_output(&tokens);
        assert_eq!(result.unwrap_err().to_string(),
                   "Parsing error: Expected :, found 1");
    }
}

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

    #[test]
    fn rejects_unexpected_token() {
        let token_vec = vec![Token::Pipe, Token::Dot, Token::Colon];
        let mut tokens = token_vec.iter();

        assert!(expect(&mut tokens, &Token::Pipe).is_ok());
        assert!(expect(&mut tokens, &Token::Dot).is_ok());
        assert!(expect(&mut tokens, &Token::Comma).is_err());
    }
}

#[cfg(test)]
mod test_split_block {
    use std::collections::HashMap;
    use super::*;
    use super::super::tokenize;
    use super::super::split_block;
    use super::super::BoxedBlockParser;
    use super::super::FnParseBlock;
    use interpreter::Renderable;
    use interpreter::Context;
    use interpreter;

    #[derive(Debug)]
    struct NullBlock;

    impl Renderable for NullBlock {
        fn render(&self, _context: &mut Context) -> Result<Option<String>> {
            Ok(None)
        }
    }

    pub fn null_block(_tag_name: &str,
                      _arguments: &[Token],
                      _tokens: &[Element],
                      _options: &LiquidOptions)
                      -> Result<Box<Renderable>> {
        Ok(Box::new(NullBlock))
    }

    fn options() -> LiquidOptions {
        let mut options = LiquidOptions::default();
        let blocks: HashMap<String, BoxedBlockParser> = ["comment", "for", "if"]
            .into_iter()
            .map(|name| (name.to_string(), (null_block as FnParseBlock).into()))
            .collect();
        options.blocks = blocks;
        options
    }

    #[test]
    fn parse_empty_expression() {
        let text = "{{}}";

        let tokens = tokenize(&text).unwrap();
        let template = parse(&tokens, &options()).map(interpreter::Template::new);
        assert!(template.is_err());
    }

    #[test]
    fn handles_nonmatching_stream() {
        // A stream of tokens with lots of `else`s in it, but only one at the
        // top level, which is where it should split.
        let tokens = tokenize("{% comment %}A{%endcomment%} bunch of {{text}} with {{no}} \
                                   else tag")
            .unwrap();

        // note that we need an options block that has been initilaised with
        // the supported block list; otherwise the split_tag function won't know
        // which things start a nested block.
        let options = options();
        let (_, trailing) = split_block(&tokens[..], &["else"], &options);
        assert!(trailing.is_none());
    }

    #[test]
    fn honours_nesting() {
        // A stream of tokens with lots of `else`s in it, but only one at the
        // top level, which is where it should split.
        let tokens = tokenize(concat!("{% for x in (1..10) %}",
                                      "{% if x == 2 %}",
                                      "{% for y (2..10) %}{{y}}{% else %} zz {% endfor %}",
                                      "{% else %}",
                                      "c",
                                      "{% endif %}",
                                      "{% else %}",
                                      "something",
                                      "{% endfor %}",
                                      "{% else %}",
                                      "trailing tags"))
            .unwrap();

        // note that we need an options block that has been initilaised with
        // the supported block list; otherwise the split_tag function won't know
        // which things start a nested block.
        let options = options();
        let (_, trailing) = split_block(&tokens[..], &["else"], &options);
        match trailing {
            Some(split) => {
                assert_eq!(split.delimiter, "else");
                assert_eq!(split.args, &[Token::Identifier("else".to_owned())]);
                assert_eq!(split.trailing,
                           &[Element::Tag(vec![Token::Identifier("else".to_owned())],
                                          "{% else %}".to_owned()),
                             Element::Raw("trailing tags".to_owned())]);
            }
            None => panic!("split failed"),
        }
    }
}
<|endoftext|>


<commit_before>extern crate rs6502;

use std::io::{self, BufRead, Write};
use std::sync::mpsc::{channel, Receiver};
use std::thread;

use rs6502::{CodeSegment, Cpu, Disassembler};

const HELPTEXT: &'static str = "

HAKKA
-----

Commands:

break | b: addr
       - Toggles a breakpoint at a specific address. If the
         program counter hits this address, execution
         stops.

step | s
       - Executes the current instruction before breaking
         execution again.

continue | c
       - Resumes execution of code within the virtual machine.

list
       - Lists the code surrounding the current program
         counter.

memset | set: addr args [args, ...]
       - memset sets the value of memory directly.

memdmp | dmp: page || start end
       - memdmp dumps a single memory page, or a specified
         memory range from <start> to <end> (inclusive).

monitor | mon: start end
       - monitor dumps the memory between start and end (inclusive)
         every second. Press ENTER to stop the monitor.

source
       - Lists the code currently running in the virtual
         machine. A '>' symbol indicates the current
         program counter.

registers | reg
       - Lists the CPU registers and their current values.

help
       - Lists this help text.
";

#[derive(Debug)]
pub struct MemoryMonitor {
    pub enabled: bool,
    start_addr: usize,
    end_addr: usize,
}

pub struct VirtualMachine {
    pub cpu: Cpu,
    pub monitor: MemoryMonitor,
    segments: Vec<CodeSegment>,
    clock_rate: Option<u32>,
    receiver: Receiver<String>,
    last_command: String,
    breakpoints: [u8; 64 * 1024],
    broken: bool,
    step: bool,
}

impl VirtualMachine {
    pub fn new<CR>(cpu: Cpu, clock_rate: CR) -> VirtualMachine
        where CR: Into<Option<u32>>
    {
        let (tx, rx) = channel();

        thread::spawn(move || {
            println!("Welcome to hakka. Type 'help' to get started.");
            println!("");

            std::io::stdout().write(b"hakka> ").unwrap();
            std::io::stdout().flush().unwrap();

            loop {
                let mut line = String::new();
                let stdin = io::stdin();
                let mut lock = stdin.lock();
                if let Ok(_) = lock.read_line(&mut line) {
                    tx.send(line).unwrap();
                } else {
                    break;
                }
            }
        });

        VirtualMachine {
            cpu: cpu,
            segments: Vec::new(),
            clock_rate: clock_rate.into(),
            receiver: rx,
            monitor: MemoryMonitor {
                enabled: false,
                start_addr: 0,
                end_addr: 0,
            },
            last_command: "".into(),
            breakpoints: [0; 64 * 1024],
            broken: false,
            step: false,
        }
    }

    pub fn load_code_segments(&mut self, segments: Vec<CodeSegment>) {
        if segments.len() == 0 {
            return;
        }
        self.segments = segments;
        for segment in &self.segments {
            self.cpu.load(&segment.code, segment.address);
        }

        self.cpu.registers.PC = self.segments[0].address;
    }

    /// Cycles the Virtual Machine CPU according to the clock rate
    pub fn cycle(&mut self) {
        if let Some(clock_rate) = self.clock_rate {
            let mut n = 0;
            while (n < clock_rate && !self.broken) || self.step {
                n += self.cpu.step().expect("SEGFAULT") as u32;
                if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                    self.broken = true;
                    println!("");
                    println!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC);
                }
                // If we stepped, dump the local disassembly
                if self.step {
                    self.dump_local_disassembly();
                }
                self.step = false;
            }
        } else {
            self.cpu.step().expect("SEGFAULT");
            if self.step {
                self.dump_local_disassembly();
            }
            self.step = false;
            if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                self.broken = true;
                println!("");
                println!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC);
            }
        }
    }

    pub fn try_execute_command(&mut self) {
        if let Ok(input) = self.receiver.try_recv() {
            let mut input: String = input.trim().into();

            if input.len() == 0 {
                if self.monitor.enabled {
                    self.monitor.enabled = false;
                }
            }

            if input == "r" || input == "repeat" {
                input = self.last_command.clone();
            }

            let parts = input.split(" ").collect::<Vec<_>>();

            if parts[0] == "help" {
                print!("{}", HELPTEXT);
            }

            if parts[0] == "source" {
                self.dump_disassembly();
            }

            if parts[0] == "list" {
                self.dump_local_disassembly();
            }

            if parts[0] == "monitor" || parts[0] == "mon" {
                if self.monitor.enabled {
                    self.monitor.enabled = false;
                } else {
                    self.enable_memory_monitor(&input);
                }
            }

            if parts[0] == "memset" || parts[0] == "set" {
                if parts.len() < 3 {
                    println!("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
                } else if parts.len() == 3 {
                    if let Ok(dst) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                        if let Ok(src) = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                            self.cpu.memory[dst as usize] = src;
                        } else {
                            println!("ERR: Unable to parse source byte value");
                        }
                    } else {
                        println!("ERR: Unable to parse destination byte value");
                    }
                } else {
                    if let Ok(mut dst) = usize::from_str_radix(&parts[1].replace("0x", "")[..],
                                                               16) {
                        for p in &parts[2..] {
                            if let Ok(byte) = u8::from_str_radix(&p.replace("0x", "")[..], 16) {
                                self.cpu.memory[dst] = byte;
                                dst += 0x01;
                            } else {
                                println!("ERR: Unable to parse source byte value");
                            }
                        }
                    } else {
                        println!("ERR: Unable to parse destination byte value");
                    }
                }
            }

            if parts[0] == "memdmp" || parts[0] == "dmp" {
                // 1 argument assumes 1 memory "page"
                if parts.len() == 2 {
                    if let Ok(page_number) = parts[1].parse() {

                        self.dump_memory_page(page_number);
                    } else {
                        println!("ERR: Unable to parse memory page");
                    }
                } else if parts.len() == 3 {
                    // A memory range instead
                    if let Ok(start) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                        if let Ok(end) = u16::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                            self.dump_memory_range(start, end);
                        } else {
                            println!("ERR: Unable to parse end address value");
                        }
                    } else {
                        println!("ERR: Unable to parse start address value");
                    }
                }
            }

            if parts[0] == "registers" || parts[0] == "reg" {
                self.dump_registers();
            }

            if parts[0] == "break" || parts[0] == "b" {
                // 1 argument assumes 1 memory "page"
                if parts.len() == 2 {
                    if let Ok(addr) = usize::from_str_radix(&parts[1][..], 16) {
                        if addr <= u16::max_value() as usize {
                            if self.breakpoints[addr] > 0 {
                                self.breakpoints[addr] = 0;
                            } else {
                                self.breakpoints[addr] = 1;
                            }
                        } else {
                            println!("ERR: Value outside addressable range.");
                        }
                    } else {
                        println!("ERR: Unable to parse breakpoint address");
                    }
                } else {
                    println!("ERR: Requires 1 argument");
                }
            }

            if parts[0] == "continue" || parts[0] == "c" {
                self.broken = false;
                println!("Execution resumed");
            }

            if parts[0] == "step" || parts[0] == "s" {
                self.step = true;
            }

            std::io::stdout().write(b"hakka> ").unwrap();
            std::io::stdout().flush().unwrap();

            // Don't assign a blank command as a last command
            if input.len() > 0 {
                self.last_command = input.clone();
            }
        }

    }

    fn enable_memory_monitor(&mut self, input: &str) {
        let parts: Vec<&str> = input.split(" ").collect();
        if parts.len() < 3 {
            println!("ERR: Requires 2 arguments. Example: monitor 0x00 0xFF");
        } else {
            let start = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
            let end = usize::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

            self.monitor.start_addr = start;
            self.monitor.end_addr = end;
            self.monitor.enabled = true;
        }
    }

    fn dump_disassembly(&self) {
        println!("");
        println!("-- Disassembly --");

        for segment in &self.segments {
            println!(".ORG ${:04X}", segment.address);
            let disassembler = Disassembler::with_offset(segment.address);
            let pairs = disassembler.disassemble_with_addresses(&segment.code);
            let result = self.highlight_lines(self.cpu.registers.PC as usize,
                                 pairs,
                                 segment.address,
                                 false)
                .join("");
            print!("{}", result);
            println!("");
        }
    }

    fn dump_local_disassembly(&self) {
        println!("");
        println!("-- Disassembly --");

        let pc = self.cpu.registers.PC as usize;
        let local_segment = self.get_local_segment(pc);
        let disassembler = Disassembler::with_offset(local_segment.address);
        let pairs = disassembler.disassemble_with_addresses(&local_segment.code);
        let result = self.highlight_lines(pc, pairs, local_segment.address, true).join("");
        print!("{}", result);
    }

    pub fn dump_memory_page(&self, page: usize) {
        let mut addr = page * 0x100;
        println!("-- Memory dump --");
        for chunk in self.cpu.memory[page * 0x100..(page * 0x100) + 0x100].chunks(8) {
            print!("{:04X}: ", addr);
            for b in chunk {
                print!("{:02X} ", *b);
            }
            addr += 0x08;
            println!("");
        }
    }

    pub fn dump_memory(&self) {
        println!("-- Memory dump --");
        for chunk in self.cpu.memory[self.monitor.start_addr..self.monitor.end_addr + 0x01]
            .chunks(8) {
            for b in chunk {
                print!("{:02X} ", *b);
            }
            println!("");
        }
    }

    fn dump_memory_range(&self, start: u16, end: u16) {
        println!("-- Memory dump --");
        let start = start as usize;
        let end = end as usize;
        for chunk in self.cpu.memory[start..end + 0x01].chunks(8) {
            for b in chunk {
                print!("{:02X} ", *b);
            }
            println!("");
        }
    }

    fn dump_registers(&self) {
        println!("-- Registers --");
        println!("A: {} ({:04X})", self.cpu.registers.A, self.cpu.registers.A);
        println!("X: {} ({:04X})", self.cpu.registers.X, self.cpu.registers.X);
        println!("Y: {} ({:04X})", self.cpu.registers.Y, self.cpu.registers.Y);
        println!("PC: {} ({:04X})",
                 self.cpu.registers.PC,
                 self.cpu.registers.PC);
        println!("S: {} ({:04X})",
                 self.cpu.stack.pointer,
                 self.cpu.stack.pointer);
    }

    fn get_local_segment(&self, pc: usize) -> &CodeSegment {
        for segment in &self.segments {
            let addr = segment.address as usize;
            if pc >= addr && pc <= addr + segment.code.len() {
                return segment;
            }
        }

        &self.segments[0]
    }

    fn highlight_lines(&self,
                       pc: usize,
                       pairs: Vec<(String, u16)>,
                       segment_start: u16,
                       limit_results: bool)
                       -> Vec<String> {
        let mut result = Vec::new();

        let base = pc as isize - segment_start as isize;

        for pair in pairs {
            if limit_results {
                let start = if base > 0x0A { base - 0x0A } else { 0 };
                if (pair.1 as isize) < start || (pair.1 as isize) > base + 0x0A {
                    continue;
                }
            }
            let current_line = pc as u16 == segment_start + pair.1;
            let breakpoint = self.breakpoints[segment_start as usize + pair.1 as usize] > 0x00;

            if breakpoint && current_line {
                result.push(format!("> * {}", pair.0));
            } else if breakpoint && !current_line {
                result.push(format!("  * {}", pair.0));
            } else if !breakpoint && current_line {
                result.push(format!(">   {}", pair.0));
            } else {
                result.push(format!("    {}", pair.0));
            }
        }

        result
    }
}<commit_msg>Add ways to enter stepping mode without setting a breakpoint first<commit_after>extern crate rs6502;

use std::io::{self, BufRead, Write};
use std::sync::mpsc::{channel, Receiver};
use std::thread;

use rs6502::{CodeSegment, Cpu, Disassembler};

const HELPTEXT: &'static str = "

HAKKA
-----

Commands:

break | b: addr
       - Toggles a breakpoint at a specific address. If the
         program counter hits this address, execution
         stops.

step | s
       - Executes the current instruction before breaking
         execution again.

continue | c
       - Resumes execution of code within the virtual machine.

list
       - Lists the code surrounding the current program
         counter.

memset | set: addr args [args, ...]
       - memset sets the value of memory directly.

memdmp | dmp: page || start end
       - memdmp dumps a single memory page, or a specified
         memory range from <start> to <end> (inclusive).

monitor | mon: start end
       - monitor dumps the memory between start and end (inclusive)
         every second. Press ENTER to stop the monitor.

source
       - Lists the code currently running in the virtual
         machine. A '>' symbol indicates the current
         program counter.

registers | reg
       - Lists the CPU registers and their current values.

help
       - Lists this help text.
";

#[derive(Debug)]
pub struct MemoryMonitor {
    pub enabled: bool,
    start_addr: usize,
    end_addr: usize,
}

pub struct VirtualMachine {
    pub cpu: Cpu,
    pub monitor: MemoryMonitor,
    segments: Vec<CodeSegment>,
    clock_rate: Option<u32>,
    receiver: Receiver<String>,
    last_command: String,
    breakpoints: [u8; 64 * 1024],
    broken: bool,
    step: bool,
}

impl VirtualMachine {
    pub fn new<CR>(cpu: Cpu, clock_rate: CR) -> VirtualMachine
        where CR: Into<Option<u32>>
    {
        let (tx, rx) = channel();

        thread::spawn(move || {
            println!("Welcome to hakka. Type 'help' to get started.");
            println!("");

            std::io::stdout().write(b"hakka> ").unwrap();
            std::io::stdout().flush().unwrap();

            loop {
                let mut line = String::new();
                let stdin = io::stdin();
                let mut lock = stdin.lock();
                if let Ok(_) = lock.read_line(&mut line) {
                    tx.send(line).unwrap();
                } else {
                    break;
                }
            }
        });

        VirtualMachine {
            cpu: cpu,
            segments: Vec::new(),
            clock_rate: clock_rate.into(),
            receiver: rx,
            monitor: MemoryMonitor {
                enabled: false,
                start_addr: 0,
                end_addr: 0,
            },
            last_command: "".into(),
            breakpoints: [0; 64 * 1024],
            broken: false,
            step: false,
        }
    }

    pub fn load_code_segments(&mut self, segments: Vec<CodeSegment>) {
        if segments.len() == 0 {
            return;
        }
        self.segments = segments;
        for segment in &self.segments {
            self.cpu.load(&segment.code, segment.address);
        }

        self.cpu.registers.PC = self.segments[0].address;
    }

    /// Cycles the Virtual Machine CPU according to the clock rate
    pub fn cycle(&mut self) {
        if let Some(clock_rate) = self.clock_rate {
            let mut n = 0;
            while (n < clock_rate && !self.broken) || self.step {
                n += self.cpu.step().expect("SEGFAULT") as u32;
                if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                    self.broken = true;
                    println!("");
                    println!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC);
                }
                // If we stepped, dump the local disassembly
                if self.step {
                    self.dump_local_disassembly();
                }
                self.step = false;
            }
        } else {
            self.cpu.step().expect("SEGFAULT");
            if self.step {
                self.dump_local_disassembly();
            }
            self.step = false;
            if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                self.broken = true;
                println!("");
                println!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC);
            }
        }
    }

    pub fn try_execute_command(&mut self) {
        if let Ok(input) = self.receiver.try_recv() {
            let mut input: String = input.trim().into();

            if input.len() == 0 {
                if self.monitor.enabled {
                    self.monitor.enabled = false;
                }
            }

            if input == "r" || input == "repeat" {
                input = self.last_command.clone();
            }

            let parts = input.split(" ").collect::<Vec<_>>();

            if parts[0] == "help" {
                print!("{}", HELPTEXT);
            }

            if parts[0] == "source" {
                self.dump_disassembly();
            }

            if parts[0] == "list" {
                self.dump_local_disassembly();
            }

            if parts[0] == "monitor" || parts[0] == "mon" {
                if self.monitor.enabled {
                    self.monitor.enabled = false;
                } else {
                    self.enable_memory_monitor(&input);
                }
            }

            if parts[0] == "memset" || parts[0] == "set" {
                if parts.len() < 3 {
                    println!("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
                } else if parts.len() == 3 {
                    if let Ok(dst) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                        if let Ok(src) = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                            self.cpu.memory[dst as usize] = src;
                        } else {
                            println!("ERR: Unable to parse source byte value");
                        }
                    } else {
                        println!("ERR: Unable to parse destination byte value");
                    }
                } else {
                    if let Ok(mut dst) = usize::from_str_radix(&parts[1].replace("0x", "")[..],
                                                               16) {
                        for p in &parts[2..] {
                            if let Ok(byte) = u8::from_str_radix(&p.replace("0x", "")[..], 16) {
                                self.cpu.memory[dst] = byte;
                                dst += 0x01;
                            } else {
                                println!("ERR: Unable to parse source byte value");
                            }
                        }
                    } else {
                        println!("ERR: Unable to parse destination byte value");
                    }
                }
            }

            if parts[0] == "memdmp" || parts[0] == "dmp" {
                // 1 argument assumes 1 memory "page"
                if parts.len() == 2 {
                    if let Ok(page_number) = parts[1].parse() {

                        self.dump_memory_page(page_number);
                    } else {
                        println!("ERR: Unable to parse memory page");
                    }
                } else if parts.len() == 3 {
                    // A memory range instead
                    if let Ok(start) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                        if let Ok(end) = u16::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                            self.dump_memory_range(start, end);
                        } else {
                            println!("ERR: Unable to parse end address value");
                        }
                    } else {
                        println!("ERR: Unable to parse start address value");
                    }
                }
            }

            if parts[0] == "registers" || parts[0] == "reg" {
                self.dump_registers();
            }

            if parts[0] == "break" || parts[0] == "b" {
                // 1 argument assumes 1 memory "page"
                if parts.len() == 2 {
                    if let Ok(addr) = usize::from_str_radix(&parts[1][..], 16) {
                        if addr <= u16::max_value() as usize {
                            if self.breakpoints[addr] > 0 {
                                self.breakpoints[addr] = 0;
                            } else {
                                self.breakpoints[addr] = 1;
                            }
                        } else {
                            println!("ERR: Value outside addressable range.");
                        }
                    } else {
                        println!("ERR: Unable to parse breakpoint address");
                    }
                } else {
                    self.broken = true;
                    println!("Execution stopped");
                }
            }

            if parts[0] == "continue" || parts[0] == "c" {
                self.broken = false;
                println!("Execution resumed");
            }

            if parts[0] == "step" || parts[0] == "s" {
                self.broken = true;
                self.step = true;
            }

            std::io::stdout().write(b"hakka> ").unwrap();
            std::io::stdout().flush().unwrap();

            // Don't assign a blank command as a last command
            if input.len() > 0 {
                self.last_command = input.clone();
            }
        }

    }

    fn enable_memory_monitor(&mut self, input: &str) {
        let parts: Vec<&str> = input.split(" ").collect();
        if parts.len() < 3 {
            println!("ERR: Requires 2 arguments. Example: monitor 0x00 0xFF");
        } else {
            let start = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
            let end = usize::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

            self.monitor.start_addr = start;
            self.monitor.end_addr = end;
            self.monitor.enabled = true;
        }
    }

    fn dump_disassembly(&self) {
        println!("");
        println!("-- Disassembly --");

        for segment in &self.segments {
            println!(".ORG ${:04X}", segment.address);
            let disassembler = Disassembler::with_offset(segment.address);
            let pairs = disassembler.disassemble_with_addresses(&segment.code);
            let result = self.highlight_lines(self.cpu.registers.PC as usize,
                                 pairs,
                                 segment.address,
                                 false)
                .join("");
            print!("{}", result);
            println!("");
        }
    }

    fn dump_local_disassembly(&self) {
        println!("");
        println!("-- Disassembly --");

        let pc = self.cpu.registers.PC as usize;
        let local_segment = self.get_local_segment(pc);
        let disassembler = Disassembler::with_offset(local_segment.address);
        let pairs = disassembler.disassemble_with_addresses(&local_segment.code);
        let result = self.highlight_lines(pc, pairs, local_segment.address, true).join("");
        print!("{}", result);
    }

    pub fn dump_memory_page(&self, page: usize) {
        let mut addr = page * 0x100;
        println!("-- Memory dump --");
        for chunk in self.cpu.memory[page * 0x100..(page * 0x100) + 0x100].chunks(8) {
            print!("{:04X}: ", addr);
            for b in chunk {
                print!("{:02X} ", *b);
            }
            addr += 0x08;
            println!("");
        }
    }

    pub fn dump_memory(&self) {
        println!("-- Memory dump --");
        for chunk in self.cpu.memory[self.monitor.start_addr..self.monitor.end_addr + 0x01]
            .chunks(8) {
            for b in chunk {
                print!("{:02X} ", *b);
            }
            println!("");
        }
    }

    fn dump_memory_range(&self, start: u16, end: u16) {
        println!("-- Memory dump --");
        let start = start as usize;
        let end = end as usize;
        for chunk in self.cpu.memory[start..end + 0x01].chunks(8) {
            for b in chunk {
                print!("{:02X} ", *b);
            }
            println!("");
        }
    }

    fn dump_registers(&self) {
        println!("-- Registers --");
        println!("A: {} ({:04X})", self.cpu.registers.A, self.cpu.registers.A);
        println!("X: {} ({:04X})", self.cpu.registers.X, self.cpu.registers.X);
        println!("Y: {} ({:04X})", self.cpu.registers.Y, self.cpu.registers.Y);
        println!("PC: {} ({:04X})",
                 self.cpu.registers.PC,
                 self.cpu.registers.PC);
        println!("S: {} ({:04X})",
                 self.cpu.stack.pointer,
                 self.cpu.stack.pointer);
    }

    fn get_local_segment(&self, pc: usize) -> &CodeSegment {
        for segment in &self.segments {
            let addr = segment.address as usize;
            if pc >= addr && pc <= addr + segment.code.len() {
                return segment;
            }
        }

        &self.segments[0]
    }

    fn highlight_lines(&self,
                       pc: usize,
                       pairs: Vec<(String, u16)>,
                       segment_start: u16,
                       limit_results: bool)
                       -> Vec<String> {
        let mut result = Vec::new();

        let base = pc as isize - segment_start as isize;

        for pair in pairs {
            if limit_results {
                let start = if base > 0x0A { base - 0x0A } else { 0 };
                if (pair.1 as isize) < start || (pair.1 as isize) > base + 0x0A {
                    continue;
                }
            }
            let current_line = pc as u16 == segment_start + pair.1;
            let breakpoint = self.breakpoints[segment_start as usize + pair.1 as usize] > 0x00;

            if breakpoint && current_line {
                result.push(format!("> * {}", pair.0));
            } else if breakpoint && !current_line {
                result.push(format!("  * {}", pair.0));
            } else if !breakpoint && current_line {
                result.push(format!(">   {}", pair.0));
            } else {
                result.push(format!("    {}", pair.0));
            }
        }

        result
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;
use std::io::Write;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};

use super::super::config::{Tokens, Config, ClientRef};
use super::super::results::{BearerResult, BearerError};
use super::oauth2client;

fn url_encode(to_decode: &str) -> String {
    to_decode.as_bytes().iter().fold(String::new(), |mut out, &b| {
        match b as char {
            // unreserved:
            'A'...'Z' | 'a'...'z' | '0'...'9' | '-' | '.' | '_' | '~' => out.push(b as char),

            ' ' => out.push('+'),

            ch => out.push_str(format!("%{:02X}", ch as usize).as_str()),
        };

        out
    })
}

struct Http<'a> {
    port: usize,
    client: ClientRef<'a>,
    tokens: Option<BearerResult<Tokens>>,
}

impl<'a> Http<'a> {
    pub fn new(config: &'a Config) -> Self {
        Http {
            port: 6750,
            client: config.client(),
            tokens: None,
        }
    }

    pub fn fetch_tokens(&mut self) -> BearerResult<Tokens> {
        let listener = TcpListener::bind(self.addr().as_str()).unwrap();

        while self.tokens.is_none() {
            let stream = listener.incoming().next().unwrap();
            let mut stream = stream.unwrap();
            self.handle_client(&mut stream);
        }
        let tokens = self.tokens.as_ref().unwrap();
        match tokens.as_ref() {
            Ok(tokens) => Ok(tokens.clone()),
            Err(err) => Err(err.clone()),
        }
    }

    fn handle_client(&mut self, stream: &mut TcpStream) {
        stream.set_read_timeout(Some(Duration::new(15, 0))).unwrap();
        let mut buffer = [0; 4096];
        stream.read(&mut buffer[..]).unwrap();
        let httpquery = String::from_utf8_lossy(&buffer);
        // debug!("{}", string);

        // We don't bother the header
        let httpquery = httpquery.lines().next().unwrap();
        // debug!("{}", httpquery);

        let mut httpquery = httpquery.split_whitespace();
        let verb = httpquery.next().unwrap();
        if verb != "GET" {
            self.handle_405(stream);
            return;
        }

        let path = httpquery.next().unwrap();
        let mut path = path.split("?");
        let pathinfo = path.next().unwrap();

        if pathinfo != "/callback" {
            self.handle_404(stream);
            return;
        }

        let querystring = path.next();
        if querystring.is_none() {
            self.handle_302(stream);
            return;
        }

        let querystring = querystring.unwrap().split("&");
        for param in querystring {
            let mut param = param.split("=");
            let key = param.next();
            let value = param.next().unwrap();
            match key {
                Some("error") => self.handle_200_error(stream, value),
                Some("code") => self.handle_200_code(stream, value),
                _ => {}
            }
        }
    }

    fn handle_404(&mut self, stream: &mut TcpStream) {
        stream.write(b"HTTP/1.1 404 Not Found
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: 10

Not Found
")
            .unwrap();
    }

    fn handle_405(&mut self, stream: &mut TcpStream) {
        stream.write(b"HTTP/1.1 405 Method Not Allowed
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: 19

Method Not Allowed
")
            .unwrap();
    }

    fn handle_302(&mut self, stream: &mut TcpStream) {

        let mut location = format!(
            "{}?response_type=code&client_id={}&redirect_uri={}",
            self.client.authorize_url,
            url_encode(self.client.client_id),
            url_encode(self.redirect_uri().as_ref()));

        if let Some(scope) = self.client.scope {
            location.push_str("&scope=");
            location.push_str(scope);
        }

        debug!("Redirecting to {}", location);

        let resp = format!("HTTP/1.1 302 Moved Temporarily
Connection: close
Server: bearer-rs
Location: {}
", location);
        stream.write(resp.as_bytes()).unwrap();
    }

    fn handle_200_code(&mut self, stream: &mut TcpStream, code: &str) {
        debug!("OAuth2.0 Authorization Code received, fetching tokens");

        let tokens = oauth2client::from_authcode(&self.client, code, self.redirect_uri().as_str());
        let content = match tokens {
            Ok(res) => {
                self.tokens = Some(Ok(res));
                "Token received".to_string()
            }
            Err(err) => {
                format!("Error while fetching token: {:?}", err)
            }
        };

        let resp = format!("HTTP/1.1 200 Ok
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: {}

{}",
                           content.len(),
                           content);

        stream.write(resp.as_bytes()).unwrap();

    }

    fn handle_200_error(&mut self, stream: &mut TcpStream, error: &str) {
        let content = format!("No Tokens returns. OAuth2.0 Authorization Server Error: {}",
                              error);
        let resp = format!("HTTP/1.1 200 Ok
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: {}

{}",
                           content.len(),
                           content);

        stream.write(resp.as_bytes()).unwrap();
        self.tokens = Some(Err(BearerError::OAuth2Error(content)));

    }

    fn addr(&self) -> String {
        return format!("127.0.0.1:{}", self.port);
    }

    fn redirect_uri(&self) -> String {
        return format!("http://localhost:{}/callback", self.port);
    }
}

pub fn get_tokens<'a>(config: &'a Config) -> BearerResult<Tokens> {

    let mut server: Http<'a> = Http::new(config);
    let token = server.fetch_tokens()?;
    Ok(token)
}
<commit_msg>Add tests for the fetch tokens<commit_after>use std::time::Duration;
use std::io::Write;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};

use super::super::config::{Tokens, Config, ClientRef};
use super::super::results::{BearerResult, BearerError};
use super::oauth2client;

fn url_encode(to_encode: &str) -> String {
    to_encode.as_bytes().iter().fold(String::new(), |mut out, &b| {
        match b as char {
            // unreserved:
            'A'...'Z' | 'a'...'z' | '0'...'9' | '-' | '.' | '_' | '~' => out.push(b as char),

            ' ' => out.push('+'),

            ch => out.push_str(format!("%{:02X}", ch as usize).as_str()),
        };

        out
    })
}

struct Http<'a> {
    port: usize,
    client: ClientRef<'a>,
    tokens: Option<BearerResult<Tokens>>,
}

impl<'a> Http<'a> {
    pub fn new(config: &'a Config) -> Self {
        Http {
            port: 6750,
            client: config.client(),
            tokens: None,
        }
    }

    pub fn fetch_tokens(&mut self) -> BearerResult<Tokens> {
        let listener = TcpListener::bind(self.addr().as_str()).unwrap();

        while self.tokens.is_none() {
            let stream = listener.incoming().next().unwrap();
            let mut stream = stream.unwrap();
            self.handle_client(&mut stream);
        }
        let tokens = self.tokens.as_ref().unwrap();
        match tokens.as_ref() {
            Ok(tokens) => Ok(tokens.clone()),
            Err(err) => Err(err.clone()),
        }
    }

    fn handle_client(&mut self, stream: &mut TcpStream) {
        stream.set_read_timeout(Some(Duration::new(15, 0))).unwrap();
        let mut buffer = [0; 4096];
        stream.read(&mut buffer[..]).unwrap();
        let httpquery = String::from_utf8_lossy(&buffer);
        // debug!("{}", string);

        // We don't bother the header
        let httpquery = httpquery.lines().next().unwrap();
        // debug!("{}", httpquery);

        let mut httpquery = httpquery.split_whitespace();
        let verb = httpquery.next().unwrap();
        if verb != "GET" {
            self.handle_405(stream);
            return;
        }

        let path = httpquery.next().unwrap();
        let mut path = path.split("?");
        let pathinfo = path.next().unwrap();

        if pathinfo != "/callback" {
            self.handle_404(stream);
            return;
        }

        let querystring = path.next();
        if querystring.is_none() {
            self.handle_302(stream);
            return;
        }

        let querystring = querystring.unwrap().split("&");
        for param in querystring {
            let mut param = param.split("=");
            let key = param.next();
            let value = param.next().unwrap();
            match key {
                Some("error") => self.handle_200_error(stream, value),
                Some("code") => self.handle_200_code(stream, value),
                _ => {}
            }
        }
    }

    fn handle_404(&mut self, stream: &mut TcpStream) {
        stream.write(b"HTTP/1.1 404 Not Found
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: 10

Not Found
")
            .unwrap();
    }

    fn handle_405(&mut self, stream: &mut TcpStream) {
        stream.write(b"HTTP/1.1 405 Method Not Allowed
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: 19

Method Not Allowed
")
            .unwrap();
    }

    fn handle_302(&mut self, stream: &mut TcpStream) {

        let mut location = format!(
            "{}?response_type=code&client_id={}&redirect_uri={}",
            self.client.authorize_url,
            url_encode(self.client.client_id),
            url_encode(self.redirect_uri().as_ref()));

        if let Some(scope) = self.client.scope {
            location.push_str("&scope=");
            location.push_str(scope);
        }

        debug!("Redirecting to {}", location);

        let resp = format!("HTTP/1.1 302 Moved Temporarily
Connection: close
Server: bearer-rs
Location: {}
", location);
        stream.write(resp.as_bytes()).unwrap();
    }

    fn handle_200_code(&mut self, stream: &mut TcpStream, code: &str) {
        debug!("OAuth2.0 Authorization Code received, fetching tokens");

        let tokens = oauth2client::from_authcode(&self.client, code, self.redirect_uri().as_str());
        let content = match tokens {
            Ok(res) => {
                self.tokens = Some(Ok(res));
                "Token received".to_string()
            }
            Err(err) => {
                format!("Error while fetching token: {:?}", err)
            }
        };

        let resp = format!("HTTP/1.1 200 Ok
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: {}

{}",
                           content.len(),
                           content);

        stream.write(resp.as_bytes()).unwrap();

    }

    fn handle_200_error(&mut self, stream: &mut TcpStream, error: &str) {
        let content = format!("No Tokens returns. OAuth2.0 Authorization Server Error: {}",
                              error);
        let resp = format!("HTTP/1.1 200 Ok
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: {}

{}",
                           content.len(),
                           content);

        stream.write(resp.as_bytes()).unwrap();
        self.tokens = Some(Err(BearerError::OAuth2Error(content)));

    }

    fn addr(&self) -> String {
        return format!("127.0.0.1:{}", self.port);
    }

    fn redirect_uri(&self) -> String {
        return format!("http://localhost:{}/callback", self.port);
    }
}

pub fn get_tokens<'a>(config: &'a Config) -> BearerResult<Tokens> {

    let mut server: Http<'a> = Http::new(config);
    let token = server.fetch_tokens()?;
    Ok(token)
}

#[cfg(test)]
mod tests {
    use std::thread;
    use std::time;
    use std::net::TcpStream;

    use super::*;

    #[test]
    fn test_url_encode() {
        assert_eq!(url_encode("The éêè !"), "The+%C3%A9%C3%AA%C3%A8+%21")
    }

    #[test]
    fn test_http_fetch_tokens_ok() {

        let httphandler = thread::spawn(move || {
            let conf = Config::from_file("src/tests/conf", "dummy").unwrap();
            let mut http = Http::new(&conf);
            let tokens = http.fetch_tokens();
            assert_eq!(tokens.is_ok(), true);
            let tokens = tokens.unwrap();
            assert_eq!(tokens.access_token, "atok");
            assert_eq!(tokens.refresh_token.unwrap(), "rtok");
            //assert_eq!(tokens.expires_at, "TIME DEPENDANT VALUE");
        });

        let dur = time::Duration::from_millis(700);
        thread::sleep(dur);

        let mut client = TcpStream::connect("127.0.0.1:6750").unwrap();
        client.write_all(b"GET /callback HTTP/1.1\r\n\r\n").unwrap();
        let mut response = String::new();
        client.read_to_string(&mut response).unwrap();
        assert_eq!(response, r#"HTTP/1.1 302 Moved Temporarily
Connection: close
Server: bearer-rs
Location: http://localhost:1337/authorize?response_type=code&client_id=129eff26&redirect_uri=http%3A%2F%2Flocalhost%3A6750%2Fcallback
"#);

        let authservhandler = thread::spawn(move || {
            let authorization_server = TcpListener::bind("127.0.0.1:1337").unwrap();
            let stream = authorization_server.incoming().next().unwrap();
            let mut stream = stream.unwrap();
            let tokens = r#"{
"access_token": "atok",
"expires_in": 42,
"refresh_token": "rtok"}"#;
            let resp = format!(r#"HTTP/1.0 200 Ok
Content-Type: application/json;
Content-Length: {}

{}"#,
                               tokens.len(),
                               tokens);
            stream.write(resp.as_bytes()).unwrap();
        });

        let dur = time::Duration::from_millis(700);
        thread::sleep(dur);

        let mut client = TcpStream::connect("127.0.0.1:6750").unwrap();
        client.write_all(b"GET /callback?code=abc HTTP/1.1\r\n\r\n").unwrap();
        let mut response = String::new();
        client.read_to_string(&mut response).unwrap();

        assert_eq!(response, r#"HTTP/1.1 200 Ok
Connection: close
Server: bearer-rs
Content-Type: text/plain;charset=UTF-8
Content-Length: 14

Token received"#);

        // ensure threads are terminated
        httphandler.join().unwrap();
        authservhandler.join().unwrap();

    }

}<|endoftext|>