<commit_before>use std::cmp::Ordering;
use std::sync::Arc;

use kailua_env::{Pos, Span};
use kailua_syntax::{Tok, Punct, Keyword, NestedToken, NestingCategory, Chunk};
use kailua_check::{Output, Key, Tables};
use kailua_check::flags::T_STRING;

use protocol::*;

#[derive(Clone, Debug)]
pub enum CompletionClass {
    // complete a top-level name or keyword.
    // the caret is located inside tokens[i] or on the intersection of tokens[i-1] and tokens[i].
    // in the former case tokens[i] should be a name; in the latter case tokens[i-1] may not exist.
    Name(usize, NestingCategory),

    // complete a field or method after `.` or `:`.
    // the caret is located after tokens[i] (which should be `.` or `:`).
    Field(usize),
}

fn index_and_neighbor<T, F>(tokens: &[T], pos: Pos, as_span: F) -> (usize, bool, bool)
    where F: Fn(&T) -> Span
{
    //        pos
    // ________v________   idx  end   after
    //     BOF^  0000000   0    false false
    //     BOF^  $EOF      0    false false
    //     BOF^$EOF        0    false false
    // iiiiiiii  jjjjjjj   i    true  true
    // iiiiiiiijjjjjjjjj   i    true  true
    // iiiiii    jjjjjjj   i    false false
    // iiiiii  jjjjjjjjj   j    false false
    // iiii   jjjjjj kkk   j    false true
    // iii jjjjj   kkkkk   j    false true
    // ii jjjjj   kkkkkk   j    true  true
    // zzzzzzzz   $EOF     z    true  true
    // zzzz    $EOF        z    false false
    match tokens.binary_search_by(|tok| as_span(tok).begin().cmp(&pos)) {
        Ok(i) => { // tokens[i].begin == pos
            if i > 0 && as_span(&tokens[i-1]).end() == pos {
                (i-1, true, true)
            } else {
                (i, false, false)
            }
        },
        Err(0) => { // pos < tokens[0].begin or inf
            (0, false, false)
        },
        Err(i) => { // tokens[i-1].begin < pos < tokens[i].begin or inf
            match pos.cmp(&as_span(&tokens[i-1]).end()) {
                Ordering::Less => (i-1, false, true),
                Ordering::Equal => (i-1, true, true),
                Ordering::Greater => (i-1, false, false),
            }
        },
    }
}

#[test]
fn test_index_and_neighbor() {
    use kailua_env::{Source, SourceFile};

    // we need a sizable span to construct a dummy list of "tokens" (solely represented by spans)
    let mut source = Source::new();
    let span = source.add(SourceFile::from_u8("dummy".to_string(), b"0123456789"[..].to_owned()));
    let pos = |i| span.clone().nth(i).unwrap();

    let tokens = [Span::new(pos(1), pos(2)), Span::new(pos(2), pos(4)),
                  Span::new(pos(5), pos(7)), Span::new(pos(8), pos(8))];
    assert_eq!(index_and_neighbor(&tokens, pos(0), |&sp| sp), (0, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(1), |&sp| sp), (0, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(2), |&sp| sp), (0, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(3), |&sp| sp), (1, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(4), |&sp| sp), (1, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(5), |&sp| sp), (2, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(6), |&sp| sp), (2, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(7), |&sp| sp), (2, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(8), |&sp| sp), (3, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(9), |&sp| sp), (3, false, false));

    let tokens = [Span::new(pos(1), pos(2)), Span::new(pos(2), pos(4)),
                  Span::new(pos(5), pos(8)), Span::new(pos(8), pos(8))];
    assert_eq!(index_and_neighbor(&tokens, pos(7), |&sp| sp), (2, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(8), |&sp| sp), (2, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(9), |&sp| sp), (3, false, false));
}

pub fn classify(tokens: &[NestedToken], pos: Pos) -> Option<CompletionClass> {
    let (idx, end, after) = index_and_neighbor(tokens, pos, |tok| tok.tok.span);

    let ptok = if idx > 0 { tokens.get(idx-1) } else { None };
    let tok = tokens.get(idx);
    match (end, after, ptok.map(|tok| &tok.tok.base), tok.map(|tok| &tok.tok.base)) {
        // ... `.` | ...
        // ... `:` | ...
        (true, true, _, Some(&Tok::Punct(Punct::Dot))) |
        (true, true, _, Some(&Tok::Punct(Punct::Colon))) => {
            Some(CompletionClass::Field(idx))
        },

        // ... `.` NAM|E ...
        // ... `:` NAM|E ...
        // ... `.` NAME | ... (with no space between NAME and the caret)
        // ... `:` NAME | ...
        (_, true, Some(&Tok::Punct(Punct::Dot)), Some(&Tok::Name(_))) |
        (_, true, Some(&Tok::Punct(Punct::Dot)), Some(&Tok::Keyword(_))) |
        (_, true, Some(&Tok::Punct(Punct::Colon)), Some(&Tok::Name(_))) |
        (_, true, Some(&Tok::Punct(Punct::Colon)), Some(&Tok::Keyword(_))) => {
            Some(CompletionClass::Field(idx))
        },

        // ... NAME | ... (ditto)
        (_, true, _, Some(&Tok::Name(_))) |
        (_, true, _, Some(&Tok::Keyword(_))) => {
            Some(CompletionClass::Name(idx, tok.unwrap().category))
        },

        _ => None,
    }
}

fn make_item(label: String, kind: CompletionItemKind, detail: Option<String>) -> CompletionItem {
    CompletionItem {
        label: label,
        kind: Some(kind),
        detail: detail,
        documentation: None,
        sortText: None,
        filterText: None,
        insertText: None,
        textEdit: None,
        additionalTextEdits: Vec::new(),
        command: None,
        data: None,
    }
}

// check if the caret is located in regions where the autocompletion should be disabled:
//
// 1. `local NAME ... | ... [= ...]`
// 2. `for NAME ... | ... = ... do ... end`
// 3. `function NAME ... | ( ... )`
// 4. `function [NAME ...] ( ... | ... )`
//
// the check for 1 and 2 is handled by looking backward for the first token
// that is not a comment, a name or a comma and is in the same nesting as the caret.
// if the token exists and it's `local` or `for`, autocompletion is disabled.
//
// the check for 3 is handled by looking backward for the first token
// that is not a comment, a name, a dot or a colon and is in the same nesting as the caret.
// if the token exists and it's `function`, autocompletion is disabled.
//
// the check for 4 is handled similarly to the check for 1 and 2,
// but once seen a `(` token, it will switch to the check for 3 at the parent nesting.
//
// for the interactivity, the lookbehind is limited to a reasonable number.
fn is_name_completion_disabled(tokens: &[NestedToken], name_idx: usize) -> bool {
    const LOOKBEHIND_LIMIT: usize = 4096;

    let name_tok = &tokens[name_idx];
    let mut init_depth = name_tok.depth;
    let init_serial = name_tok.serial;

    let mut name_decl_possible = true; // case 1, 2 and 4a
    let mut func_sig_possible = true; // case 3 and 4b
    for (i, tok) in tokens[..name_idx].iter().enumerate().rev().take(LOOKBEHIND_LIMIT) {
        if !(name_decl_possible || func_sig_possible) { break; }

        if tok.depth <= init_depth && tok.serial != init_serial {
            // escaped the current nesting, stop the search
            return false;
        } else if tok.depth > init_depth {
            // ignore more nested tokens (but count them towards the threshold)
            continue;
        }

        // name_decl_possible can continue to func_sig_possible in place, so this should be first
        if func_sig_possible {
            match tok.tok.base {
                Tok::Comment |
                Tok::Name(_) |
                Tok::Punct(Punct::Dot) |
                Tok::Punct(Punct::Colon) => break,

                Tok::Keyword(Keyword::Function) => return true,

                _ => func_sig_possible = false,
            }
        }

        if name_decl_possible {
            match tok.tok.base {
                Tok::Comment |
                Tok::Name(_) |
                Tok::Punct(Punct::Comma) |
                // Newline to account for meta comments (other tokens are nested)
                Tok::Punct(Punct::Newline) => break,

                Tok::Punct(Punct::LParen) => {
                    // `function ... ( ... | ... )` is possible
                    // update the initial nesting to point to a token before `(` and proceed
                    if i == 0 { return false; }
                    init_depth = tokens[i-1].depth;
                    name_decl_possible = false;
                    func_sig_possible = true;
                }

                Tok::Keyword(Keyword::Local) |
                Tok::Keyword(Keyword::For) => return true,

                _ => name_decl_possible = false,
            }
        }
    }

    false
}

const EXPR_KEYWORDS: &'static [&'static str] = &[
    "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if",
    "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
];
const META_KEYWORDS: &'static [&'static str] = &[
    "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if",
    "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
    "assume", "const", "global", "map", "module", "once", "open", "type", "var", "vector",
];

