<commit_before>use std::mem;

pub use self::Token::*;
pub use self::ParserState::*;
pub use self::TokenClass::*;

/// `Token` is a section of a compiled mustache string.
#[derive(Clone, Debug)]
pub enum Token {
    Text(String),
    ETag(Vec<String>, String),
    UTag(Vec<String>, String),
    Section(Vec<String>, bool, Vec<Token>, String, String, String, String, String),
    IncompleteSection(Vec<String>, bool, String, bool),
    Partial(String, String, String),
}

enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(String, usize),
    NewLineWhiteSpace(String, usize),
}

/// `Parser` parses a string into a series of `Token`s.
pub struct Parser<'a, T: 'a> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: usize,
    col: usize,
    content: String,
    state: ParserState,
    otag: String,
    ctag: String,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: usize,
    tokens: Vec<Token>,
    partials: Vec<String>,
}

enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<Item=char>> Parser<'a, T> {
    pub fn new(reader: &'a mut T, otag: &str, ctag: &str) -> Parser<'a, T> {
        let mut parser = Parser {
            reader: reader,
            ch: None,
            lookahead: None,
            line: 1,
            col: 1,
            content: String::new(),
            state: TEXT,
            otag: otag.to_string(),
            ctag: ctag.to_string(),
            otag_chars: otag.chars().collect(),
            ctag_chars: ctag.chars().collect(),
            tag_position: 0,
            tokens: Vec::new(),
            partials: Vec::new(),
        };

        parser.bump();
        parser
    }

    fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        match self.ch {
            Some(ch) => {
                if ch == '\n' {
                    self.line += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
            }
            None => { }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    /// Parse the template into tokens and a list of partial files.
    pub fn parse(mut self) -> (Vec<Token>, Vec<String>) {
        let mut curly_brace_tag = false;

        loop {
            let ch = match self.ch {
                Some(ch) => ch,
                None => { break; }
            };

            match self.state {
                TEXT => {
                    if ch == self.otag_chars[0] {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == self.otag_chars[self.tag_position] {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content.is_empty() && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push(ch);
                        self.bump();
                    } else if ch == self.ctag_chars[0] {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == self.ctag_chars[self.tag_position] {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push(ch);
                            self.bump();
                        }
                    } else {
                        panic!("character {} is not part of CTAG: {}",
                              ch,
                              self.ctag_chars[self.tag_position]);
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { panic!("unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            match *token {
                IncompleteSection(ref path, _, _, _) => {
                    panic!("Unclosed mustache section {}", path.join("."));
              }
              _ => {}
            }
        };

        let Parser { tokens, partials, .. } = self;

        (tokens, partials)
    }

    fn add_text(&mut self) {
        if !self.content.is_empty() {
            let mut content = String::new();
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content.to_string()));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        match self.ch {
            None => { }
            Some(ch) => {
                if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                    return Normal;
                }
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(| c:char| c == '\n' || !c.is_whitespace()) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.to_string(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.as_bytes()[pos] == b'\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.to_string(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag.clone() + &self.content + &self.ctag;

        // Move the content to avoid a copy.
        let mut content = String::new();
        mem::swap(&mut content, &mut self.content);
        let len = content.len();
        let content = content;

        match content.as_bytes()[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = &content[1..len];
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with("}") {
                    let name = &content[1..len - 1];
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { panic!("unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.len() == 0 {
                        panic!("closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => panic!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = String::new();
                                for s in srcs.iter() { src.push_str(s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.to_string(),
                                        osection,
                                        src.to_string(),
                                        tag,
                                        self.ctag.to_string()));
                                break;
                            } else {
                                panic!("Unclosed section");
                            }
                        }
                        _ => { match last {
                            Some(last_token) => {children.push(last_token); }
                            None => ()
                            }
                        }
                    }
                }
            }
            '>' => { self.add_partial(&content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2usize && content.ends_with("=") {
                    let s = self.check_content(&content[1..len - 1]);

                    fn is_whitespace(c: char) -> bool { c.is_whitespace() }
                    let pos = s.find(is_whitespace);
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s[0..pos].to_string();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = &s[pos..];
                    let pos = s2.find(| c : char| !c.is_whitespace());
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2[pos..].to_string();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    panic!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(&content);
                let name = if name == "." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: String) {
        let indent = match self.classify_token() {
            Normal => "".to_string(),
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                "".to_string()
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = &s[pos..];

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                ws.to_string()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = &content[1..content.len()];
        let name = self.check_content(name);

        self.tokens.push(Partial(name.to_string(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        for (i, ch) in self.otag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn not_ctag(&mut self) {
        for (i, ch) in self.ctag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn check_content(&self, content: &str) -> String {
        let trimmed = content.trim();
        if trimmed.len() == 0 {
            panic!("empty tag");
        }
        trimmed.to_string()
    }
}
<commit_msg>fix(warnings): stop publically exporting unused private variants from parser<commit_after>use std::mem;

pub use self::Token::*;

use self::TokenClass::*;
use self::ParserState::*;

/// `Token` is a section of a compiled mustache string.
#[derive(Clone, Debug)]
pub enum Token {
    Text(String),
    ETag(Vec<String>, String),
    UTag(Vec<String>, String),
    Section(Vec<String>, bool, Vec<Token>, String, String, String, String, String),
    IncompleteSection(Vec<String>, bool, String, bool),
    Partial(String, String, String),
}

enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(String, usize),
    NewLineWhiteSpace(String, usize),
}

/// `Parser` parses a string into a series of `Token`s.
pub struct Parser<'a, T: 'a> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: usize,
    col: usize,
    content: String,
    state: ParserState,
    otag: String,
    ctag: String,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: usize,
    tokens: Vec<Token>,
    partials: Vec<String>,
}

enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<Item=char>> Parser<'a, T> {
    pub fn new(reader: &'a mut T, otag: &str, ctag: &str) -> Parser<'a, T> {
        let mut parser = Parser {
            reader: reader,
            ch: None,
            lookahead: None,
            line: 1,
            col: 1,
            content: String::new(),
            state: TEXT,
            otag: otag.to_string(),
            ctag: ctag.to_string(),
            otag_chars: otag.chars().collect(),
            ctag_chars: ctag.chars().collect(),
            tag_position: 0,
            tokens: Vec::new(),
            partials: Vec::new(),
        };

        parser.bump();
        parser
    }

    fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        match self.ch {
            Some(ch) => {
                if ch == '\n' {
                    self.line += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
            }
            None => { }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    /// Parse the template into tokens and a list of partial files.
    pub fn parse(mut self) -> (Vec<Token>, Vec<String>) {
        let mut curly_brace_tag = false;

        loop {
            let ch = match self.ch {
                Some(ch) => ch,
                None => { break; }
            };

            match self.state {
                TEXT => {
                    if ch == self.otag_chars[0] {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == self.otag_chars[self.tag_position] {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content.is_empty() && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push(ch);
                        self.bump();
                    } else if ch == self.ctag_chars[0] {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == self.ctag_chars[self.tag_position] {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push(ch);
                            self.bump();
                        }
                    } else {
                        panic!("character {} is not part of CTAG: {}",
                              ch,
                              self.ctag_chars[self.tag_position]);
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { panic!("unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            match *token {
                IncompleteSection(ref path, _, _, _) => {
                    panic!("Unclosed mustache section {}", path.join("."));
              }
              _ => {}
            }
        };

        let Parser { tokens, partials, .. } = self;

        (tokens, partials)
    }

    fn add_text(&mut self) {
        if !self.content.is_empty() {
            let mut content = String::new();
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content.to_string()));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        match self.ch {
            None => { }
            Some(ch) => {
                if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                    return Normal;
                }
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(| c:char| c == '\n' || !c.is_whitespace()) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.to_string(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.as_bytes()[pos] == b'\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.to_string(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag.clone() + &self.content + &self.ctag;

        // Move the content to avoid a copy.
        let mut content = String::new();
        mem::swap(&mut content, &mut self.content);
        let len = content.len();
        let content = content;

        match content.as_bytes()[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = &content[1..len];
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with("}") {
                    let name = &content[1..len - 1];
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { panic!("unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.len() == 0 {
                        panic!("closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => panic!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = String::new();
                                for s in srcs.iter() { src.push_str(s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.to_string(),
                                        osection,
                                        src.to_string(),
                                        tag,
                                        self.ctag.to_string()));
                                break;
                            } else {
                                panic!("Unclosed section");
                            }
                        }
                        _ => { match last {
                            Some(last_token) => {children.push(last_token); }
                            None => ()
                            }
                        }
                    }
                }
            }
            '>' => { self.add_partial(&content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2usize && content.ends_with("=") {
                    let s = self.check_content(&content[1..len - 1]);

                    fn is_whitespace(c: char) -> bool { c.is_whitespace() }
                    let pos = s.find(is_whitespace);
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s[0..pos].to_string();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = &s[pos..];
                    let pos = s2.find(| c : char| !c.is_whitespace());
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2[pos..].to_string();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    panic!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(&content);
                let name = if name == "." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: String) {
        let indent = match self.classify_token() {
            Normal => "".to_string(),
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                "".to_string()
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = &s[pos..];

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                ws.to_string()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = &content[1..content.len()];
        let name = self.check_content(name);

        self.tokens.push(Partial(name.to_string(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        for (i, ch) in self.otag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn not_ctag(&mut self) {
        for (i, ch) in self.ctag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn check_content(&self, content: &str) -> String {
        let trimmed = content.trim();
        if trimmed.len() == 0 {
            panic!("empty tag");
        }
        trimmed.to_string()
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::Read;
use std::path;
use std::sync;

use liquid_core::error::{Result, ResultLiquidExt, ResultLiquidReplaceExt};
use liquid_core::parser;
use liquid_core::runtime;

use super::Template;
use crate::reflection;
use liquid_core::partials;
#[cfg(feature = "stdlib")]
use liquid_lib::stdlib;

type Partials = partials::EagerCompiler<partials::InMemorySource>;

pub struct ParserBuilder

where
    P: partials::PartialCompiler,
{
    blocks: parser::PluginRegistry<Box<dyn parser::ParseBlock>>,
    tags: parser::PluginRegistry<Box<dyn parser::ParseTag>>,
    filters: parser::PluginRegistry<Box<dyn parser::ParseFilter>>,
    partials: Option
,
}

impl ParserBuilder<Partials> {
    /// Create an empty Liquid parser
    pub fn new() -> Self {
        Self::default()
    }

    #[cfg(feature = "stdlib")]
    pub fn with_stdlib() -> Self {
        Self::new().stdlib()
    }
}

impl
 ParserBuilder

where
    P: partials::PartialCompiler,
{
    #[cfg(feature = "stdlib")]
    /// Create a Liquid parser with built-in Liquid features
    pub fn stdlib(self) -> Self {
        self.tag(stdlib::AssignTag)
            .tag(stdlib::BreakTag)
            .tag(stdlib::ContinueTag)
            .tag(stdlib::CycleTag)
            .tag(stdlib::IncludeTag)
            .tag(stdlib::IncrementTag)
            .tag(stdlib::DecrementTag)
            .block(stdlib::RawBlock)
            .block(stdlib::IfBlock)
            .block(stdlib::UnlessBlock)
            .block(stdlib::IfChangedBlock)
            .block(stdlib::ForBlock)
            .block(stdlib::TableRowBlock)
            .block(stdlib::CommentBlock)
            .block(stdlib::CaptureBlock)
            .block(stdlib::CaseBlock)
            .filter(stdlib::Abs)
            .filter(stdlib::Append)
            .filter(stdlib::AtLeast)
            .filter(stdlib::AtMost)
            .filter(stdlib::Capitalize)
            .filter(stdlib::Ceil)
            .filter(stdlib::Compact)
            .filter(stdlib::Concat)
            .filter(stdlib::Date)
            .filter(stdlib::Default)
            .filter(stdlib::DividedBy)
            .filter(stdlib::Downcase)
            .filter(stdlib::Escape)
            .filter(stdlib::EscapeOnce)
            .filter(stdlib::First)
            .filter(stdlib::Floor)
            .filter(stdlib::Join)
            .filter(stdlib::Last)
            .filter(stdlib::Lstrip)
            .filter(stdlib::Map)
            .filter(stdlib::Minus)
            .filter(stdlib::Modulo)
            .filter(stdlib::NewlineToBr)
            .filter(stdlib::Plus)
            .filter(stdlib::Prepend)
            .filter(stdlib::Remove)
            .filter(stdlib::RemoveFirst)
            .filter(stdlib::Replace)
            .filter(stdlib::ReplaceFirst)
            .filter(stdlib::Reverse)
            .filter(stdlib::Round)
            .filter(stdlib::Rstrip)
            .filter(stdlib::Size)
            .filter(stdlib::Slice)
            .filter(stdlib::Sort)
            .filter(stdlib::SortNatural)
            .filter(stdlib::Split)
            .filter(stdlib::Strip)
            .filter(stdlib::StripHtml)
            .filter(stdlib::StripNewlines)
            .filter(stdlib::Times)
            .filter(stdlib::Truncate)
            .filter(stdlib::TruncateWords)
            .filter(stdlib::Uniq)
            .filter(stdlib::Upcase)
            .filter(stdlib::UrlDecode)
            .filter(stdlib::UrlEncode)
    }

    /// Inserts a new custom block into the parser
    pub fn block<B: Into<Box<dyn parser::ParseBlock>>>(mut self, block: B) -> Self {
        let block = block.into();
        self.blocks
            .register(block.reflection().start_tag().to_owned(), block);
        self
    }

    /// Inserts a new custom tag into the parser
    pub fn tag<T: Into<Box<dyn parser::ParseTag>>>(mut self, tag: T) -> Self {
        let tag = tag.into();
        self.tags.register(tag.reflection().tag().to_owned(), tag);
        self
    }

    /// Inserts a new custom filter into the parser
    pub fn filter<F: Into<Box<dyn parser::ParseFilter>>>(mut self, filter: F) -> Self {
        let filter = filter.into();
        self.filters
            .register(filter.reflection().name().to_owned(), filter);
        self
    }

    /// Set which partial-templates will be available.
    pub fn partials<N: partials::PartialCompiler>(self, partials: N) -> ParserBuilder<N> {
        let Self {
            blocks,
            tags,
            filters,
            partials: _partials,
        } = self;
        ParserBuilder {
            blocks,
            tags,
            filters,
            partials: Some(partials),
        }
    }

    /// Create a parser
    pub fn build(self) -> Result<Parser> {
        let Self {
            blocks,
            tags,
            filters,
            partials,
        } = self;

        let mut options = parser::Language::empty();
        options.blocks = blocks;
        options.tags = tags;
        options.filters = filters;
        let options = sync::Arc::new(options);
        let partials = partials
            .map(|p| p.compile(options.clone()))
            .map_or(Ok(None), |r| r.map(Some))?
            .map(|p| p.into());
        let p = Parser { options, partials };
        Ok(p)
    }
}

impl
 Default for ParserBuilder

where
    P: partials::PartialCompiler,
{
    fn default() -> Self {
        Self {
            blocks: Default::default(),
            tags: Default::default(),
            filters: Default::default(),
            partials: Default::default(),
        }
    }
}

impl
 reflection::ParserReflection for ParserBuilder

where
    P: partials::PartialCompiler,
{
    fn blocks<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::BlockReflection> + 'r> {
        Box::new(self.blocks.plugins().map(|p| p.reflection()))
    }

    fn tags<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::TagReflection> + 'r> {
        Box::new(self.tags.plugins().map(|p| p.reflection()))
    }

    fn filters<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::FilterReflection> + 'r> {
        Box::new(self.filters.plugins().map(|p| p.reflection()))
    }

    fn partials<'r>(&'r self) -> Box<dyn Iterator<Item = &str> + 'r> {
        Box::new(
            self.partials
                .as_ref()
                .into_iter()
                .flat_map(|s| s.source().names()),
        )
    }
}

#[derive(Default, Clone)]
pub struct Parser {
    options: sync::Arc<parser::Language>,
    partials: Option<sync::Arc<dyn runtime::PartialStore + Send + Sync>>,
}

impl Parser {
    pub fn new() -> Self {
        Default::default()
    }

    /// Parses a liquid template, returning a Template object.
    /// # Examples
    ///
    /// ## Minimal Template
    ///
    /// ```
    /// let template = liquid::ParserBuilder::with_stdlib()
    ///     .build().unwrap()
    ///     .parse("Liquid!").unwrap();
    ///
    /// let globals = liquid::Object::new();
    /// let output = template.render(&globals).unwrap();
    /// assert_eq!(output, "Liquid!".to_string());
    /// ```
    ///
    pub fn parse(&self, text: &str) -> Result<Template> {
        let template = parser::parse(text, &self.options).map(runtime::Template::new)?;
        Ok(Template {
            template,
            partials: self.partials.clone(),
        })
    }

    /// Parse a liquid template from a file, returning a `Result<Template, Error>`.
    /// # Examples
    ///
    /// ## Minimal Template
    ///
    /// `template.txt`:
    ///
    /// ```text
    /// "Liquid {{data}}"
    /// ```
    ///
    /// Your rust code:
    ///
    /// ```rust,no_run
    /// let template = liquid::ParserBuilder::with_stdlib()
    ///     .build().unwrap()
    ///     .parse_file("path/to/template.txt").unwrap();
    ///
    /// let globals = liquid::object!({
    ///     "data": 4f64,
    /// });
    /// let output = template.render(&globals).unwrap();
    /// assert_eq!(output, "Liquid! 4\n".to_string());
    /// ```
    ///
    pub fn parse_file<P: AsRef<path::Path>>(&self, file: P) -> Result<Template> {
        self.parse_file_path(file.as_ref())
    }

    fn parse_file_path(&self, file: &path::Path) -> Result<Template> {
        let mut f = File::open(file)
            .replace("Cannot open file")
            .context_key("path")
            .value_with(|| file.to_string_lossy().into_owned().into())?;
        let mut buf = String::new();
        f.read_to_string(&mut buf)
            .replace("Cannot read file")
            .context_key("path")
            .value_with(|| file.to_string_lossy().into_owned().into())?;

        self.parse(&buf)
    }
}

impl reflection::ParserReflection for Parser {
    fn blocks<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::BlockReflection> + 'r> {
        Box::new(self.options.blocks.plugins().map(|p| p.reflection()))
    }

    fn tags<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::TagReflection> + 'r> {
        Box::new(self.options.tags.plugins().map(|p| p.reflection()))
    }

    fn filters<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::FilterReflection> + 'r> {
        Box::new(self.options.filters.plugins().map(|p| p.reflection()))
    }

    fn partials<'r>(&'r self) -> Box<dyn Iterator<Item = &str> + 'r> {
        Box::new(self.partials.as_ref().into_iter().flat_map(|s| s.names()))
    }
}
<commit_msg>feat(liquid): Add where to std filters<commit_after>use std::fs::File;
use std::io::prelude::Read;
use std::path;
use std::sync;

use liquid_core::error::{Result, ResultLiquidExt, ResultLiquidReplaceExt};
use liquid_core::parser;
use liquid_core::runtime;

use super::Template;
use crate::reflection;
use liquid_core::partials;
#[cfg(feature = "stdlib")]
use liquid_lib::stdlib;

type Partials = partials::EagerCompiler<partials::InMemorySource>;

pub struct ParserBuilder

where
    P: partials::PartialCompiler,
{
    blocks: parser::PluginRegistry<Box<dyn parser::ParseBlock>>,
    tags: parser::PluginRegistry<Box<dyn parser::ParseTag>>,
    filters: parser::PluginRegistry<Box<dyn parser::ParseFilter>>,
    partials: Option
,
}

impl ParserBuilder<Partials> {
    /// Create an empty Liquid parser
    pub fn new() -> Self {
        Self::default()
    }

    #[cfg(feature = "stdlib")]
    pub fn with_stdlib() -> Self {
        Self::new().stdlib()
    }
}

impl
 ParserBuilder

where
    P: partials::PartialCompiler,
{
    #[cfg(feature = "stdlib")]
    /// Create a Liquid parser with built-in Liquid features
    pub fn stdlib(self) -> Self {
        self.tag(stdlib::AssignTag)
            .tag(stdlib::BreakTag)
            .tag(stdlib::ContinueTag)
            .tag(stdlib::CycleTag)
            .tag(stdlib::IncludeTag)
            .tag(stdlib::IncrementTag)
            .tag(stdlib::DecrementTag)
            .block(stdlib::RawBlock)
            .block(stdlib::IfBlock)
            .block(stdlib::UnlessBlock)
            .block(stdlib::IfChangedBlock)
            .block(stdlib::ForBlock)
            .block(stdlib::TableRowBlock)
            .block(stdlib::CommentBlock)
            .block(stdlib::CaptureBlock)
            .block(stdlib::CaseBlock)
            .filter(stdlib::Abs)
            .filter(stdlib::Append)
            .filter(stdlib::AtLeast)
            .filter(stdlib::AtMost)
            .filter(stdlib::Capitalize)
            .filter(stdlib::Ceil)
            .filter(stdlib::Compact)
            .filter(stdlib::Concat)
            .filter(stdlib::Date)
            .filter(stdlib::Default)
            .filter(stdlib::DividedBy)
            .filter(stdlib::Downcase)
            .filter(stdlib::Escape)
            .filter(stdlib::EscapeOnce)
            .filter(stdlib::First)
            .filter(stdlib::Floor)
            .filter(stdlib::Join)
            .filter(stdlib::Last)
            .filter(stdlib::Lstrip)
            .filter(stdlib::Map)
            .filter(stdlib::Minus)
            .filter(stdlib::Modulo)
            .filter(stdlib::NewlineToBr)
            .filter(stdlib::Plus)
            .filter(stdlib::Prepend)
            .filter(stdlib::Remove)
            .filter(stdlib::RemoveFirst)
            .filter(stdlib::Replace)
            .filter(stdlib::ReplaceFirst)
            .filter(stdlib::Reverse)
            .filter(stdlib::Round)
            .filter(stdlib::Rstrip)
            .filter(stdlib::Size)
            .filter(stdlib::Slice)
            .filter(stdlib::Sort)
            .filter(stdlib::SortNatural)
            .filter(stdlib::Split)
            .filter(stdlib::Strip)
            .filter(stdlib::StripHtml)
            .filter(stdlib::StripNewlines)
            .filter(stdlib::Times)
            .filter(stdlib::Truncate)
            .filter(stdlib::TruncateWords)
            .filter(stdlib::Uniq)
            .filter(stdlib::Upcase)
            .filter(stdlib::UrlDecode)
            .filter(stdlib::UrlEncode)
            .filter(stdlib::Where)
    }

    /// Inserts a new custom block into the parser
    pub fn block<B: Into<Box<dyn parser::ParseBlock>>>(mut self, block: B) -> Self {
        let block = block.into();
        self.blocks
            .register(block.reflection().start_tag().to_owned(), block);
        self
    }

    /// Inserts a new custom tag into the parser
    pub fn tag<T: Into<Box<dyn parser::ParseTag>>>(mut self, tag: T) -> Self {
        let tag = tag.into();
        self.tags.register(tag.reflection().tag().to_owned(), tag);
        self
    }

    /// Inserts a new custom filter into the parser
    pub fn filter<F: Into<Box<dyn parser::ParseFilter>>>(mut self, filter: F) -> Self {
        let filter = filter.into();
        self.filters
            .register(filter.reflection().name().to_owned(), filter);
        self
    }

    /// Set which partial-templates will be available.
    pub fn partials<N: partials::PartialCompiler>(self, partials: N) -> ParserBuilder<N> {
        let Self {
            blocks,
            tags,
            filters,
            partials: _partials,
        } = self;
        ParserBuilder {
            blocks,
            tags,
            filters,
            partials: Some(partials),
        }
    }

    /// Create a parser
    pub fn build(self) -> Result<Parser> {
        let Self {
            blocks,
            tags,
            filters,
            partials,
        } = self;

        let mut options = parser::Language::empty();
        options.blocks = blocks;
        options.tags = tags;
        options.filters = filters;
        let options = sync::Arc::new(options);
        let partials = partials
            .map(|p| p.compile(options.clone()))
            .map_or(Ok(None), |r| r.map(Some))?
            .map(|p| p.into());
        let p = Parser { options, partials };
        Ok(p)
    }
}

impl
 Default for ParserBuilder

where
    P: partials::PartialCompiler,
{
    fn default() -> Self {
        Self {
            blocks: Default::default(),
            tags: Default::default(),
            filters: Default::default(),
            partials: Default::default(),
        }
    }
}

impl
 reflection::ParserReflection for ParserBuilder

where
    P: partials::PartialCompiler,
{
    fn blocks<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::BlockReflection> + 'r> {
        Box::new(self.blocks.plugins().map(|p| p.reflection()))
    }

    fn tags<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::TagReflection> + 'r> {
        Box::new(self.tags.plugins().map(|p| p.reflection()))
    }

    fn filters<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::FilterReflection> + 'r> {
        Box::new(self.filters.plugins().map(|p| p.reflection()))
    }

    fn partials<'r>(&'r self) -> Box<dyn Iterator<Item = &str> + 'r> {
        Box::new(
            self.partials
                .as_ref()
                .into_iter()
                .flat_map(|s| s.source().names()),
        )
    }
}

#[derive(Default, Clone)]
pub struct Parser {
    options: sync::Arc<parser::Language>,
    partials: Option<sync::Arc<dyn runtime::PartialStore + Send + Sync>>,
}

impl Parser {
    pub fn new() -> Self {
        Default::default()
    }

    /// Parses a liquid template, returning a Template object.
    /// # Examples
    ///
    /// ## Minimal Template
    ///
    /// ```
    /// let template = liquid::ParserBuilder::with_stdlib()
    ///     .build().unwrap()
    ///     .parse("Liquid!").unwrap();
    ///
    /// let globals = liquid::Object::new();
    /// let output = template.render(&globals).unwrap();
    /// assert_eq!(output, "Liquid!".to_string());
    /// ```
    ///
    pub fn parse(&self, text: &str) -> Result<Template> {
        let template = parser::parse(text, &self.options).map(runtime::Template::new)?;
        Ok(Template {
            template,
            partials: self.partials.clone(),
        })
    }

    /// Parse a liquid template from a file, returning a `Result<Template, Error>`.
    /// # Examples
    ///
    /// ## Minimal Template
    ///
    /// `template.txt`:
    ///
    /// ```text
    /// "Liquid {{data}}"
    /// ```
    ///
    /// Your rust code:
    ///
    /// ```rust,no_run
    /// let template = liquid::ParserBuilder::with_stdlib()
    ///     .build().unwrap()
    ///     .parse_file("path/to/template.txt").unwrap();
    ///
    /// let globals = liquid::object!({
    ///     "data": 4f64,
    /// });
    /// let output = template.render(&globals).unwrap();
    /// assert_eq!(output, "Liquid! 4\n".to_string());
    /// ```
    ///
    pub fn parse_file<P: AsRef<path::Path>>(&self, file: P) -> Result<Template> {
        self.parse_file_path(file.as_ref())
    }

    fn parse_file_path(&self, file: &path::Path) -> Result<Template> {
        let mut f = File::open(file)
            .replace("Cannot open file")
            .context_key("path")
            .value_with(|| file.to_string_lossy().into_owned().into())?;
        let mut buf = String::new();
        f.read_to_string(&mut buf)
            .replace("Cannot read file")
            .context_key("path")
            .value_with(|| file.to_string_lossy().into_owned().into())?;

        self.parse(&buf)
    }
}

impl reflection::ParserReflection for Parser {
    fn blocks<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::BlockReflection> + 'r> {
        Box::new(self.options.blocks.plugins().map(|p| p.reflection()))
    }

    fn tags<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::TagReflection> + 'r> {
        Box::new(self.options.tags.plugins().map(|p| p.reflection()))
    }

    fn filters<'r>(&'r self) -> Box<dyn Iterator<Item = &dyn parser::FilterReflection> + 'r> {
        Box::new(self.options.filters.plugins().map(|p| p.reflection()))
    }

    fn partials<'r>(&'r self) -> Box<dyn Iterator<Item = &str> + 'r> {
        Box::new(self.partials.as_ref().into_iter().flat_map(|s| s.names()))
    }
}
<|endoftext|>


<commit_before>extern crate nom;
use nom::*;
use std::str;
use std::collections::HashMap;
use std::hash::Hash;

#[derive(PartialEq,Debug)]
pub enum JsonValue<'a> {
    Float(f64),
    Int(i64),
    Str(&'a str),
    Bool(bool),
    Object(HashMap<&'a str, JsonValue<'a>>),
    Vec(Vec<JsonValue<'a>>),
    Null,
}

named!(quoted_str<&str>,
       map_res!(delimited!(char!('"'),
                           escaped!(call!(alpha), '\\',
                                    is_a_bytes!(&b"\"n\\"[..])),
                           char!('"')),
                str::from_utf8));

named!(quoted_str_val<JsonValue>,
       map!(quoted_str,
            |x: &'a str| { JsonValue::Str(x) }));

named!(json_int<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   digit,
                   str::from_utf8
               ),
               |x: &str| { x.parse::<i64>() }
           ),
           |x: i64| { JsonValue::Int(x) }
       ));

named!(json_float<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   recognize!(separated_pair!(digit, char!('.'), digit)),
                   str::from_utf8
               ),
               |x: &str| { x.parse::<f64>() }
           ),
           |x: f64| { JsonValue::Float(x) }
       ));

named!(json_boolean<JsonValue>,
       alt!(value!(JsonValue::Bool(true), tag!("true")) |
            value!(JsonValue::Bool(false), tag!("false"))));

named!(json_null<JsonValue>,
       value!(JsonValue::Null, tag!("null")));

macro_rules! multispaced (
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        delimited!($i, opt!(multispace), $submac!($($args)*), opt!(multispace));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

macro_rules! multispace_delimited (
    (
        $i:expr,
        $submac1:ident!( $($args1:tt)* ),
        $submac2:ident!( $($args2:tt)* ),
        $submac3:ident!( $($args3:tt)* )
    ) => (
        delimited!($i,
                   multispaced!($submac1!($($args1)*)),
                   $submac2!($($args2)*),
                   multispaced!($submac3!($($args3)*)));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

named!(keypair<(&str,JsonValue)>,
       separated_pair!(quoted_str,
                       multispaced!(alt!(char!(':') | char!('='))),
                       json_value));

fn pairs_to_map<K: Hash + Eq, V>(v: Vec<(K, V)>) -> HashMap<K, V> {
    v.into_iter().fold(HashMap::new(), |mut m, (k, v)| {
        m.insert(k, v);
        m
    })
}

named!(object_map<JsonValue>,
       multispace_delimited!(
           char!('{'),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       keypair
                   ),
                   opt!(multispaced!(char!(',')))),
               |x| { JsonValue::Object(pairs_to_map(x)) }),
           char!('}')));

named!(json_array<JsonValue>,
       multispace_delimited!(
           char!('['),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       json_value
                   ),
                   opt!(multispaced!(char!(',')))),
               JsonValue::Vec),
           char!(']')));

named!(json_value<JsonValue>,
       alt!(json_null |
            json_float |
            json_int |
            quoted_str_val |
            json_boolean |
            object_map |
            json_array));

#[test]
fn check_quoted_str() {
    let empty = &b""[..];

    assert_eq!(quoted_str(&b"\"foo\""[..]),
               IResult::Done(empty,"foo"));

    assert_eq!(quoted_str_val(&b"\"foo\""[..]),
               IResult::Done(empty,JsonValue::Str("foo")));

    assert_eq!(quoted_str(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,"f\\\"o\\\"o"));

    assert_eq!(quoted_str_val(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,JsonValue::Str("f\\\"o\\\"o")));
}

#[test]
fn check_int() {
    assert_eq!(json_int(&b"123"[..]),
               IResult::Done(&b""[..],JsonValue::Int(123)));
}

#[test]
fn check_float() {
    assert_eq!(json_float(&b"123.123"[..]),
               IResult::Done(&b""[..],JsonValue::Float(123.123)));
}

#[test]
fn check_pairs_to_map() {
    let mut res = HashMap::with_capacity(3);
    res.insert("a", JsonValue::Int(1));
    res.insert("b", JsonValue::Int(2));
    res.insert("c", JsonValue::Int(3));
    assert_eq!(res,
               pairs_to_map(vec![("a",JsonValue::Int(1)),
                                 ("b",JsonValue::Int(2)),
                                 ("c",JsonValue::Int(3))]));
}

#[test]
fn check_object_map() {
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));
    assert_eq!(object_map(&b"{\"foo\" : true,\"bar\"=\n\"with\\\"quotes\",\"baz\":123,\n\"withafloat\":123.123}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res = HashMap::new();
    let mut nested_res = HashMap::new();

    nested_res.insert("nestedfoo", JsonValue::Bool(true));
    nested_res.insert("bar", JsonValue::Str("with\\\"quotes"));
    nested_res.insert("baz", JsonValue::Int(123));
    nested_res.insert("withafloat", JsonValue::Float(123.123));
    res.insert("foo", JsonValue::Object(nested_res));
    assert_eq!(object_map(&b"{\"foo\":{\"nestedfoo\":true,\"bar\":\"with\\\"quotes\",\"baz\":123,\"withafloat\":123.123}}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res2 = HashMap::new();
    res2.insert("foo", JsonValue::Bool(true));
    assert_eq!(object_map(&b"{ \"foo\" : true }"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res2)));
}

#[test]
fn check_array() {

    assert_eq!(json_value(&b"[1,2,null,4,]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Int(1),
                                                 JsonValue::Int(2),
                                                 JsonValue::Null,
                                                 JsonValue::Int(4)])));
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));

    assert_eq!(json_value(&b"[{\"foo\":true,\n\"bar\":\"with\\\"quotes\"  ,\"baz\":123,\"withafloat\":123.123}]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Object(res)])));

    assert_eq!(json_value(&b"[ \"foo\" ]"[..]),
           IResult::Done(&b""[..], JsonValue::Vec(vec![JsonValue::Str("foo")])));
}
<commit_msg>(maint) Add ability to optionally specify object separator<commit_after>extern crate nom;
use nom::*;
use std::str;
use std::collections::HashMap;
use std::hash::Hash;

#[derive(PartialEq,Debug)]
pub enum JsonValue<'a> {
    Float(f64),
    Int(i64),
    Str(&'a str),
    Bool(bool),
    Object(HashMap<&'a str, JsonValue<'a>>),
    Vec(Vec<JsonValue<'a>>),
    Null,
}

named!(quoted_str<&str>,
       map_res!(delimited!(char!('"'),
                           escaped!(call!(alpha), '\\',
                                    is_a_bytes!(&b"\"n\\"[..])),
                           char!('"')),
                str::from_utf8));

named!(quoted_str_val<JsonValue>,
       map!(quoted_str,
            |x: &'a str| { JsonValue::Str(x) }));

named!(json_int<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   digit,
                   str::from_utf8
               ),
               |x: &str| { x.parse::<i64>() }
           ),
           |x: i64| { JsonValue::Int(x) }
       ));

named!(json_float<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   recognize!(separated_pair!(digit, char!('.'), digit)),
                   str::from_utf8
               ),
               |x: &str| { x.parse::<f64>() }
           ),
           |x: f64| { JsonValue::Float(x) }
       ));

named!(json_boolean<JsonValue>,
       alt!(value!(JsonValue::Bool(true), tag!("true")) |
            value!(JsonValue::Bool(false), tag!("false"))));

named!(json_null<JsonValue>,
       value!(JsonValue::Null, tag!("null")));

macro_rules! multispaced (
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        delimited!($i, opt!(multispace), $submac!($($args)*), opt!(multispace));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

macro_rules! multispace_delimited (
    (
        $i:expr,
        $submac1:ident!( $($args1:tt)* ),
        $submac2:ident!( $($args2:tt)* ),
        $submac3:ident!( $($args3:tt)* )
    ) => (
        delimited!($i,
                   multispaced!($submac1!($($args1)*)),
                   $submac2!($($args2)*),
                   multispaced!($submac3!($($args3)*)));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

named!(keypair<(&str,JsonValue)>,
       alt!(
           separated_pair!(quoted_str,
                           opt!(multispaced!(alt!(char!(':') | char!('=')))),
                           object_map) |
           separated_pair!(quoted_str,
                           multispaced!(alt!(char!(':') | char!('='))),
                           json_value)
       )

);

fn pairs_to_map<K: Hash + Eq, V>(v: Vec<(K, V)>) -> HashMap<K, V> {
    v.into_iter().fold(HashMap::new(), |mut m, (k, v)| {
        m.insert(k, v);
        m
    })
}

named!(object_map<JsonValue>,
       multispace_delimited!(
           char!('{'),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       keypair
                   ),
                   opt!(multispaced!(char!(',')))),
               |x| { JsonValue::Object(pairs_to_map(x)) }),
           char!('}')));

named!(json_array<JsonValue>,
       multispace_delimited!(
           char!('['),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       json_value
                   ),
                   opt!(multispaced!(char!(',')))),
               JsonValue::Vec),
           char!(']')));