fn keywords_per_category(nesting_category: NestingCategory) -> &'static [&'static str] {
    match nesting_category {
        NestingCategory::Expr => EXPR_KEYWORDS,
        NestingCategory::Meta => META_KEYWORDS,
    }
}

pub fn complete_name(tokens: &[NestedToken], name_idx: usize, nesting_category: NestingCategory,
                     pos: Pos, last_chunk: Arc<Chunk>) -> Vec<CompletionItem> {
    let mut items = Vec::new();

    // check if the caret is at the name definition and autocompletion should be disabled
    if nesting_category == NestingCategory::Expr && is_name_completion_disabled(tokens, name_idx) {
        return items;
    }

    for scope in last_chunk.map.scope_from_pos(pos) {
        for (name, _scope, _id) in last_chunk.map.names_and_scopes(scope) {
            let name = String::from_utf8_lossy(name).into_owned();
            items.push(make_item(name, CompletionItemKind::Variable, None));
        }
    }

    // TODO: global scope should be here

    for keyword in keywords_per_category(nesting_category) {
        items.push(make_item(keyword[..].to_owned(), CompletionItemKind::Keyword, None));
    }

    items
}

pub fn complete_field(tokens: &[NestedToken], sep_idx: usize,
                      last_output: Arc<Output>) -> Vec<CompletionItem> {
    let mut items = Vec::new();

    // note that this approach of using the closest non-comment token's end is
    // prone to syntax error; while completing `f"":` should work,
    // `"":` (invalid, should have been `(""):`) will also work.
    // as such a mistake can be readily reported, however, we don't try to perfect the approach.
    fn is_comment(tok: &NestedToken) -> bool {
        match tok.tok.base { Tok::Comment => true, _ => false }
    }
    let prefix_expr_end = match tokens[..sep_idx].iter().rev().find(|tok| !is_comment(tok)) {
        Some(tok) => tok.tok.span.end(),
        None => return items,
    };

    // find all slot-associated spans that intersects (even at the end points) the end pos...
    let spans = last_output.spanned_slots().adjacencies(Span::from(prefix_expr_end));
    // ...and keep spans which actually _ends_ at the end pos...
    let spans_before = spans.filter(|slot| slot.span.end() == prefix_expr_end);
    // ...and pick the smallest one among them (there should be at most one such span).
    let closest_slot = spans_before.min_by_key(|slot| slot.span.len());

    // now we've got the closest slot for given position; check if it's actually a table or similar
    if let Some(slot) = closest_slot.map(|s| s.map(|s| s.clone())) {
        if let Some(mut ty) = last_output.resolve_exact_type(&slot.unlift()) {
            if ty.flags() == T_STRING {
                if let Some(metaslot) = last_output.get_string_meta() {
                    // use the string meta table for strings
                    if let Some(metaty) = last_output.resolve_exact_type(&metaslot.unlift()) {
                        ty = metaty;
                    }
                }
            }

            if let Some(&Tables::Fields(ref fields)) = ty.get_tables() {
                for key in fields.keys() {
                    if let Key::Str(ref s) = *key {
                        let name = String::from_utf8_lossy(&s).into_owned();
                        items.push(make_item(name, CompletionItemKind::Field, None));
                    }
                }
            }
        }
    }

    items
}

<commit_msg>Fixed a lame translation error in the name completion.<commit_after>use std::cmp::Ordering;
use std::sync::Arc;

use kailua_env::{Pos, Span};
use kailua_syntax::{Tok, Punct, Keyword, NestedToken, NestingCategory, Chunk};
use kailua_check::{Output, Key, Tables};
use kailua_check::flags::T_STRING;

use protocol::*;

#[derive(Clone, Debug)]
pub enum CompletionClass {
    // complete a top-level name or keyword.
    // the caret is located inside tokens[i] or on the intersection of tokens[i-1] and tokens[i].
    // in the former case tokens[i] should be a name; in the latter case tokens[i-1] may not exist.
    Name(usize, NestingCategory),

    // complete a field or method after `.` or `:`.
    // the caret is located after tokens[i] (which should be `.` or `:`).
    Field(usize),
}

fn index_and_neighbor<T, F>(tokens: &[T], pos: Pos, as_span: F) -> (usize, bool, bool)
    where F: Fn(&T) -> Span
{
    //        pos
    // ________v________   idx  end   after
    //     BOF^  0000000   0    false false
    //     BOF^  $EOF      0    false false
    //     BOF^$EOF        0    false false
    // iiiiiiii  jjjjjjj   i    true  true
    // iiiiiiiijjjjjjjjj   i    true  true
    // iiiiii    jjjjjjj   i    false false
    // iiiiii  jjjjjjjjj   j    false false
    // iiii   jjjjjj kkk   j    false true
    // iii jjjjj   kkkkk   j    false true
    // ii jjjjj   kkkkkk   j    true  true
    // zzzzzzzz   $EOF     z    true  true
    // zzzz    $EOF        z    false false
    match tokens.binary_search_by(|tok| as_span(tok).begin().cmp(&pos)) {
        Ok(i) => { // tokens[i].begin == pos
            if i > 0 && as_span(&tokens[i-1]).end() == pos {
                (i-1, true, true)
            } else {
                (i, false, false)
            }
        },
        Err(0) => { // pos < tokens[0].begin or inf
            (0, false, false)
        },
        Err(i) => { // tokens[i-1].begin < pos < tokens[i].begin or inf
            match pos.cmp(&as_span(&tokens[i-1]).end()) {
                Ordering::Less => (i-1, false, true),
                Ordering::Equal => (i-1, true, true),
                Ordering::Greater => (i-1, false, false),
            }
        },
    }
}

#[test]
fn test_index_and_neighbor() {
    use kailua_env::{Source, SourceFile};

    // we need a sizable span to construct a dummy list of "tokens" (solely represented by spans)
    let mut source = Source::new();
    let span = source.add(SourceFile::from_u8("dummy".to_string(), b"0123456789"[..].to_owned()));
    let pos = |i| span.clone().nth(i).unwrap();

    let tokens = [Span::new(pos(1), pos(2)), Span::new(pos(2), pos(4)),
                  Span::new(pos(5), pos(7)), Span::new(pos(8), pos(8))];
    assert_eq!(index_and_neighbor(&tokens, pos(0), |&sp| sp), (0, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(1), |&sp| sp), (0, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(2), |&sp| sp), (0, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(3), |&sp| sp), (1, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(4), |&sp| sp), (1, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(5), |&sp| sp), (2, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(6), |&sp| sp), (2, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(7), |&sp| sp), (2, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(8), |&sp| sp), (3, false, false));
    assert_eq!(index_and_neighbor(&tokens, pos(9), |&sp| sp), (3, false, false));

    let tokens = [Span::new(pos(1), pos(2)), Span::new(pos(2), pos(4)),
                  Span::new(pos(5), pos(8)), Span::new(pos(8), pos(8))];
    assert_eq!(index_and_neighbor(&tokens, pos(7), |&sp| sp), (2, false, true));
    assert_eq!(index_and_neighbor(&tokens, pos(8), |&sp| sp), (2, true, true));
    assert_eq!(index_and_neighbor(&tokens, pos(9), |&sp| sp), (3, false, false));
}

pub fn classify(tokens: &[NestedToken], pos: Pos) -> Option<CompletionClass> {
    let (idx, end, after) = index_and_neighbor(tokens, pos, |tok| tok.tok.span);

    let ptok = if idx > 0 { tokens.get(idx-1) } else { None };
    let tok = tokens.get(idx);
    match (end, after, ptok.map(|tok| &tok.tok.base), tok.map(|tok| &tok.tok.base)) {
        // ... `.` | ...
        // ... `:` | ...
        (true, true, _, Some(&Tok::Punct(Punct::Dot))) |
        (true, true, _, Some(&Tok::Punct(Punct::Colon))) => {
            Some(CompletionClass::Field(idx))
        },

        // ... `.` NAM|E ...
        // ... `:` NAM|E ...
        // ... `.` NAME | ... (with no space between NAME and the caret)
        // ... `:` NAME | ...
        (_, true, Some(&Tok::Punct(Punct::Dot)), Some(&Tok::Name(_))) |
        (_, true, Some(&Tok::Punct(Punct::Dot)), Some(&Tok::Keyword(_))) |
        (_, true, Some(&Tok::Punct(Punct::Colon)), Some(&Tok::Name(_))) |
        (_, true, Some(&Tok::Punct(Punct::Colon)), Some(&Tok::Keyword(_))) => {
            Some(CompletionClass::Field(idx))
        },

        // ... NAME | ... (ditto)
        (_, true, _, Some(&Tok::Name(_))) |
        (_, true, _, Some(&Tok::Keyword(_))) => {
            Some(CompletionClass::Name(idx, tok.unwrap().category))
        },

        _ => None,
    }
}

fn make_item(label: String, kind: CompletionItemKind, detail: Option<String>) -> CompletionItem {
    CompletionItem {
        label: label,
        kind: Some(kind),
        detail: detail,
        documentation: None,
        sortText: None,
        filterText: None,
        insertText: None,
        textEdit: None,
        additionalTextEdits: Vec::new(),
        command: None,
        data: None,
    }
}

// check if the caret is located in regions where the autocompletion should be disabled:
//
// 1. `local NAME ... | ... [= ...]`
// 2. `for NAME ... | ... = ... do ... end`
// 3. `function NAME ... | ( ... )`
// 4. `function [NAME ...] ( ... | ... )`
//
// the check for 1 and 2 is handled by looking backward for the first token
// that is not a comment, a name or a comma and is in the same nesting as the caret.
// if the token exists and it's `local` or `for`, autocompletion is disabled.
//
// the check for 3 is handled by looking backward for the first token
// that is not a comment, a name, a dot or a colon and is in the same nesting as the caret.
// if the token exists and it's `function`, autocompletion is disabled.
//
// the check for 4 is handled similarly to the check for 1 and 2,
// but once seen a `(` token, it will switch to the check for 3 at the parent nesting.
//
// for the interactivity, the lookbehind is limited to a reasonable number.
fn is_name_completion_disabled(tokens: &[NestedToken], name_idx: usize) -> bool {
    const LOOKBEHIND_LIMIT: usize = 4096;

    let name_tok = &tokens[name_idx];
    let mut init_depth = name_tok.depth;
    let init_serial = name_tok.serial;

    let mut name_decl_possible = true; // case 1, 2 and 4a
    let mut func_sig_possible = true; // case 3 and 4b
    for (i, tok) in tokens[..name_idx].iter().enumerate().rev().take(LOOKBEHIND_LIMIT) {
        if !(name_decl_possible || func_sig_possible) { break; }

        if tok.depth <= init_depth && tok.serial != init_serial {
            // escaped the current nesting, stop the search
            return false;
        } else if tok.depth > init_depth {
            // ignore more nested tokens (but count them towards the threshold)
            continue;
        }

        // name_decl_possible can continue to func_sig_possible in place, so this should be first
        if func_sig_possible {
            match tok.tok.base {
                Tok::Comment |
                Tok::Name(_) |
                Tok::Punct(Punct::Dot) |
                Tok::Punct(Punct::Colon) => {},

                Tok::Keyword(Keyword::Function) => return true,

                _ => func_sig_possible = false,
            }
        }

        if name_decl_possible {
            match tok.tok.base {
                Tok::Comment |
                Tok::Name(_) |
                Tok::Punct(Punct::Comma) |
                // Newline to account for meta comments (other tokens are nested)
                Tok::Punct(Punct::Newline) => {},

                Tok::Punct(Punct::LParen) => {
                    // `function ... ( ... | ... )` is possible
                    // update the initial nesting to point to a token before `(` and proceed
                    if i == 0 { return false; }
                    init_depth = tokens[i-1].depth;
                    name_decl_possible = false;
                    func_sig_possible = true;
                }

                Tok::Keyword(Keyword::Local) |
                Tok::Keyword(Keyword::For) => return true,

                _ => name_decl_possible = false,
            }
        }
    }

    false
}

const EXPR_KEYWORDS: &'static [&'static str] = &[
    "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if",
    "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
];
const META_KEYWORDS: &'static [&'static str] = &[
    "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if",
    "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
    "assume", "const", "global", "map", "module", "once", "open", "type", "var", "vector",
];

fn keywords_per_category(nesting_category: NestingCategory) -> &'static [&'static str] {
    match nesting_category {
        NestingCategory::Expr => EXPR_KEYWORDS,
        NestingCategory::Meta => META_KEYWORDS,
    }
}

pub fn complete_name(tokens: &[NestedToken], name_idx: usize, nesting_category: NestingCategory,
                     pos: Pos, last_chunk: Arc<Chunk>) -> Vec<CompletionItem> {
    let mut items = Vec::new();

    // check if the caret is at the name definition and autocompletion should be disabled
    if nesting_category == NestingCategory::Expr && is_name_completion_disabled(tokens, name_idx) {
        return items;
    }

    for scope in last_chunk.map.scope_from_pos(pos) {
        for (name, _scope, _id) in last_chunk.map.names_and_scopes(scope) {
            let name = String::from_utf8_lossy(name).into_owned();
            items.push(make_item(name, CompletionItemKind::Variable, None));
        }
    }

    // TODO: global scope should be here

    for keyword in keywords_per_category(nesting_category) {
        items.push(make_item(keyword[..].to_owned(), CompletionItemKind::Keyword, None));
    }

    items
}

pub fn complete_field(tokens: &[NestedToken], sep_idx: usize,
                      last_output: Arc<Output>) -> Vec<CompletionItem> {
    let mut items = Vec::new();

    // note that this approach of using the closest non-comment token's end is
    // prone to syntax error; while completing `f"":` should work,
    // `"":` (invalid, should have been `(""):`) will also work.
    // as such a mistake can be readily reported, however, we don't try to perfect the approach.
    fn is_comment(tok: &NestedToken) -> bool {
        match tok.tok.base { Tok::Comment => true, _ => false }
    }
    let prefix_expr_end = match tokens[..sep_idx].iter().rev().find(|tok| !is_comment(tok)) {
        Some(tok) => tok.tok.span.end(),
        None => return items,
    };

    // find all slot-associated spans that intersects (even at the end points) the end pos...
    let spans = last_output.spanned_slots().adjacencies(Span::from(prefix_expr_end));
    // ...and keep spans which actually _ends_ at the end pos...
    let spans_before = spans.filter(|slot| slot.span.end() == prefix_expr_end);
    // ...and pick the smallest one among them (there should be at most one such span).
    let closest_slot = spans_before.min_by_key(|slot| slot.span.len());

    // now we've got the closest slot for given position; check if it's actually a table or similar
    if let Some(slot) = closest_slot.map(|s| s.map(|s| s.clone())) {
        if let Some(mut ty) = last_output.resolve_exact_type(&slot.unlift()) {
            if ty.flags() == T_STRING {
                if let Some(metaslot) = last_output.get_string_meta() {
                    // use the string meta table for strings
                    if let Some(metaty) = last_output.resolve_exact_type(&metaslot.unlift()) {
                        ty = metaty;
                    }
                }
            }

            if let Some(&Tables::Fields(ref fields)) = ty.get_tables() {
                for key in fields.keys() {
                    if let Key::Str(ref s) = *key {
                        let name = String::from_utf8_lossy(&s).into_owned();
                        items.push(make_item(name, CompletionItemKind::Field, None));
                    }
                }
            }
        }
    }

    items
}

<|endoftext|>


<commit_before>/*!
General Admin Commands
*/

use std::env;
use std::fs;
use std::path::PathBuf;
use std::io::Write;
use std::ffi::OsStr;
use clap::ArgMatches;

use errors::*;

fn default_static_root() -> PathBuf {
    let mut root = env::current_dir().expect("Failed to get the current directory");
    root.push("static/badges");
    root
}

/// Print a message and require y/n confirmation
fn confirm(msg: &str) -> Result<()> {
    print!("{}", msg);
    ::std::io::stdout().flush().expect("Error flushing stdout");
    let mut input = String::new();
    let stdin = ::std::io::stdin();
    stdin.read_line(&mut input).expect("Error reading stdin");
    if input.trim().to_lowercase() == "y" { return Ok(()) }
    Err(Error::Msg("Unable to confirm...".to_string()))
}