named!(json_value<JsonValue>,
       alt!(json_null |
            json_float |
            json_int |
            quoted_str_val |
            json_boolean |
            object_map |
            json_array));

#[test]
fn check_quoted_str() {
    let empty = &b""[..];

    assert_eq!(quoted_str(&b"\"foo\""[..]),
               IResult::Done(empty,"foo"));

    assert_eq!(quoted_str_val(&b"\"foo\""[..]),
               IResult::Done(empty,JsonValue::Str("foo")));

    assert_eq!(quoted_str(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,"f\\\"o\\\"o"));

    assert_eq!(quoted_str_val(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,JsonValue::Str("f\\\"o\\\"o")));
}

#[test]
fn check_int() {
    assert_eq!(json_int(&b"123"[..]),
               IResult::Done(&b""[..],JsonValue::Int(123)));
}

#[test]
fn check_float() {
    assert_eq!(json_float(&b"123.123"[..]),
               IResult::Done(&b""[..],JsonValue::Float(123.123)));
}

#[test]
fn check_pairs_to_map() {
    let mut res = HashMap::with_capacity(3);
    res.insert("a", JsonValue::Int(1));
    res.insert("b", JsonValue::Int(2));
    res.insert("c", JsonValue::Int(3));
    assert_eq!(res,
               pairs_to_map(vec![("a",JsonValue::Int(1)),
                                 ("b",JsonValue::Int(2)),
                                 ("c",JsonValue::Int(3))]));
}