const CACHE_KEEP: [&'static str; 1] = [".gitkeep"];

fn clear_cached_files(no_confirm: bool, dir: &str) -> Result<()> {
    let static_root = if dir.is_empty() { default_static_root() } else { PathBuf::from(dir) };
    if !no_confirm {
        confirm(&format!("** Delete everything in {:?}? (y/n) > ", &static_root))?;
    }
    let read_dir = fs::read_dir(&static_root)
        .map_err(|e| Error::IoErrorMsg(e, format!("Unable to read `STATIC_ROOT` dir: {:?} - make sure you run this from the project root", &static_root)))?;

    let mut count = 0;
    for entry in read_dir {
        if let Ok(entry) = entry {
            let path = entry.path();

            if path.is_dir() {
                fs::remove_dir_all(path)?;
                continue;
            }

            if let Some(fname) = path.file_name().and_then(OsStr::to_str) {
                if CACHE_KEEP.contains(&fname) { continue; }
            }

            fs::remove_file(path)?;
            count += 1;
        };
    }

    println!("[badge-cache] [admin] - cleaned out {} cached badges in {:?}", count, &static_root);
    Ok(())
}

pub fn handle(matches: &ArgMatches) -> Result<()> {
    let no_confirm = matches.is_present("no-confirm");
    if let Some(dir) = matches.value_of("badge-dir") {
        clear_cached_files(no_confirm, &dir)?;
        return Ok(())
    }
    Ok(())
}
<commit_msg>admin | clear cached badges<commit_after>/*!
General Admin Commands
*/

use std::env;
use std::fs;
use std::path::PathBuf;
use std::io::Write;
use std::ffi::OsStr;
use clap::ArgMatches;

use errors::*;

fn default_static_root() -> PathBuf {
    let mut root = env::current_dir().expect("Failed to get the current directory");
    root.push("static/badges");
    root
}

/// Print a message and require y/n confirmation
fn confirm(msg: &str) -> Result<()> {
    print!("{}", msg);
    ::std::io::stdout().flush().expect("Error flushing stdout");
    let mut input = String::new();
    let stdin = ::std::io::stdin();
    stdin.read_line(&mut input).expect("Error reading stdin");
    if input.trim().to_lowercase() == "y" { return Ok(()) }
    Err(Error::Msg("Unable to confirm...".to_string()))
}

const CACHE_KEEP: [&'static str; 1] = [".gitkeep"];

fn clear_cached_files(no_confirm: bool, dir: &str) -> Result<()> {
    let static_root = if dir.is_empty() { default_static_root() } else { PathBuf::from(dir) };
    if !no_confirm {
        confirm(&format!("** Delete everything in {:?}? (y/n) > ", &static_root))?;
    }
    let read_dir = fs::read_dir(&static_root)
        .map_err(|e| Error::IoErrorMsg(e, format!("Unable to read `STATIC_ROOT` dir: {:?} - make sure you run this from the project root", &static_root)))?;

    let mut count = 0;
    for entry in read_dir {
        if let Ok(entry) = entry {
            let path = entry.path();

            if path.is_dir() {
                // ignore nested directories
                continue;
            }

            if let Some(fname) = path.file_name().and_then(OsStr::to_str) {
                if CACHE_KEEP.contains(&fname) { continue; }
            }

            fs::remove_file(path)?;
            count += 1;
        };
    }

    println!("[badge-cache] [admin] - cleaned out {} cached badges in {:?}", count, &static_root);
    Ok(())
}

pub fn handle(matches: &ArgMatches) -> Result<()> {
    let no_confirm = matches.is_present("no-confirm");
    if let Some(dir) = matches.value_of("badge-dir") {
        clear_cached_files(no_confirm, &dir)?;
        return Ok(())
    }
    Ok(())
}
<|endoftext|>


<commit_before>use network::buffer::Buffer;
use pool::Reset;
use std::io::{self,Write};
use nom::HexDisplay;
use std::cmp::max;

#[derive(Debug,PartialEq,Clone)]
pub enum InputElement {
  /// length in the stream
  Slice(usize),
  Splice(usize), // x bytes copied in kernel
}

#[derive(Debug,PartialEq,Clone)]
pub enum OutputElement {
  /// length in the stream
  Slice(usize),
  Delete(usize),
  Insert(Vec<u8>),
  Splice(usize), // should copy x bytes from kernel to socket
}

/// The BufferQueue has two roles: holding incoming data, and indicating
/// which data will go out. When new data arrives, it is added at the
/// end of the internal buffer. This new data is then eventually parsed or
/// handled in some way by external code. The external code then adds
/// element to the queue, indicating what to do with the data:
///   - copy a subset of the input data (and advance if needed)
///   - insert external data, like a HTTP header
///   - splice out of the kernel some data that was spliced in
///
/// position is the index in the stream of data already handled.
/// it corresponds to the beginning of available data in the Buffer
/// a Slice(begin, end) would point to buffer.data()[begin-position..end-position]
/// (in the easiest case)
///
/// unparsed_position is the index in the stream of data that was
/// not parsed yet
///
/// The buffer's available data may be smaller than `end - begin`.
/// It can happen if the parser indicated we need to copy more data than is available,
/// like with a content length
///
/// should the buffer queue indicate how much data it needs?
//#[derive(Debug,PartialEq,Clone)]
pub struct BufferQueue {
  /// position of buffer start in stream
  pub buffer_position:        usize,
  pub parsed_position:        usize,
  pub output_position:        usize,
  pub start_parsing_position: usize,
  pub buffer:                 Buffer,
  /// Vec<(start, length)>
  pub input_queue:            Vec<InputElement>,
  pub output_queue:           Vec<OutputElement>,
}

impl BufferQueue {
  pub fn with_capacity(capacity: usize) -> BufferQueue {
    BufferQueue {
      buffer_position:        0,
      parsed_position:        0,
      output_position:        0,
      start_parsing_position: 0,
      buffer:                 Buffer::with_capacity(capacity),
      input_queue:            Vec::with_capacity(8),
      output_queue:           Vec::with_capacity(8),
    }
  }

  pub fn available_input_data(&self) -> usize {
    self.input_queue.iter().fold(0, |acc, el| {
      acc + match el {
        &InputElement::Slice(sz) | &InputElement::Splice(sz) => sz
      }
    })
  }

  pub fn spliced_input(&mut self, count: usize) {
    self.input_queue.push(InputElement::Splice(count));
  }

  pub fn merge_input_slices(&self) -> usize {
    let mut acc = 0usize;
    for el in self.input_queue.iter() {
      match el {
        &InputElement::Splice(_) => break,
        &InputElement::Slice(sz) => acc += sz,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged input slices can't be larger than current data in buffer");
    acc
  }

  pub fn input_data_size(&self) -> usize {
    let mut acc = 0usize;
    for el in self.input_queue.iter() {
      match el {
        &InputElement::Splice(sz) => acc += sz,
        &InputElement::Slice(sz)  => acc += sz,
      }
    }
    acc
  }

  pub fn unparsed_data(&self) -> &[u8] {
    let largest_size = self.merge_input_slices();
    if largest_size == 0 {
      return &self.buffer.data()[0..0];
    }
    let end = max(self.buffer.data().len(), self.parsed_position+largest_size);
    &self.buffer.data()[self.parsed_position..end]
  }

  /// should only be called with a count inferior to self.input_data_size()
  pub fn consume_parsed_data(&mut self, size: usize) {
    //FIXME: to_consume must contain unparsed_position - parsed_position ?
    let mut to_consume = size;
    while to_consume > 0 {
      let new_first_element = match self.input_queue.first() {
        None => {
          //assert!(to_consume == 0, "no more element in queue, we should not ask to consume {} more bytes", to_consume);
          break;
        },
        Some(&InputElement::Slice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = InputElement::Slice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&InputElement::Splice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            panic!("we should not start parsing from inside a splicing buffer. But what if consume_parsed_data was called during a parsing loop? Should only call consume_parsed_data after the parsing loop finished");
          }
        },
      };

      match new_first_element {
        None     => { self.input_queue.remove(0); },
        Some(el) => { self.input_queue[0] = el; },
      };
    }

    self.parsed_position        += size - to_consume;
    self.start_parsing_position += size;
  }

  pub fn slice_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Slice(count));
  }

  pub fn delete_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Delete(count));
  }

  pub fn splice_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Splice(count));
  }

  pub fn insert_output(&mut self, v: Vec<u8>) {
    self.output_queue.push(OutputElement::Insert(v));
  }

  pub fn output_data_size(&self) -> usize {
    let mut acc = 0usize;
    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Splice(sz)    => acc += sz,
        &OutputElement::Slice(sz)     => acc += sz,
        &OutputElement::Insert(ref v) => acc += v.len(),
        &OutputElement::Delete(_)     => {},
      }
    }
    acc
  }

  pub fn merge_output_slices(&self) -> usize {
    let mut acc = 0usize;
    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Slice(sz) => acc += sz,
        _ => break,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged output slices can't be larger than current data in buffer");
    acc
  }

  pub fn merge_output_deletes(&self) -> usize {
    let mut acc = 0usize;
    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Delete(sz) => acc += sz,
        _ => break,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged output deletes can't be larger than current data in buffer");
    acc
  }

  pub fn next_output_data(&self) -> &[u8] {
    let mut it = self.output_queue.iter();
    //first, calculate how many bytes we need to jump
    let mut start         = 0usize;
    let mut largest_size  = 0usize;
    let mut delete_ended  = false;
    for el in it {
      println!("start={}, length={}, el = {:?}", start, largest_size, el);
      if !delete_ended {
        match el {
          &OutputElement::Delete(sz) => start += sz,
          _ => {
            delete_ended = true;
            match el {
              &OutputElement::Slice(sz)     => largest_size += sz,
              &OutputElement::Insert(ref v) => return &v[..],
              _ => break,
            }
          },
        }
      } else {
        match el {
          &OutputElement::Slice(sz) => largest_size += sz,
          _ => break,
        }
      }
    }

    println!("buffer data: {:?}", self.buffer.data());
    println!("calculated start={}, length={}", start, largest_size);
    &self.buffer.data()[start..start+largest_size]
  }

  /// should only be called with a count inferior to self.input_data_size()
  pub fn consume_output_data(&mut self, size: usize) {
    let mut to_consume = size;
    while to_consume > 0 {
      let new_first_element = match self.output_queue.first() {
        None => {
          assert!(to_consume == 0, "no more element in queue, we should not ask to consume {} more bytes", to_consume);
          break;
        },
        Some(&OutputElement::Slice(sz)) => {
          self.buffer_position += to_consume;
          self.buffer.consume(to_consume);

          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = OutputElement::Slice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&OutputElement::Delete(sz)) => {
          self.buffer_position += sz;
          //FIXME: what if we can't delete that much data?
          self.buffer.consume(sz);
          None
        },
        Some(&OutputElement::Splice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = OutputElement::Splice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&OutputElement::Insert(ref v)) => {
          if to_consume >= v.len() {
            to_consume = to_consume - v.len();
            None
          } else {
            let new_element = OutputElement::Insert(Vec::from(&v[to_consume..]));
            to_consume = 0;
            Some(new_element)
          }
        },
      };

      match new_first_element {
        None     => { self.output_queue.remove(0); },
        Some(el) => { self.output_queue[0] = el; },
      };
    }
  }
}

impl Write for BufferQueue {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    match self.buffer.write(buf) {
      Err(e) => Err(e),
      Ok(sz) => {
        self.input_queue.push(InputElement::Slice(sz));
        Ok(sz)
      }
    }
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

impl Reset for BufferQueue {
  fn reset(&mut self) {
    self.parsed_position = 0;
    self.output_position = 0;
    self.buffer_position = 0;
    self.start_parsing_position = 0;
    self.buffer.reset();
    self.input_queue.clear();
    self.output_queue.clear();
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use network::buffer::Buffer;
  use nom::HexDisplay;
  use std::io::Write;

  #[test]
  fn consume() {
    let mut b = BufferQueue {
      parsed_position:        0,
      output_position:        0,
      buffer_position:        0,
      start_parsing_position: 0,
      buffer:                 Buffer::from_slice(b"ABCDEFGHIJ"),
      input_queue:            vec!(InputElement::Slice(10)),
      output_queue:           vec!()
    };

    assert_eq!(b.unparsed_data(), &b"ABCDEFGHIJ"[..]);
    b.consume_parsed_data(4);
    assert_eq!(b.parsed_position, 4);
    assert_eq!(b.start_parsing_position, 4);
    assert_eq!(b.input_queue, vec!(InputElement::Slice(6)));
    println!("TEST[{}]", line!());
    assert_eq!(b.unparsed_data(), &b"EFGHIJ"[..]);
    println!("TEST[{}]", line!());

    b.slice_output(4);
    assert_eq!(b.output_queue, vec!(OutputElement::Slice(4)));

    b.insert_output(Vec::from(&b"test"[..]));
    assert_eq!(b.output_queue, vec!(
        OutputElement::Slice(4),
        OutputElement::Insert(Vec::from(&b"test"[..]))
      )
    );
    assert_eq!(b.next_output_data(), &b"ABCD"[..]);

    b.consume_output_data(2);
    assert_eq!(b.next_output_data(), &b"CD"[..]);

    println!("TEST[{}]", line!());
    b.consume_parsed_data(8);
    assert_eq!(b.parsed_position, 10);
    assert_eq!(b.start_parsing_position, 12);
    assert_eq!(b.input_queue, vec!());

    println!("TEST[{}]", line!());
    assert_eq!(b.unparsed_data(), &b""[..]);
    println!("TEST[{}]", line!());

    println!("**test**");
    b.consume_output_data(2);
    assert_eq!(b.next_output_data(), &b"test"[..]);
    b.consume_output_data(2);
    assert_eq!(b.next_output_data(), &b"st"[..]);

    b.delete_output(2);
    b.slice_output(4);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Insert(Vec::from(&b"st"[..])),
        OutputElement::Delete(2),
        OutputElement::Slice(4)
      )
    );

    b.consume_output_data(2);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Delete(2),
        OutputElement::Slice(4)
      )
    );
    assert_eq!(b.next_output_data(), &b"GHIJ"[..]);

    b.consume_output_data(1);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Slice(3)
      )
    );
    assert_eq!(b.next_output_data(), &b"HIJ"[..]);

    b.write(&b"KLMNOP"[..]);
  }
}
<commit_msg>more utility function for BufferQueue<commit_after>use network::buffer::Buffer;
use pool::Reset;
use std::io::{self,Write};
use nom::HexDisplay;
use std::cmp::{min,max};

#[derive(Debug,PartialEq,Clone)]
pub enum InputElement {
  /// length in the stream
  Slice(usize),
  Splice(usize), // x bytes copied in kernel
}

#[derive(Debug,PartialEq,Clone)]
pub enum OutputElement {
  /// length in the stream
  Slice(usize),
  Delete(usize),
  Insert(Vec<u8>),
  Splice(usize), // should copy x bytes from kernel to socket
}

/// The BufferQueue has two roles: holding incoming data, and indicating
/// which data will go out. When new data arrives, it is added at the
/// end of the internal buffer. This new data is then eventually parsed or
/// handled in some way by external code. The external code then adds
/// element to the queue, indicating what to do with the data:
///   - copy a subset of the input data (and advance if needed)
///   - insert external data, like a HTTP header
///   - splice out of the kernel some data that was spliced in
///
/// position is the index in the stream of data already handled.
/// it corresponds to the beginning of available data in the Buffer
/// a Slice(begin, end) would point to buffer.data()[begin-position..end-position]
/// (in the easiest case)
///
/// unparsed_position is the index in the stream of data that was
/// not parsed yet
///
/// The buffer's available data may be smaller than `end - begin`.
/// It can happen if the parser indicated we need to copy more data than is available,
/// like with a content length
///
/// should the buffer queue indicate how much data it needs?
#[derive(Debug,PartialEq,Clone)]
pub struct BufferQueue {
  /// position of buffer start in stream
  pub buffer_position:        usize,
  pub parsed_position:        usize,
  pub output_position:        usize,
  pub start_parsing_position: usize,
  pub buffer:                 Buffer,
  /// Vec<(start, length)>
  pub input_queue:            Vec<InputElement>,
  pub output_queue:           Vec<OutputElement>,
}

impl BufferQueue {
  pub fn with_capacity(capacity: usize) -> BufferQueue {
    BufferQueue {
      buffer_position:        0,
      parsed_position:        0,
      output_position:        0,
      start_parsing_position: 0,
      buffer:                 Buffer::with_capacity(capacity),
      input_queue:            Vec::with_capacity(8),
      output_queue:           Vec::with_capacity(8),
    }
  }

  pub fn available_input_data(&self) -> usize {
    self.input_queue.iter().fold(0, |acc, el| {
      acc + match el {
        &InputElement::Slice(sz) | &InputElement::Splice(sz) => sz
      }
    })
  }

  pub fn sliced_input(&mut self, count: usize) {
    let needed = self.start_parsing_position - self.parsed_position;
    if needed > 0 {
      if count >= needed {
        self.parsed_position = self.start_parsing_position;
        self.slice_output(needed);
      } else {
        self.parsed_position += count;
        self.slice_output(count);
      }
    } else {
      if count > 0 {
        self.input_queue.push(InputElement::Slice(count));
      }
    }
    //println!("sliced_input: buffer size: {}, parsed_position: {} start_parsing_position: {}, input_queue: {:?}, output_queue: {:?}",
    //  self.buffer.available_data(), self.parsed_position, self.start_parsing_position,
    //  self.input_queue, self.output_queue);
  }

  pub fn spliced_input(&mut self, count: usize) {
    //FIXME: do the same thing with needed data as in sliced_input
    if count >0 {
      self.input_queue.push(InputElement::Splice(count));
    }
  }

  pub fn needs_input(&self) -> bool {
    self.start_parsing_position > self.parsed_position
  }

  pub fn merge_input_slices(&self) -> usize {
    let mut acc = 0usize;
    for el in self.input_queue.iter() {
      match el {
        &InputElement::Splice(_) => break,
        &InputElement::Slice(sz) => acc += sz,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged input slices can't be larger than current data in buffer");
    acc
  }

  pub fn input_data_size(&self) -> usize {
    let mut acc = 0usize;
    for el in self.input_queue.iter() {
      match el {
        &InputElement::Splice(sz) => acc += sz,
        &InputElement::Slice(sz)  => acc += sz,
      }
    }
    acc
  }

  pub fn unparsed_data(&self) -> &[u8] {
    let largest_size = self.merge_input_slices();
    //println!("buffer: {}, parsed: {}", self.buffer_position, self.parsed_position);
    let start = self.parsed_position - self.buffer_position;
    if largest_size == 0 || start >= self.buffer.available_data() {
      return &self.buffer.data()[0..0];
    }
    //println!("available buffer data: {}, buffer position: {}, parsed_position: {}, start: {}, merged slices size: {}",
      self.buffer.available_data(), self.buffer_position,
    self.parsed_position, start, largest_size);
    let end = max(self.buffer.available_data(), start+largest_size);
    &self.buffer.data()[start..end]
  }

  /// should only be called with a count inferior to self.input_data_size()
  pub fn consume_parsed_data(&mut self, size: usize) {
    //FIXME: to_consume must contain unparsed_position - parsed_position ?
    let mut to_consume = size;
    while to_consume > 0 {
      let new_first_element = match self.input_queue.first() {
        None => {
          //assert!(to_consume == 0, "no more element in queue, we should not ask to consume {} more bytes", to_consume);
          break;
        },
        Some(&InputElement::Slice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = InputElement::Slice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&InputElement::Splice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            panic!("we should not start parsing from inside a splicing buffer. But what if consume_parsed_data was called during a parsing loop? Should only call consume_parsed_data after the parsing loop finished");
          }
        },
      };

      match new_first_element {
        None     => { self.input_queue.remove(0); },
        Some(el) => { self.input_queue[0] = el; },
      };
    }

    self.parsed_position        += size - to_consume;
    self.start_parsing_position += size;
  }