#[test]
fn check_object_map() {
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));
    assert_eq!(object_map(&b"{\"foo\" : true,\"bar\"=\n\"with\\\"quotes\",\"baz\":123,\n\"withafloat\":123.123}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res = HashMap::new();
    let mut nested_res = HashMap::new();

    nested_res.insert("nestedfoo", JsonValue::Bool(true));
    nested_res.insert("bar", JsonValue::Str("with\\\"quotes"));
    nested_res.insert("baz", JsonValue::Int(123));
    nested_res.insert("withafloat", JsonValue::Float(123.123));
    res.insert("foo", JsonValue::Object(nested_res));
    assert_eq!(object_map(&b"{\"foo\" {\"nestedfoo\":true,\"bar\":\"with\\\"quotes\",\"baz\":123,\"withafloat\":123.123}}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res2 = HashMap::new();
    res2.insert("foo", JsonValue::Bool(true));
    assert_eq!(object_map(&b"{ \"foo\" : true }"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res2)));
}

#[test]
fn check_array() {

    assert_eq!(json_value(&b"[1,2,null,4,]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Int(1),
                                                 JsonValue::Int(2),
                                                 JsonValue::Null,
                                                 JsonValue::Int(4)])));
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));

    assert_eq!(json_value(&b"[{\"foo\":true,\n\"bar\":\"with\\\"quotes\"  ,\"baz\":123,\"withafloat\":123.123}]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Object(res)])));

    assert_eq!(json_value(&b"[ \"foo\" ]"[..]),
           IResult::Done(&b""[..], JsonValue::Vec(vec![JsonValue::Str("foo")])));
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::{FieldCoord, Model, Turn};

pub fn update(model: &mut Model, click: Option<FieldCoord>) {
    match click {
        Some(click) => if (model.turn == Turn::White && click.is_white()) ||
            (model.turn == Turn::Black && click.is_black())
        {
            if model.board.is_piece_on_field(&click) {
                let available_moves = model
                    .board
                    .get_field_vertex_neighbors(&click)
                    .into_iter()
                    .filter(|c| !model.board.is_piece_on_field(c))
                    .collect();

                model.available_moves = Some(available_moves);
                model.selected_piece = Some(click);
            } else if let Some(selected) = model.selected_piece.take() {
                if model.board.can_move_piece(&selected, &click) {
                    model.board.move_piece(&selected, &click);
                    model.last_move = Some((Some(selected), click));
                    model.turn.switch_turns();
                }
                clear_selection(model);
            }
        } else {
            clear_selection(model);
        },
        None => clear_selection(model),
    }
}

fn clear_selection(model: &mut Model) {
    model.selected_piece = None;
    model.available_moves = None;
}
<commit_msg>Deselect when clicking on selected piece again<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::{FieldCoord, Model, Turn};

pub fn update(model: &mut Model, click: Option<FieldCoord>) {
    match click {
        Some(click) => if (model.turn == Turn::White && click.is_white()) ||
            (model.turn == Turn::Black && click.is_black())
        {
            if model.board.is_piece_on_field(&click) {
                if model.selected_piece.as_ref() == Some(&click) {
                    clear_selection(model);
                } else {
                    let available_moves = model
                        .board
                        .get_field_vertex_neighbors(&click)
                        .into_iter()
                        .filter(|c| !model.board.is_piece_on_field(c))
                        .collect();

                    model.available_moves = Some(available_moves);
                    model.selected_piece = Some(click);
                }
            } else if let Some(selected) = model.selected_piece.take() {
                if model.board.can_move_piece(&selected, &click) {
                    model.board.move_piece(&selected, &click);
                    model.last_move = Some((Some(selected), click));
                    model.turn.switch_turns();
                }
                clear_selection(model);
            }
        } else {
            clear_selection(model);
        },
        None => clear_selection(model),
    }
}

fn clear_selection(model: &mut Model) {
    model.selected_piece = None;
    model.available_moves = None;
}
<|endoftext|>


<commit_before>use std::vec::Vec;

pub mod errors;
pub use self::errors::{Result, Error};

use proto::metapb;

// Client to communicate with placement driver (pd).
pub trait Client {
    // Bootstrap the cluster with cluster ID, if the cluster is
    // already bootstrapped, return ClusterBootstrapped error.
    // We must first bootstrap the cluster and then use it.
    // When a node starts, if it finds nothing in the node and
    // cluster is not bootstrapped, it begin to bootstrap
    // itself with node 1, store 1 and peer 1 for region 1,
    // and then call bootstrap_cluster to let pd know it.
    // It may happen that multi nodes start at same time to try to
    // bootstrap, and only one can success, others will fail
    // and must remove their created local data themselves.
    fn bootstrap_cluster(&mut self, cluster_id: u64) -> Result<()>;

    // Return whether the cluster is bootstrapped or not.
    // We must use the cluster after bootstrapped, so when the
    // node starts, it must check it with is_cluster_bootstrapped,
    // and panic if not bootstrapped.
    fn is_cluster_bootstrapped(&self, cluster_id: u64) -> Result<bool>;

    // Allocate a node id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use node 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_node_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a store id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use store 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_store_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a peer id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use peer 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_peer_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a region id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use region 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_region_id(&mut self, cluster_id: u64) -> Result<u64>;

    // When the node starts, or some node information changed, it
    // uses put_node to tell pd know it.
    fn put_node(&mut self, cluster_id: u64, node: metapb::Node) -> Result<()>;

    // When the store starts, or some store information changed, it
    // uses put_store to tell pd know it.
    fn put_store(&mut self, cluster_id: u64, store: metapb::Store) -> Result<()>;

    // Delete the node from cluster, it is a very dangerous operation
    // and can not be recoverable, all the data belongs to this node
    // will be removed and we can't re-add this node again.
    // Sometimes, the node may crash and restart again, if the node is
    // off-line for a long time, pd will try to do auto-balance and then
    // delete the node.
    fn delete_node(&mut self, cluster_id: u64, node_id: u64) -> Result<()>;

    // Delete the store from cluster, it is a very dangerous operation
    // and can not be recoverable, all the data belongs to this store
    // will be removed and we can't re-add this store again.
    // If the store is off-line for a long time, pd will try to do
    // auto-balance and then delete the store.
    fn delete_store(&mut self, cluster_id: u64, store_id: u64) -> Result<()>;

    // Discuss (@ngaut): We don't need to support region and peer put/delete,
    // because pd knows all region and peers itself.
    // When bootstrapping, pd knows region 1 and peer 1 exists in node 1, store 1.
    // When changing peer, pd determines where to add a new peer in some store
    // for this region.
    // When region splitting, pd determines the new region id and peer id for the
    // split region.
    // When region merging, pd knows which two regions will be merged and which region
    // and peers will be removed.
    // When doing auto-balance, pd determines how to move the region from one store to another.

    // Get node address so that we can send message to it.
    fn get_node_addr(&self, cluster_id: u64, node_id: u64) -> Result<String>;

    // For route.
    // Get the region which the key belongs to.
    fn get_region_with_key(&self, cluster_id: u64, key: &[u8]) -> Result<metapb::Region>;

    // For route.
    // Get all regions which the keys belong to.
    fn get_regions_with_keys(&self,
                             cluster_id: u64,
                             keys: Vec<Vec<u8>>)
                             -> Result<Vec<metapb::Region>>;

    // For route.
    // Scan all regions which the region key range is in [start_key, end_key].
    fn scan_regions(&self,
                    cluster_id: u64,
                    start_key: &[u8],
                    end_key: &[u8],
                    limit: usize)
                    -> Result<Vec<metapb::Region>>;
}
<commit_msg>pd: support get node and store.<commit_after>use std::vec::Vec;

pub mod errors;
pub use self::errors::{Result, Error};

use proto::metapb;

// Client to communicate with placement driver (pd).
pub trait Client {
    // Bootstrap the cluster with cluster ID, if the cluster is
    // already bootstrapped, return ClusterBootstrapped error.
    // We must first bootstrap the cluster and then use it.
    // When a node starts, if it finds nothing in the node and
    // cluster is not bootstrapped, it begin to bootstrap
    // itself with node 1, store 1 and peer 1 for region 1,
    // and then call bootstrap_cluster to let pd know it.
    // It may happen that multi nodes start at same time to try to
    // bootstrap, and only one can success, others will fail
    // and must remove their created local data themselves.
    fn bootstrap_cluster(&mut self, cluster_id: u64) -> Result<()>;

    // Return whether the cluster is bootstrapped or not.
    // We must use the cluster after bootstrapped, so when the
    // node starts, it must check it with is_cluster_bootstrapped,
    // and panic if not bootstrapped.
    fn is_cluster_bootstrapped(&self, cluster_id: u64) -> Result<bool>;

    // Allocate a node id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use node 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_node_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a store id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use store 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_store_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a peer id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use peer 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_peer_id(&mut self, cluster_id: u64) -> Result<u64>;

    // Allocate a region id for this cluster. Pd must guarantee that
    // the id is increased monotonically, because we use region 1
    // for bootstrap already, so this function returns 2 at first.
    fn alloc_region_id(&mut self, cluster_id: u64) -> Result<u64>;

    // When the node starts, or some node information changed, it
    // uses put_node to tell pd know it.
    fn put_node(&mut self, cluster_id: u64, node: metapb::Node) -> Result<()>;

    // When the store starts, or some store information changed, it
    // uses put_store to tell pd know it.
    fn put_store(&mut self, cluster_id: u64, store: metapb::Store) -> Result<()>;

    // Delete the node from cluster, it is a very dangerous operation
    // and can not be recoverable, all the data belongs to this node
    // will be removed and we can't re-add this node again.
    // Sometimes, the node may crash and restart again, if the node is
    // off-line for a long time, pd will try to do auto-balance and then
    // delete the node.
    fn delete_node(&mut self, cluster_id: u64, node_id: u64) -> Result<()>;

    // Delete the store from cluster, it is a very dangerous operation
    // and can not be recoverable, all the data belongs to this store
    // will be removed and we can't re-add this store again.
    // If the store is off-line for a long time, pd will try to do
    // auto-balance and then delete the store.
    fn delete_store(&mut self, cluster_id: u64, store_id: u64) -> Result<()>;

    // Discuss (@ngaut): We don't need to support region and peer put/delete,
    // because pd knows all region and peers itself.
    // When bootstrapping, pd knows region 1 and peer 1 exists in node 1, store 1.
    // When changing peer, pd determines where to add a new peer in some store
    // for this region.
    // When region splitting, pd determines the new region id and peer id for the
    // split region.
    // When region merging, pd knows which two regions will be merged and which region
    // and peers will be removed.
    // When doing auto-balance, pd determines how to move the region from one store to another.

    // Get node information.
    fn get_node(&self, cluster_id: u64, node_id: u64) -> Result<metapb::Node>;

    // Get store information.
    fn get_store(&self, cluster_id: u64, store_id: u64) -> Result<metapb::Store>;

    // For route.
    // Get the region which the key belongs to.
    fn get_region_with_key(&self, cluster_id: u64, key: &[u8]) -> Result<metapb::Region>;

    // For route.
    // Get all regions which the keys belong to.
    fn get_regions_with_keys(&self,
                             cluster_id: u64,
                             keys: Vec<Vec<u8>>)
                             -> Result<Vec<metapb::Region>>;

    // For route.
    // Scan all regions which the region key range is in [start_key, end_key].
    fn scan_regions(&self,
                    cluster_id: u64,
                    start_key: &[u8],
                    end_key: &[u8],
                    limit: usize)
                    -> Result<Vec<metapb::Region>>;
}
<|endoftext|>


<commit_before>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<String>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  Object(BTreeMap<String, Value>),
  Array(Vec<Value>),
  Number(f64),
  String(String),
  Boolean(bool),
  Null
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Box<Filter>>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Box<Filter>>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}
<commit_msg>Add reference value type<commit_after>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;
use structure::Collection;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<String>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  /// The abscense of any value.
  Null,
  /// True or false.
  Boolean(bool),
  /// A numeric value, float, integer, whatever.
  Number(f64),
  /// A list of characters.
  String(String),
  /// A list of values.
  Array(Vec<Value>),
  /// A map of key/value pairs.
  Object(BTreeMap<String, Value>),
  /// A reference to a document in another collection.
  Reference(Collection, Box<Value>)
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Box<Filter>>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Box<Filter>>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}
<|endoftext|>