  pub fn slice_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Slice(count));
  }

  pub fn delete_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Delete(count));
  }

  pub fn splice_output(&mut self, count: usize) {
    self.output_queue.push(OutputElement::Splice(count));
  }

  pub fn insert_output(&mut self, v: Vec<u8>) {
    self.output_queue.push(OutputElement::Insert(v));
  }

  pub fn has_output_data(&self) -> bool {
    self.output_queue.len() > 0
  }

  pub fn output_data_size(&self) -> usize {
    let mut acc = 0usize;
    let mut available_buffer_size = self.buffer.available_data();

    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Splice(sz)    => acc += sz,
        &OutputElement::Slice(sz)     => {
          if available_buffer_size >= sz {
            acc += sz;
            available_buffer_size -= sz;
          } else {
            let advance = sz - available_buffer_size;
            acc += advance;
            return acc;
          }
        },
        &OutputElement::Insert(ref v) => acc += v.len(),
        &OutputElement::Delete(sz)    => {
          if available_buffer_size >=sz {
            available_buffer_size -= sz;
          } else {
            return acc;
          }
        },
      }
    }
    acc
  }

  pub fn merge_output_slices(&self) -> usize {
    let mut acc = 0usize;
    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Slice(sz) => acc += sz,
        _ => break,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged output slices can't be larger than current data in buffer");
    acc
  }

  pub fn merge_output_deletes(&self) -> usize {
    let mut acc = 0usize;
    for el in self.output_queue.iter() {
      match el {
        &OutputElement::Delete(sz) => acc += sz,
        _ => break,
      }
    }

    assert!(acc <= self.buffer.available_data(), "the merged output deletes can't be larger than current data in buffer");
    acc
  }

  pub fn next_output_data(&self) -> &[u8] {
    let mut it = self.output_queue.iter();
    //first, calculate how many bytes we need to jump
    let mut start         = 0usize;
    let mut largest_size  = 0usize;
    let mut delete_ended  = false;
    let mut available_buffer_size = self.buffer.available_data();
    //println!("NEXT OUTPUT DATA:\nqueue:\n{:?}\nbuffer:\n{}", self.output_queue, self.buffer.data().to_hex(16));
    for el in it {
      //println!("start={}, length={}, el = {:?}", start, largest_size, el);
      if !delete_ended {
        match el {
          &OutputElement::Delete(sz) => start += sz,
          _ => {
            delete_ended = true;
            match el {
              &OutputElement::Slice(sz)     => largest_size += sz,
              &OutputElement::Insert(ref v) => return &v[..],
              _ => break,
            }
          },
        }
      } else {
        match el {
          &OutputElement::Slice(sz) => largest_size += sz,
          _ => break,
        }
      }
    }

    //println!("buffer data: {:?}", self.buffer.data());
    //println!("calculated start={}, length={}", start, largest_size);
    //FIXME: should not be larger than the buffer
    let length = self.buffer.available_data();
    if start > length {
      &self.buffer.data()[0..0]
    } else {
      let end = min(start+largest_size, length);
      &self.buffer.data()[start..end]
    }
  }

  /// should only be called with a count inferior to self.input_data_size()
  pub fn consume_output_data(&mut self, size: usize) {
    let mut to_consume = size;
    while to_consume > 0 {
      let new_first_element = match self.output_queue.first() {
        None => {
          assert!(to_consume == 0, "no more element in queue, we should not ask to consume {} more bytes", to_consume);
          break;
        },
        Some(&OutputElement::Slice(sz)) => {
          self.buffer_position += to_consume;
          self.buffer.consume(to_consume);

          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = OutputElement::Slice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&OutputElement::Delete(sz)) => {
          self.buffer_position += sz;
          //FIXME: what if we can't delete that much data?
          self.buffer.consume(sz);
          None
        },
        Some(&OutputElement::Splice(sz)) => {
          if to_consume >= sz {
            to_consume -= sz;
            None
          } else {
            let new_element = OutputElement::Splice(sz - to_consume);
            to_consume = 0;
            Some(new_element)
          }
        },
        Some(&OutputElement::Insert(ref v)) => {
          if to_consume >= v.len() {
            to_consume = to_consume - v.len();
            None
          } else {
            let new_element = OutputElement::Insert(Vec::from(&v[to_consume..]));
            to_consume = 0;
            Some(new_element)
          }
        },
      };

      match new_first_element {
        None     => { self.output_queue.remove(0); },
        Some(el) => { self.output_queue[0] = el; },
      };
    }
  }
}

impl Write for BufferQueue {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    match self.buffer.write(buf) {
      Err(e) => Err(e),
      Ok(sz) => {
        self.input_queue.push(InputElement::Slice(sz));
        Ok(sz)
      }
    }
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

impl Reset for BufferQueue {
  fn reset(&mut self) {
    self.parsed_position = 0;
    self.output_position = 0;
    self.buffer_position = 0;
    self.start_parsing_position = 0;
    self.buffer.reset();
    self.input_queue.clear();
    self.output_queue.clear();
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use network::buffer::Buffer;
  use nom::HexDisplay;
  use std::io::Write;

  #[test]
  fn consume() {
    let mut b = BufferQueue {
      parsed_position:        0,
      output_position:        0,
      buffer_position:        0,
      start_parsing_position: 0,
      buffer:                 Buffer::from_slice(b"ABCDEFGHIJ"),
      input_queue:            vec!(InputElement::Slice(10)),
      output_queue:           vec!()
    };

    assert_eq!(b.unparsed_data(), &b"ABCDEFGHIJ"[..]);
    b.consume_parsed_data(4);
    assert_eq!(b.parsed_position, 4);
    assert_eq!(b.start_parsing_position, 4);
    assert_eq!(b.input_queue, vec!(InputElement::Slice(6)));
    println!("TEST[{}]", line!());
    assert_eq!(b.unparsed_data(), &b"EFGHIJ"[..]);
    println!("TEST[{}]", line!());

    b.slice_output(4);
    assert_eq!(b.output_queue, vec!(OutputElement::Slice(4)));

    b.insert_output(Vec::from(&b"test"[..]));
    assert_eq!(b.output_queue, vec!(
        OutputElement::Slice(4),
        OutputElement::Insert(Vec::from(&b"test"[..]))
      )
    );
    assert_eq!(b.next_output_data(), &b"ABCD"[..]);

    println!("before consume: {:?}", b);
    b.consume_output_data(2);
    println!("after consume: {:?}", b);
    println!("next output data: {}", b.next_output_data().to_hex(8));
    assert_eq!(b.next_output_data(), &b"CD"[..]);

    println!("TEST[{}]", line!());
    b.consume_parsed_data(8);
    assert_eq!(b.parsed_position, 10);
    assert_eq!(b.start_parsing_position, 12);
    assert_eq!(b.input_queue, vec!());

    println!("TEST[{}]", line!());
    assert_eq!(b.unparsed_data(), &b""[..]);
    println!("TEST[{}]", line!());

    println!("**test**");
    b.consume_output_data(2);
    assert_eq!(b.next_output_data(), &b"test"[..]);
    b.consume_output_data(2);
    assert_eq!(b.next_output_data(), &b"st"[..]);

    b.delete_output(2);
    b.slice_output(4);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Insert(Vec::from(&b"st"[..])),
        OutputElement::Delete(2),
        OutputElement::Slice(4)
      )
    );

    b.consume_output_data(2);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Delete(2),
        OutputElement::Slice(4)
      )
    );
    assert_eq!(b.next_output_data(), &b"GHIJ"[..]);

    b.consume_output_data(1);
    assert_eq!(
      b.output_queue,
      vec!(
        OutputElement::Slice(3)
      )
    );
    assert_eq!(b.next_output_data(), &b"HIJ"[..]);

    b.write(&b"KLMNOP"[..]);
  }
}
<|endoftext|>


<commit_before>//! Byte level parsers and combinators
//!

/// `tag!(&[T]: nom::AsBytes) => &[T] -> IResult<&[T], &[T]>`
/// declares a byte array as a suite to recognize
///
/// consumes the recognized characters
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!(x, tag!("abcd"));
///  let r = x(&b"abcdefgh"[..]);
///  assert_eq!(r, Done(&b"efgh"[..], &b"abcd"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! tag (
  ($i:expr, $inp: expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected = $inp;
      let bytes = as_bytes(&expected);

      if bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(bytes.len()))
      } else if &$i[0..bytes.len()] == bytes {
        $crate::IResult::Done(&$i[bytes.len()..], &$i[0..bytes.len()])
      } else {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::Tag as u32, $i))
      }
    }
  );
);

/// `is_not!(&[T:AsBytes]) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes that do not appear in the provided array
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!( not_space, is_not!( " \t\r\n" ) );
///
///  let r = not_space(&b"abcdefgh\nijkl"[..]);
///  assert_eq!(r, Done(&b"\nijkl"[..], &b"abcdefgh"[..]));
///  # }
/// ```
#[macro_export]
macro_rules! is_not(
  ($input:expr, $arr:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $arr;
      let bytes      = as_bytes(&expected);
      let mut parsed = false;
      let mut index  = 0;

      for idx in 0..$input.len() {
        index = idx;
        for &i in bytes.iter() {
          if $input[idx] == i {
            parsed = true;
            break;
          }
        }
        if parsed { break; }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::IsNot as u32,$input))
      } else {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      }
    }
  );
);

/// `is_a!(&[T]) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes that appear in the provided array
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!(abcd, is_a!( "abcd" ));
///
///  let r1 = abcd(&b"aaaaefgh"[..]);
///  assert_eq!(r1, Done(&b"efgh"[..], &b"aaaa"[..]));
///
///  let r2 = abcd(&b"dcbaefgh"[..]);
///  assert_eq!(r2, Done(&b"efgh"[..], &b"dcba"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! is_a(
  ($input:expr, $arr:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected  = $arr;
      let bytes     = as_bytes(&expected);
      let mut index = 0;

      for idx in 0..$input.len() {
        index = idx;
        let mut cont = false;
        for &i in bytes.iter() {
          if $input[idx] == i {
            cont = true;
            break;
          }
        }
        if !cont { break; }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::IsA as u32,$input))
      } else {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      }
    }
  );
);

/// `filter!(&[T] -> bool) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes until the provided function fails.
///
/// The argument is either a function `&[T] -> bool` or a macro returning a `bool
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # use nom::is_alphanumeric;
/// # fn main() {
///  named!( alpha, filter!( is_alphanumeric ) );
///
///  let r = alpha(&b"abcd\nefgh"[..]);
///  assert_eq!(r, Done(&b"\nefgh"[..], &b"abcd"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! filter(
  ($input:expr, $submac:ident!( $($args:tt)* )) => (

    {
      let mut index = 0;
      let mut found = false;
      for idx in 0..$input.len() {
        index = idx;
        if !$submac!($input[idx], $($args)*) {
          found = true;
          break;
        }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::Filter as u32,$input))
      } else if found {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      } else {
        $crate::IResult::Done(&b""[..], $input)
      }
    }
  );
  ($input:expr, $f:expr) => (
    filter!($input, call!($f));
  );
);

/// `take!(nb) => &[T] -> IResult<&[T], &[T]>`
/// generates a parser consuming the specified number of bytes
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  // Desmond parser
///  named!(take5, take!( 5 ) );
///
///  let a = b"abcdefgh";
///
///  assert_eq!(take5(&a[..]), Done(&b"fgh"[..], &b"abcde"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! take(
  ($i:expr, $count:expr) => (
    {
      let cnt = $count as usize;
      if $i.len() < cnt {
        $crate::IResult::Incomplete($crate::Needed::Size(cnt))
      } else {
        $crate::IResult::Done(&$i[cnt..],&$i[0..cnt])
      }
    }
  );
);

/// `take!(nb) => &[T] -> IResult<&[T], &str>`
/// same as take! but returning a &str
#[macro_export]
macro_rules! take_str (
 ( $i:expr, $size:expr ) => ( map_res!($i, take!($size), from_utf8) );
);

/// `take_until_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>`
/// generates a parser consuming bytes until the specified byte sequence is found
#[macro_export]
macro_rules! take_until_and_consume(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      let mut index  = 0;
      let mut parsed = false;

      for idx in 0..$i.len() {
        if idx + bytes.len() > $i.len() {
          index = idx;
          break;
        }
        if &$i[idx..idx + bytes.len()] == bytes {
          parsed = true;
          index = idx;
          break;
        }
      }

      if index + bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(index + bytes.len()))
      } else {
        if parsed {
          $crate::IResult::Done(&$i[(index + bytes.len())..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilAndConsume as u32,$i))
        }
      }
    }
  );
);

/// `take_until!(tag) => &[T] -> IResult<&[T], &[T]>`
#[macro_export]
macro_rules! take_until(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      let mut index  = 0;
      let mut parsed = false;

      for idx in 0..$i.len() {
        if idx + bytes.len() > $i.len() {
          index = idx;
          break;
        }
        if &$i[idx..idx+bytes.len()] == bytes {
          parsed = true;
          index  = idx;
          break;
        }
      }

      if index + bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(index + bytes.len()))
      } else {
        if parsed {
          $crate::IResult::Done(&$i[index..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntil as u32,$i))
        }
      }
    }
  );
);

/// `take_until_either_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>`
#[macro_export]
macro_rules! take_until_either_and_consume(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      let mut index  = 0;
      let mut parsed = false;

      for idx in 0..$i.len() {
        if idx + 1 > $i.len() {
          index = idx;
          break;
        }
        for &t in bytes.iter() {
          if $i[idx] == t {
            parsed = true;
            index = idx;
            break;
          }
        }
        if parsed { break; }
      }
      if index + 1 > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(index + 1))
      } else {
        if parsed {
          $crate::IResult::Done(&$i[(index+1)..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilEitherAndConsume as u32,$i))
        }
      }
    }
  );
);

/// `take_until_either!(tag) => &[T] -> IResult<&[T], &[T]>`
#[macro_export]
macro_rules! take_until_either(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      let mut index  = 0;
      let mut parsed = false;

      for idx in 0..$i.len() {
        if idx + 1 > $i.len() {
          index = idx;
          break;
        }
        for &t in bytes.iter() {
          if $i[idx] == t {
            parsed = true;
            index = idx;
            break;
          }
        }
        if parsed { break; }
      }
      if index + 1 > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(index + 1))
      } else {
        if parsed {
          $crate::IResult::Done(&$i[index..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilEither as u32,$i))
        }
      }
    }
  );
);

#[cfg(test)]
mod tests {
  use internal::Needed;
  use internal::IResult::*;
  use internal::Err::*;
  use util::ErrorCode;

  #[test]
  fn is_a() {
    named!(a_or_b, is_a!(&b"ab"[..]));

    let a = &b"abcd"[..];
    assert_eq!(a_or_b(a), Done(&b"cd"[..], &b"ab"[..]));

    let b = &b"bcde"[..];
    assert_eq!(a_or_b(b), Done(&b"cde"[..], &b"b"[..]));

    let c = &b"cdef"[..];
    assert_eq!(a_or_b(c), Error(Position(ErrorCode::IsA as u32,c)));

    let d = &b"bacdef"[..];
    assert_eq!(a_or_b(d), Done(&b"cdef"[..], &b"ba"[..]));
  }

  use std::str::from_utf8;
  #[test]
  fn take_str_test() {
    let a = b"omnomnom";

    assert_eq!(take_str!(&a[..], 5), Done(&b"nom"[..], "omnom"));
    assert_eq!(take_str!(&a[..], 9), Incomplete(Needed::Size(9)));
  }

  #[test]
  fn take_until_test() {
    named!(x, take_until_and_consume!("efgh"));
    let r = x(&b"abcdabcdefghijkl"[..]);
    assert_eq!(r, Done(&b"ijkl"[..], &b"abcdabcd"[..]));

    println!("Done 1\n");

    let r2 = x(&b"abcdabcdefgh"[..]);
    assert_eq!(r2, Done(&b""[..], &b"abcdabcd"[..]));

    println!("Done 2\n");
    let r3 = x(&b"abcefg"[..]);
    assert_eq!(r3, Incomplete(Needed::Size(7)));
  }

}
<commit_msg>Be more explicit about the take_until* parsers<commit_after>//! Byte level parsers and combinators
//!

/// `tag!(&[T]: nom::AsBytes) => &[T] -> IResult<&[T], &[T]>`
/// declares a byte array as a suite to recognize
///
/// consumes the recognized characters
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!(x, tag!("abcd"));
///  let r = x(&b"abcdefgh"[..]);
///  assert_eq!(r, Done(&b"efgh"[..], &b"abcd"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! tag (
  ($i:expr, $inp: expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected = $inp;
      let bytes = as_bytes(&expected);

      if bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(bytes.len()))
      } else if &$i[0..bytes.len()] == bytes {
        $crate::IResult::Done(&$i[bytes.len()..], &$i[0..bytes.len()])
      } else {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::Tag as u32, $i))
      }
    }
  );
);

/// `is_not!(&[T:AsBytes]) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes that do not appear in the provided array
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!( not_space, is_not!( " \t\r\n" ) );
///
///  let r = not_space(&b"abcdefgh\nijkl"[..]);
///  assert_eq!(r, Done(&b"\nijkl"[..], &b"abcdefgh"[..]));
///  # }
/// ```
#[macro_export]
macro_rules! is_not(
  ($input:expr, $arr:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $arr;
      let bytes      = as_bytes(&expected);
      let mut parsed = false;
      let mut index  = 0;

      for idx in 0..$input.len() {
        index = idx;
        for &i in bytes.iter() {
          if $input[idx] == i {
            parsed = true;
            break;
          }
        }
        if parsed { break; }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::IsNot as u32,$input))
      } else {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      }
    }
  );
);

/// `is_a!(&[T]) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes that appear in the provided array
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  named!(abcd, is_a!( "abcd" ));
///
///  let r1 = abcd(&b"aaaaefgh"[..]);
///  assert_eq!(r1, Done(&b"efgh"[..], &b"aaaa"[..]));
///
///  let r2 = abcd(&b"dcbaefgh"[..]);
///  assert_eq!(r2, Done(&b"efgh"[..], &b"dcba"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! is_a(
  ($input:expr, $arr:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected  = $arr;
      let bytes     = as_bytes(&expected);
      let mut index = 0;

      for idx in 0..$input.len() {
        index = idx;
        let mut cont = false;
        for &i in bytes.iter() {
          if $input[idx] == i {
            cont = true;
            break;
          }
        }
        if !cont { break; }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::IsA as u32,$input))
      } else {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      }
    }
  );
);

/// `filter!(&[T] -> bool) => &[T] -> IResult<&[T], &[T]>`
/// returns the longest list of bytes until the provided function fails.
///
/// The argument is either a function `&[T] -> bool` or a macro returning a `bool
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # use nom::is_alphanumeric;
/// # fn main() {
///  named!( alpha, filter!( is_alphanumeric ) );
///
///  let r = alpha(&b"abcd\nefgh"[..]);
///  assert_eq!(r, Done(&b"\nefgh"[..], &b"abcd"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! filter(
  ($input:expr, $submac:ident!( $($args:tt)* )) => (

    {
      let mut index = 0;
      let mut found = false;
      for idx in 0..$input.len() {
        index = idx;
        if !$submac!($input[idx], $($args)*) {
          found = true;
          break;
        }
      }
      if index == 0 {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::Filter as u32,$input))
      } else if found {
        $crate::IResult::Done(&$input[index..], &$input[0..index])
      } else {
        $crate::IResult::Done(&b""[..], $input)
      }
    }
  );
  ($input:expr, $f:expr) => (
    filter!($input, call!($f));
  );
);

/// `take!(nb) => &[T] -> IResult<&[T], &[T]>`
/// generates a parser consuming the specified number of bytes
///
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult::Done;
/// # fn main() {
///  // Desmond parser
///  named!(take5, take!( 5 ) );
///
///  let a = b"abcdefgh";
///
///  assert_eq!(take5(&a[..]), Done(&b"fgh"[..], &b"abcde"[..]));
/// # }
/// ```
#[macro_export]
macro_rules! take(
  ($i:expr, $count:expr) => (
    {
      let cnt = $count as usize;
      if $i.len() < cnt {
        $crate::IResult::Incomplete($crate::Needed::Size(cnt))
      } else {
        $crate::IResult::Done(&$i[cnt..],&$i[0..cnt])
      }
    }
  );
);

/// `take!(nb) => &[T] -> IResult<&[T], &str>`
/// same as take! but returning a &str
#[macro_export]
macro_rules! take_str (
 ( $i:expr, $size:expr ) => ( map_res!($i, take!($size), from_utf8) );
);

/// `take_until_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>`
/// generates a parser consuming bytes until the specified byte sequence is found, and consumes it
#[macro_export]
macro_rules! take_until_and_consume(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      if bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(bytes.len()))
      } else {
        let mut index  = 0;
        let mut parsed = false;

        for idx in 0..$i.len() {
          if idx + bytes.len() > $i.len() {
            index = idx;
            break;
          }
          if &$i[idx..idx + bytes.len()] == bytes {
            parsed = true;
            index = idx;
            break;
          }
        }

        if parsed {
          $crate::IResult::Done(&$i[(index + bytes.len())..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilAndConsume as u32,$i))
        }
      }
    }
  );
);

/// `take_until!(tag) => &[T] -> IResult<&[T], &[T]>`
/// consumes data until it finds the specified tag
#[macro_export]
macro_rules! take_until(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      if bytes.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(bytes.len()))
      } else {
        let mut index  = 0;
        let mut parsed = false;

        for idx in 0..$i.len() {
          if idx + bytes.len() > $i.len() {
            index = idx;
            break;
          }
          if &$i[idx..idx+bytes.len()] == bytes {
            parsed = true;
            index  = idx;
            break;
          }
        }

        if parsed {
          $crate::IResult::Done(&$i[index..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntil as u32,$i))
        }
      }
    }
  );
);

/// `take_until_either_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>`
/// consumes data until it finds any of the specified characters, and consume it
#[macro_export]
macro_rules! take_until_either_and_consume(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      if 1 > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(1))
      } else {
        let mut index  = 0;
        let mut parsed = false;

        for idx in 0..$i.len() {
          if idx + 1 > $i.len() {
            index = idx;
            break;
          }
          for &t in bytes.iter() {
            if $i[idx] == t {
              parsed = true;
              index = idx;
              break;
            }
          }
          if parsed { break; }
        }

        if parsed {
          $crate::IResult::Done(&$i[(index+1)..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilEitherAndConsume as u32,$i))
        }
      }
    }
  );
);

/// `take_until_either!(tag) => &[T] -> IResult<&[T], &[T]>`
#[macro_export]
macro_rules! take_until_either(
  ($i:expr, $inp:expr) => (
    {
      #[inline(always)]
      fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
        b.as_bytes()
      }

      let expected   = $inp;
      let bytes      = as_bytes(&expected);
      if 1 > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size(1))
      } else {
        let mut index  = 0;
        let mut parsed = false;

        for idx in 0..$i.len() {
          if idx + 1 > $i.len() {
            index = idx;
            break;
          }
          for &t in bytes.iter() {
            if $i[idx] == t {
              parsed = true;
              index = idx;
              break;
            }
          }
          if parsed { break; }
        }

        if parsed {
          $crate::IResult::Done(&$i[index..], &$i[0..index])
        } else {
          $crate::IResult::Error($crate::Err::Position($crate::ErrorCode::TakeUntilEither as u32,$i))
        }
      }
    }
  );
);

#[cfg(test)]
mod tests {
  use internal::Needed;
  use internal::IResult::*;
  use internal::Err::*;
  use util::ErrorCode;

  #[test]
  fn is_a() {
    named!(a_or_b, is_a!(&b"ab"[..]));

    let a = &b"abcd"[..];
    assert_eq!(a_or_b(a), Done(&b"cd"[..], &b"ab"[..]));

    let b = &b"bcde"[..];
    assert_eq!(a_or_b(b), Done(&b"cde"[..], &b"b"[..]));

    let c = &b"cdef"[..];
    assert_eq!(a_or_b(c), Error(Position(ErrorCode::IsA as u32,c)));

    let d = &b"bacdef"[..];
    assert_eq!(a_or_b(d), Done(&b"cdef"[..], &b"ba"[..]));
  }

  use std::str::from_utf8;
  #[test]
  fn take_str_test() {
    let a = b"omnomnom";

    assert_eq!(take_str!(&a[..], 5), Done(&b"nom"[..], "omnom"));
    assert_eq!(take_str!(&a[..], 9), Incomplete(Needed::Size(9)));
  }

  #[test]
  fn take_until_test() {
    named!(x, take_until_and_consume!("efgh"));
    let r = x(&b"abcdabcdefghijkl"[..]);
    assert_eq!(r, Done(&b"ijkl"[..], &b"abcdabcd"[..]));

    println!("Done 1\n");

    let r2 = x(&b"abcdabcdefgh"[..]);
    assert_eq!(r2, Done(&b""[..], &b"abcdabcd"[..]));

    println!("Done 2\n");
    let r3 = x(&b"abcefg"[..]);
    assert_eq!(r3,  Error(Position(ErrorCode::TakeUntilAndConsume as u32, &b"abcefg"[..])));

    assert_eq!(
      x(&b"ab"[..]),
      Incomplete(Needed::Size(4))
    );
  }

  #[test]
  fn take_until_either_incomplete() {
    named!(x, take_until_either!("!."));
    assert_eq!(
      x(&b"123"[..]),
      Error(Position(ErrorCode::TakeUntilEither as u32, &b"123"[..]))
    );
  }

  #[test]
  fn take_until_incomplete() {
    named!(y, take_until!("end"));
    assert_eq!(
      y(&b"nd"[..]),
      Incomplete(Needed::Size(3))
    );
    assert_eq!(
      y(&b"123"[..]),
      Error(Position(ErrorCode::TakeUntil as u32, &b"123"[..]))
    );
  }
}
<|endoftext|>