<commit_before>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false);
    ConstValue::Scalar(loc_place.ptr)
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(op).unwrap().1;
            let down = ecx.operand_downcast(op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(down, i).unwrap();
        let val = op_to_const(&ecx, field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, mplace.into())), ty })
}
<commit_msg>Remove needless alloc_slice<commit_after>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false);
    ConstValue::Scalar(loc_place.ptr)
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(op).unwrap().1;
            let down = ecx.operand_downcast(op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(down, i).unwrap();
        let val = op_to_const(&ecx, field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, mplace.into())), ty })
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::str::Chars;

pub struct Position {
    pub line: usize,
    pub column: usize,
}

pub enum Token {
    Empty,
    Invalid,
    Identifier(String),
    Attribute(String),
    Literal(Literal),
    Punctuation(String),
    Linefeed,
    Colon,
    Semicolon,
}

pub enum Literal {
    StringLiteral(String),
    Character(char),
    Integer(String),
    FloatingPoint(String),
    Hex(String),
}

pub struct Lexer<'a> {
    pub token: Token,
    character: Option<char>,
    chars: Chars<'a>,
    previous_line: usize,
    previous_column: usize,
    pub line: usize,
    pub column: usize,
}

impl<'a> Lexer<'a> {
    pub fn new(deck: &'a str) -> Lexer {
        let mut lexer = Lexer {
            chars: deck.chars(),
            token: Token::Empty,
            character: None,
            previous_line: 1,
            previous_column: 0,
            line: 1,
            column: 0,
        };
        lexer.read_character();
        lexer
    }

    fn read_character(&mut self) {
        self.character = self.chars.next();
        match self.character {
            None => (),
            Some(c) => match c {
                '\r' => (),
                '\n' => {
                    self.column = 0;
                    self.line = self.line + 1;
                }
                '\t' => self.column = self.column + 4,
                _ => self.column = self.column + 1,
            },
        }
    }

    pub fn advance(&mut self) {
        self.skip_whitespace();
        self.previous_line = self.line;
        self.previous_column = self.column;
        match self.character {
            None => return,
            Some(c) => {
                if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
                    self.token = self.scan_identifier();
                    return;
                }

                if (c >= '1') && (c <= '9') {
                    let mut value = String::new();
                    value.push(c);
                    self.token = self.scan_numeric_literal(value);
                    return;
                }

                match c {
                    '\n' => {
                        self.read_character();
                        self.token = Token::Linefeed;
                    }
                    ':' => {
                        self.read_character();
                        self.token = Token::Colon;
                    }
                    ';' => {
                        self.read_character();
                        self.token = Token::Semicolon;
                    }
                    '0' => {
                        let mut value = String::new();
                        value.push(c);
                        self.token = self.scan_numeric_or_hex_literal(value);
                    }
                    '@' => {
                        self.read_character();
                        self.token = self.scan_attribute();
                        return;
                        }
                    '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                        let mut operation = String::new();
                        operation.push(c);
                        self.token = self.scan_operator(operation)
                    }
                    '\"' => self.token = { self.scan_string_literal() },
                    '\'' => self.token = { self.scan_character_literal() },
                    '{' | '}' | '(' | ')' | '[' | ']' | '.' | ',' | '?' | '!' | '#'
                    | '$' | '_' | '`' => {
                        let mut punctuation_string = String::new();
                        punctuation_string.push(c);
                        self.read_character();
                        self.token = Token::Punctuation(punctuation_string);
                    }
                    _ => self.token = Token::Invalid,
                }
            }
        }
    }

    pub fn empty(&mut self) {
        self.token = Token::Empty;
    }

    fn scan_identifier(&mut self) -> Token {
        let mut name = String::new();
        loop {
            match self.character {
                None => {
                    if name.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Identifier(name);
                    }
                }

                Some(c) => match c {
                    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
                        name.push(c);
                        self.read_character();
                    }
                    _ => return Token::Identifier(name),
                },
            }
        }
    }

    fn scan_attribute(&mut self) -> Token {
        let mut name = String::new();
        loop {
            match self.character {
                None => {
                    if name.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Attribute(name);
                    }
                }

                Some(c) => match c {
                    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
                        name.push(c);
                        self.read_character();
                    }
                    _ => return Token::Attribute(name),
                },
            }
        }
    }

    fn scan_operator(&mut self, mut operation: String) -> Token {
        loop {
            match self.character {
                None => {
                    if operation.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Identifier(operation);
                    }
                }
                Some(c) => match c {
                    '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                        operation.push(c);
                    }

                    _ => return Token::Identifier(operation),
                },
            }
        }
    }

    fn scan_string_literal(&mut self) -> Token {
        let mut value = String::new();

        loop {
            self.read_character();

            match self.character {
                None => return Token::Invalid,
                Some(c) => match c {
                    '\"' => {
                        self.read_character();
                        return Token::Literal(Literal::StringLiteral(value));
                    }

                    '\\' => {
                        self.read_character();
                        match self.character {
                            None => return Token::Invalid,
                            Some(c) => match c {
                                '\"' | '\\' | '\'' => {
                                    value.push('\\');
                                    value.push(c);
                                }
                                'n' => {
                                    value.push('\\');
                                    value.push('n');
                                }
                                'r' => {
                                    value.push('\\');
                                    value.push('r');
                                }
                                't' => {
                                    value.push('\\');
                                    value.push('t');
                                }
                                '0' => {
                                    value.push('\\');
                                    value.push('0');
                                }
                                _ => return Token::Invalid,
                            },
                        }
                    }

                    _ => {
                        value.push(c);
                    }
                },
            }
        }
    }

    fn scan_character_literal(&mut self) -> Token {
        let value: char;
        self.read_character();

        match self.character {
            None => return Token::Invalid,
            Some(c) => match c {
                '\'' => return Token::Invalid,
                '\\' => {
                    self.read_character();
                    match self.character {
                        None => return Token::Invalid,
                        Some(c) => match c {
                            '\"' | '\\' | '\'' => value = c,
                            'n' => value = '\n',
                            'r' => value = '\r',
                            't' => value = '\t',
                            '0' => value = '\0',
                            _ => return Token::Invalid,
                        },
                    }
                }
                _ => value = c,
            },
        }
        self.read_character();
        match self.character {
            None => Token::Invalid,
            Some(c) => match c {
                '\'' => Token::Literal(Literal::Character(value)),
                _ => Token::Invalid,
            },
        }
    }

    fn scan_numeric_or_hex_literal(&mut self, mut value: String) -> Token {
        self.read_character();
        match self.character {
            None => return Token::Literal(Literal::Integer(value)),
            Some(c) => match c {
                '0'..='9' => {
                    value.push(c);
                    return self.scan_numeric_literal(value);
                }
                '.' => {
                    value.push(c);
                    return self.scan_fraction(value);
                }
                'E' | 'e' => {
                    value.push(c);
                    return self.scan_exponent(value);
                }
                'x' => {
                    return self.scan_hex_literal();
                }
                _ => return Token::Literal(Literal::Integer(value)),
            },
        }
    }

    fn scan_numeric_literal(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::Integer(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    '.' => {
                        value.push(c);
                        return self.scan_fraction(value);
                    }
                    'E' | 'e' => {
                        value.push(c);
                        return self.scan_exponent(value);
                    }
                    _ => return Token::Literal(Literal::Integer(value)),
                },
            }
        }
    }

    fn scan_fraction(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    'E' | 'e' => {
                        value.push(c);
                        return self.scan_exponent(value);
                    }
                    _ => return Token::Literal(Literal::FloatingPoint(value)),
                },
            }
        }
    }

    fn scan_exponent(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    _ => return Token::Literal(Literal::FloatingPoint(value)),
                },
            }
        }
    }

    fn scan_hex_literal(&mut self) -> Token {
        let mut value = String::new();
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' | 'a'..='f' | 'A'..='F' => {
                        value.push(c);
                    }
                    _ => return Token::Literal(Literal::Hex(value)),
                },
            }
        }
    }

    fn skip_whitespace(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    ' ' => {
                        self.read_character();
                        continue;
                    }
                    '\t' => {
                        self.read_character();
                        continue;
                    }
                    '\r' => {
                        self.read_character();
                        continue;
                    }
                    '/' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '/' => {
                                    self.read_character();
                                    self.handle_single_line_comment()
                                }
                                '*' => {
                                    self.read_character();
                                    self.handle_multi_line_comment()
                                }
                                _ => return,
                            },
                        }
                    }

                    _ => {
                        return;
                    }
                },
            }
        }
    }

    fn handle_single_line_comment(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    '\t' => {
                        self.read_character();
                        continue;
                    }
                    '\r' => {
                        continue;
                    }
                    '\n' => {
                        self.read_character();
                        return;
                    }
                    _ => {
                        self.read_character();
                        continue;
                    }
                },
            }
        }
    }

    fn handle_multi_line_comment(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    '/' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '*' => self.handle_multi_line_comment(),
                                _ => return,
                            },
                        }
                    }

                    '*' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '/' => {
                                    self.read_character();
                                    return;
                                }
                                _ => (),
                            },
                        }
                    }

                    '\t' => {
                        self.read_character();
                        continue;
                    }

                    '\r' => {
                        continue;
                    }

                    '\n' => {
                        self.read_character();
                        continue;
                    }

                    _ => {
                        self.read_character();
                        continue;
                    }
                },
            }
        }
    }

    pub fn parse_keyword(&mut self, fixed_string: String) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                let right_keyword = *name == fixed_string;
                if right_keyword {
                    self.empty();
                }
                return right_keyword;
            }
            _ => return false,
        }
    }

    pub fn parse_identifier(&mut self, keywords: &HashSet<String>) -> Option<String> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                if keywords.contains(name) {
                    return None;
                }
                let ret = String::from(name);
                self.empty();
                return Some(ret);
            }
            _ => return None,
        }
    }

    pub fn parse_attribute(&mut self) -> Option<String> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                let ret = String::from(name);
                self.empty();
                return Some(ret);
            }
            _ => return None,
        }
    }

    pub fn parse_punctuation(&mut self, fixed_string: String) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Punctuation(name) => {
                let ret = *name == fixed_string;
                if ret {
                    self.empty();
                }
                return ret;
            }
            _ => return false,
        }
    }

    pub fn parse_literal(&mut self) -> Option<Literal> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Literal(Literal::StringLiteral(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::StringLiteral(ret))
            }
            Token::Literal(Literal::Character(character)) => {
                let ret = *character;
                self.empty();
                Some(Literal::Character(ret))
            }
            Token::Literal(Literal::Integer(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::Integer(ret))
            }
            Token::Literal(Literal::FloatingPoint(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::FloatingPoint(ret))
            }
            Token::Literal(Literal::Hex(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::Hex(ret))
            }
            _ => None,
        }
    }

    pub fn parse_colon(&mut self) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Colon | Token::Linefeed => {
                self.empty();
                true
            }
            _ => false,
        }
    }

    pub fn parse_semicolon(&mut self) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Semicolon | Token::Linefeed => {
                self.empty();
                true
            }
            _ => false,
        }
    }

    pub fn is_at_end(&mut self) -> bool {
        match self.character {
            None => true,
            Some(_) => false,
        }
    }

    pub fn get_position(&self) -> Position {
        Position {
            line: self.line,
            column: self.column,
        }
    }

    pub fn get_previous_position(&self) -> Position {
        Position {
            line: self.previous_line,
            column: self.previous_column,
        }
    }
}
<commit_msg>Fragment<commit_after>use std::collections::HashSet;
use std::str::Chars;

pub struct Position {
    pub line: usize,
    pub column: usize,
}

pub enum Token {
    Empty,
    Invalid,
    Identifier(String),
    Attribute(String),
    Literal(Literal),
    Punctuation(String),
    Linefeed,
    Colon,
    Semicolon,
}

pub enum Literal {
    StringLiteral(String),
    Fragment(String),
    Character(char),
    Integer(String),
    FloatingPoint(String),
    Hex(String),
}

pub struct Lexer<'a> {
    pub token: Token,
    character: Option<char>,
    chars: Chars<'a>,
    previous_line: usize,
    previous_column: usize,
    pub line: usize,
    pub column: usize,
}

impl<'a> Lexer<'a> {
    pub fn new(deck: &'a str) -> Lexer {
        let mut lexer = Lexer {
            chars: deck.chars(),
            token: Token::Empty,
            character: None,
            previous_line: 1,
            previous_column: 0,
            line: 1,
            column: 0,
        };
        lexer.read_character();
        lexer
    }

    fn read_character(&mut self) {
        self.character = self.chars.next();
        match self.character {
            None => (),
            Some(c) => match c {
                '\r' => (),
                '\n' => {
                    self.column = 0;
                    self.line = self.line + 1;
                }
                '\t' => self.column = self.column + 4,
                _ => self.column = self.column + 1,
            },
        }
    }

    pub fn advance(&mut self) {
        self.skip_whitespace();
        self.previous_line = self.line;
        self.previous_column = self.column;
        match self.character {
            None => return,
            Some(c) => {
                if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
                    self.token = self.scan_identifier();
                    return;
                }

                if (c >= '1') && (c <= '9') {
                    let mut value = String::new();
                    value.push(c);
                    self.token = self.scan_numeric_literal(value);
                    return;
                }

                match c {
                    '\n' => {
                        self.read_character();
                        self.token = Token::Linefeed;
                    }
                    ':' => {
                        self.read_character();
                        self.token = Token::Colon;
                    }
                    ';' => {
                        self.read_character();
                        self.token = Token::Semicolon;
                    }
                    '0' => {
                        let mut value = String::new();
                        value.push(c);
                        self.token = self.scan_numeric_or_hex_literal(value);
                    }
                    '@' => {
                        self.read_character();
                        self.token = self.scan_attribute();
                        return;
                        }
                    '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                        let mut operation = String::new();
                        operation.push(c);
                        self.token = self.scan_operator(operation)
                    }
                    '\"' => self.token = { self.scan_string_literal() },
                    '\'' => self.token = { self.scan_character_literal() },
                    '`' => self.token = { self.scan_fragment_literal() },
                    '{' | '}' | '(' | ')' | '[' | ']' | '.' | ',' | '?' | '!' | '#'
                    | '$' | '_' => {
                        let mut punctuation_string = String::new();
                        punctuation_string.push(c);
                        self.read_character();
                        self.token = Token::Punctuation(punctuation_string);
                    }
                    _ => self.token = Token::Invalid,
                }
            }
        }
    }

    pub fn empty(&mut self) {
        self.token = Token::Empty;
    }

    fn scan_identifier(&mut self) -> Token {
        let mut name = String::new();
        loop {
            match self.character {
                None => {
                    if name.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Identifier(name);
                    }
                }

                Some(c) => match c {
                    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
                        name.push(c);
                        self.read_character();
                    }
                    _ => return Token::Identifier(name),
                },
            }
        }
    }

    fn scan_attribute(&mut self) -> Token {
        let mut name = String::new();
        loop {
            match self.character {
                None => {
                    if name.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Attribute(name);
                    }
                }

                Some(c) => match c {
                    'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
                        name.push(c);
                        self.read_character();
                    }
                    _ => return Token::Attribute(name),
                },
            }
        }
    }

    fn scan_operator(&mut self, mut operation: String) -> Token {
        loop {
            match self.character {
                None => {
                    if operation.len() == 0 {
                        return Token::Invalid;
                    } else {
                        return Token::Identifier(operation);
                    }
                }
                Some(c) => match c {
                    '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                        operation.push(c);
                    }

                    _ => return Token::Identifier(operation),
                },
            }
        }
    }

    fn scan_string_literal(&mut self) -> Token {
        let mut value = String::new();

        loop {
            self.read_character();

            match self.character {
                None => return Token::Invalid,
                Some(c) => match c {
                    '\"' => {
                        self.read_character();
                        return Token::Literal(Literal::StringLiteral(value));
                    }

                    '\\' => {
                        self.read_character();
                        match self.character {
                            None => return Token::Invalid,
                            Some(c) => match c {
                                '\"' | '\\' | '\'' => {
                                    value.push('\\');
                                    value.push(c);
                                }
                                'n' => {
                                    value.push('\\');
                                    value.push('n');
                                }
                                'r' => {
                                    value.push('\\');
                                    value.push('r');
                                }
                                't' => {
                                    value.push('\\');
                                    value.push('t');
                                }
                                '0' => {
                                    value.push('\\');
                                    value.push('0');
                                }
                                _ => return Token::Invalid,
                            },
                        }
                    }

                    _ => {
                        value.push(c);
                    }
                },
            }
        }
    }

    fn scan_character_literal(&mut self) -> Token {
        let value: char;
        self.read_character();

        match self.character {
            None => return Token::Invalid,
            Some(c) => match c {
                '\'' => return Token::Invalid,
                '\\' => {
                    self.read_character();
                    match self.character {
                        None => return Token::Invalid,
                        Some(c) => match c {
                            '\"' | '\\' | '\'' => value = c,
                            'n' => value = '\n',
                            'r' => value = '\r',
                            't' => value = '\t',
                            '0' => value = '\0',
                            _ => return Token::Invalid,
                        },
                    }
                }
                _ => value = c,
            },
        }
        self.read_character();
        match self.character {
            None => Token::Invalid,
            Some(c) => match c {
                '\'' => Token::Literal(Literal::Character(value)),
                _ => Token::Invalid,
            },
        }
    }

    fn scan_fragment_literal(&mut self) -> Token {
        let mut value = String::new();

        loop {
            self.read_character();

            match self.character {
                None => return Token::Invalid,
                Some(c) => match c {
                    '`' => {
                        self.read_character();
                        return Token::Literal(Literal::Fragment(value));
                    }

                    '\\' => {
                        self.read_character();
                        match self.character {
                            None => return Token::Invalid,
                            Some(c) => match c {
                                '`' => {
                                    value.push(c);
                                }
                                _ => {
                                    value.push('\\');
                                    value.push(c);
                                }                            },
                        }
                    }

                    _ => {
                        value.push(c);
                    }
                },
            }
        }
    }

    fn scan_numeric_or_hex_literal(&mut self, mut value: String) -> Token {
        self.read_character();
        match self.character {
            None => return Token::Literal(Literal::Integer(value)),
            Some(c) => match c {
                '0'..='9' => {
                    value.push(c);
                    return self.scan_numeric_literal(value);
                }
                '.' => {
                    value.push(c);
                    return self.scan_fraction(value);
                }
                'E' | 'e' => {
                    value.push(c);
                    return self.scan_exponent(value);
                }
                'x' => {
                    return self.scan_hex_literal();
                }
                _ => return Token::Literal(Literal::Integer(value)),
            },
        }
    }

    fn scan_numeric_literal(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::Integer(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    '.' => {
                        value.push(c);
                        return self.scan_fraction(value);
                    }
                    'E' | 'e' => {
                        value.push(c);
                        return self.scan_exponent(value);
                    }
                    _ => return Token::Literal(Literal::Integer(value)),
                },
            }
        }
    }

    fn scan_fraction(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    'E' | 'e' => {
                        value.push(c);
                        return self.scan_exponent(value);
                    }
                    _ => return Token::Literal(Literal::FloatingPoint(value)),
                },
            }
        }
    }

    fn scan_exponent(&mut self, mut value: String) -> Token {
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' => {
                        value.push(c);
                    }
                    _ => return Token::Literal(Literal::FloatingPoint(value)),
                },
            }
        }
    }

    fn scan_hex_literal(&mut self) -> Token {
        let mut value = String::new();
        loop {
            self.read_character();
            match self.character {
                None => {
                    return Token::Literal(Literal::FloatingPoint(value));
                }
                Some(c) => match c {
                    '0'..='9' | 'a'..='f' | 'A'..='F' => {
                        value.push(c);
                    }
                    _ => return Token::Literal(Literal::Hex(value)),
                },
            }
        }
    }

    fn skip_whitespace(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    ' ' => {
                        self.read_character();
                        continue;
                    }
                    '\t' => {
                        self.read_character();
                        continue;
                    }
                    '\r' => {
                        self.read_character();
                        continue;
                    }
                    '/' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '/' => {
                                    self.read_character();
                                    self.handle_single_line_comment()
                                }
                                '*' => {
                                    self.read_character();
                                    self.handle_multi_line_comment()
                                }
                                _ => return,
                            },
                        }
                    }

                    _ => {
                        return;
                    }
                },
            }
        }
    }

    fn handle_single_line_comment(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    '\t' => {
                        self.read_character();
                        continue;
                    }
                    '\r' => {
                        continue;
                    }
                    '\n' => {
                        self.read_character();
                        return;
                    }
                    _ => {
                        self.read_character();
                        continue;
                    }
                },
            }
        }
    }

    fn handle_multi_line_comment(&mut self) {
        loop {
            match self.character {
                None => return,
                Some(c) => match c {
                    '/' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '*' => self.handle_multi_line_comment(),
                                _ => return,
                            },
                        }
                    }

                    '*' => {
                        self.read_character();
                        match self.character {
                            None => return,
                            Some(c) => match c {
                                '/' => {
                                    self.read_character();
                                    return;
                                }
                                _ => (),
                            },
                        }
                    }

                    '\t' => {
                        self.read_character();
                        continue;
                    }

                    '\r' => {
                        continue;
                    }

                    '\n' => {
                        self.read_character();
                        continue;
                    }

                    _ => {
                        self.read_character();
                        continue;
                    }
                },
            }
        }
    }

    pub fn parse_keyword(&mut self, fixed_string: String) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                let right_keyword = *name == fixed_string;
                if right_keyword {
                    self.empty();
                }
                return right_keyword;
            }
            _ => return false,
        }
    }

    pub fn parse_identifier(&mut self, keywords: &HashSet<String>) -> Option<String> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                if keywords.contains(name) {
                    return None;
                }
                let ret = String::from(name);
                self.empty();
                return Some(ret);
            }
            _ => return None,
        }
    }

    pub fn parse_attribute(&mut self) -> Option<String> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Identifier(name) => {
                let ret = String::from(name);
                self.empty();
                return Some(ret);
            }
            _ => return None,
        }
    }

    pub fn parse_punctuation(&mut self, fixed_string: String) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Punctuation(name) => {
                let ret = *name == fixed_string;
                if ret {
                    self.empty();
                }
                return ret;
            }
            _ => return false,
        }
    }

    pub fn parse_literal(&mut self) -> Option<Literal> {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Literal(Literal::StringLiteral(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::StringLiteral(ret))
            }
            Token::Literal(Literal::Character(character)) => {
                let ret = *character;
                self.empty();
                Some(Literal::Character(ret))
            }
            Token::Literal(Literal::Integer(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::Integer(ret))
            }
            Token::Literal(Literal::FloatingPoint(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::FloatingPoint(ret))
            }
            Token::Literal(Literal::Hex(name)) => {
                let ret = String::from(name);
                self.empty();
                Some(Literal::Hex(ret))
            }
            _ => None,
        }
    }

    pub fn parse_colon(&mut self) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Colon | Token::Linefeed => {
                self.empty();
                true
            }
            _ => false,
        }
    }

    pub fn parse_semicolon(&mut self) -> bool {
        match &self.token {
            Token::Empty => self.advance(),
            _ => (),
        }
        match &self.token {
            Token::Semicolon | Token::Linefeed => {
                self.empty();
                true
            }
            _ => false,
        }
    }

    pub fn is_at_end(&mut self) -> bool {
        match self.character {
            None => true,
            Some(_) => false,
        }
    }

    pub fn get_position(&self) -> Position {
        Position {
            line: self.line,
            column: self.column,
        }
    }

    pub fn get_previous_position(&self) -> Position {
        Position {
            line: self.previous_line,
            column: self.previous_column,
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;

use core::num::NumCast;

pub struct Au(i32);

impl Add<Au,Au> for Au {
    fn add(&self, other: &Au) -> Au { Au(**self + **other) }
}

impl Sub<Au,Au> for Au {
    fn sub(&self, other: &Au) -> Au { Au(**self - **other) }
}

impl Mul<Au,Au> for Au {
    fn mul(&self, other: &Au) -> Au { Au(**self * **other) }
}

impl Div<Au,Au> for Au {
    fn div(&self, other: &Au) -> Au { Au(**self / **other) }
}

impl Modulo<Au,Au> for Au {
    fn modulo(&self, other: &Au) -> Au { Au(**self % **other) }
}

impl Neg<Au> for Au {
    fn neg(&self) -> Au { Au(-**self) }
}

impl cmp::Ord for Au {
    fn lt(&self, other: &Au) -> bool { **self <  **other }
    fn le(&self, other: &Au) -> bool { **self <= **other }
    fn ge(&self, other: &Au) -> bool { **self >= **other }
    fn gt(&self, other: &Au) -> bool { **self >  **other }
}

impl cmp::Eq for Au {
    fn eq(&self, other: &Au) -> bool { **self == **other }
    fn ne(&self, other: &Au) -> bool { **self != **other }
}

pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }

impl NumCast for Au {
    fn from<T:NumCast>(n: T) -> Au { Au(n.to_i32()) }

    fn to_u8(&self) -> u8       { (**self).to_u8() }
    fn to_u16(&self) -> u16     { (**self).to_u16() }
    fn to_u32(&self) -> u32     { (**self).to_u32() }
    fn to_u64(&self) -> u64     { (**self).to_u64() }
    fn to_uint(&self) -> uint   { (**self).to_uint() }

    fn to_i8(&self) -> i8       { (**self).to_i8() }
    fn to_i16(&self) -> i16     { (**self).to_i16() }
    fn to_i32(&self) -> i32     { (**self).to_i32() }
    fn to_i64(&self) -> i64     { (**self).to_i64() }
    fn to_int(&self) -> int     { (**self).to_int() }

    fn to_f32(&self) -> f32     { (**self).to_f32() }
    fn to_f64(&self) -> f64     { (**self).to_f64() }
    fn to_float(&self) -> float { (**self).to_float() }
}

pub fn box<T:Copy + Ord + Add<T,T> + Sub<T,T>>(x: T, y: T, w: T, h: T) -> Rect<T> {
    Rect(Point2D(x, y), Size2D(w, h))
}

pub impl Au {
    pub fn scale_by(self, factor: float) -> Au {
        Au(((*self as float) * factor) as i32)
    }

    pub fn from_px(i: int) -> Au {
        NumCast::from(i * 60)
    }

    pub fn to_px(&self) -> int {
        (**self / 60) as int
    }

    pub fn to_snapped(&self) -> Au {
        let res = **self % 60i32;
        return if res >= 30i32 { return Au(**self - res + 60i32) }
                       else { return Au(**self - res) };
    }

    pub fn zero_point() -> Point2D<Au> {
        Point2D(Au(0), Au(0))
    }

    pub fn zero_rect() -> Rect<Au> {
        let z = Au(0);
        Rect(Point2D(z, z), Size2D(z, z))
    }

    // assumes 72 points per inch, and 96 px per inch
    pub fn from_pt(f: float) -> Au {
        from_px((f / 72f * 96f) as int)
    }

    pub fn from_frac_px(f: float) -> Au {
        Au((f * 60f) as i32)
    }

    pub fn min(x: Au, y: Au) -> Au { if *x < *y { x } else { y } }
    pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
}

pub fn zero_rect() -> Rect<Au> {
    let z = Au(0);
    Rect(Point2D(z, z), Size2D(z, z))
}

pub fn zero_point() -> Point2D<Au> {
    Point2D(Au(0), Au(0))
}

pub fn zero_size() -> Size2D<Au> {
    Size2D(Au(0), Au(0))
}

pub fn from_frac_px(f: float) -> Au {
    Au((f * 60f) as i32)
}

pub fn from_px(i: int) -> Au {
    NumCast::from(i * 60)
}

pub fn to_px(au: Au) -> int {
    (*au / 60) as int
}

pub fn to_frac_px(au: Au) -> float {
    (*au as float) / 60f
}

// assumes 72 points per inch, and 96 px per inch
pub fn from_pt(f: float) -> Au {
    from_px((f / 72f * 96f) as int)
}
<commit_msg>Changen Div to Quot and Mod to Rem.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;

use core::num::NumCast;

pub struct Au(i32);

impl Add<Au,Au> for Au {
    fn add(&self, other: &Au) -> Au { Au(**self + **other) }
}

impl Sub<Au,Au> for Au {
    fn sub(&self, other: &Au) -> Au { Au(**self - **other) }
}

impl Mul<Au,Au> for Au {
    fn mul(&self, other: &Au) -> Au { Au(**self * **other) }
}

impl Quot<Au,Au> for Au {
    fn quot(&self, other: &Au) -> Au { Au(**self / **other) }
}

impl Rem<Au,Au> for Au {
    fn rem(&self, other: &Au) -> Au { Au(**self % **other) }
}

impl Neg<Au> for Au {
    fn neg(&self) -> Au { Au(-**self) }
}

impl cmp::Ord for Au {
    fn lt(&self, other: &Au) -> bool { **self <  **other }
    fn le(&self, other: &Au) -> bool { **self <= **other }
    fn ge(&self, other: &Au) -> bool { **self >= **other }
    fn gt(&self, other: &Au) -> bool { **self >  **other }
}

impl cmp::Eq for Au {
    fn eq(&self, other: &Au) -> bool { **self == **other }
    fn ne(&self, other: &Au) -> bool { **self != **other }
}

pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }

impl NumCast for Au {
    fn from<T:NumCast>(n: T) -> Au { Au(n.to_i32()) }

    fn to_u8(&self) -> u8       { (**self).to_u8() }
    fn to_u16(&self) -> u16     { (**self).to_u16() }
    fn to_u32(&self) -> u32     { (**self).to_u32() }
    fn to_u64(&self) -> u64     { (**self).to_u64() }
    fn to_uint(&self) -> uint   { (**self).to_uint() }

    fn to_i8(&self) -> i8       { (**self).to_i8() }
    fn to_i16(&self) -> i16     { (**self).to_i16() }
    fn to_i32(&self) -> i32     { (**self).to_i32() }
    fn to_i64(&self) -> i64     { (**self).to_i64() }
    fn to_int(&self) -> int     { (**self).to_int() }

    fn to_f32(&self) -> f32     { (**self).to_f32() }
    fn to_f64(&self) -> f64     { (**self).to_f64() }
    fn to_float(&self) -> float { (**self).to_float() }
}

pub fn box<T:Copy + Ord + Add<T,T> + Sub<T,T>>(x: T, y: T, w: T, h: T) -> Rect<T> {
    Rect(Point2D(x, y), Size2D(w, h))
}

pub impl Au {
    pub fn scale_by(self, factor: float) -> Au {
        Au(((*self as float) * factor) as i32)
    }

    pub fn from_px(i: int) -> Au {
        NumCast::from(i * 60)
    }

    pub fn to_px(&self) -> int {
        (**self / 60) as int
    }

    pub fn to_snapped(&self) -> Au {
        let res = **self % 60i32;
        return if res >= 30i32 { return Au(**self - res + 60i32) }
                       else { return Au(**self - res) };
    }

    pub fn zero_point() -> Point2D<Au> {
        Point2D(Au(0), Au(0))
    }

    pub fn zero_rect() -> Rect<Au> {
        let z = Au(0);
        Rect(Point2D(z, z), Size2D(z, z))
    }

    // assumes 72 points per inch, and 96 px per inch
    pub fn from_pt(f: float) -> Au {
        from_px((f / 72f * 96f) as int)
    }

    pub fn from_frac_px(f: float) -> Au {
        Au((f * 60f) as i32)
    }

    pub fn min(x: Au, y: Au) -> Au { if *x < *y { x } else { y } }
    pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
}

pub fn zero_rect() -> Rect<Au> {
    let z = Au(0);
    Rect(Point2D(z, z), Size2D(z, z))
}

pub fn zero_point() -> Point2D<Au> {
    Point2D(Au(0), Au(0))
}

pub fn zero_size() -> Size2D<Au> {
    Size2D(Au(0), Au(0))
}

pub fn from_frac_px(f: float) -> Au {
    Au((f * 60f) as i32)
}

pub fn from_px(i: int) -> Au {
    NumCast::from(i * 60)
}

pub fn to_px(au: Au) -> int {
    (*au / 60) as int
}

pub fn to_frac_px(au: Au) -> float {
    (*au as float) / 60f
}

// assumes 72 points per inch, and 96 px per inch
pub fn from_pt(f: float) -> Au {
    from_px((f / 72f * 96f) as int)
}
<|endoftext|>


<commit_before>use config::{Config, ConfigError, File, FileFormat, Source, Value};
use failure::ResultExt;
use serde::Deserialize;
use slog_scope::{info, warn};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::PathBuf;
use structopt::StructOpt;

use crate::osm_reader::poi;
use crate::Error;

#[derive(Debug, Clone, Deserialize)]
pub struct StreetExclusion {
    pub highways: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Street {
    pub import: bool,
    pub exclusion: StreetExclusion,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Admin {
    pub import: bool,
    pub levels: Vec<u32>,
    pub city_level: u32,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Poi {
    pub import: bool,
    pub config: Option<poi::PoiConfig>,
}

#[cfg(feature = "db-storage")]
#[derive(Debug, Clone, Deserialize)]
pub struct Database {
    pub file: PathBuf,
    pub buffer_size: usize,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Elasticsearch {
    pub connection_string: String,
    pub insert_thread_count: usize,
    pub streets_shards: usize,
    pub streets_replicas: usize,
    pub admins_shards: usize,
    pub admins_replicas: usize,
    pub pois_shards: usize,
    pub pois_replicas: usize,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
    pub dataset: String,
    #[cfg(feature = "db-storage")]
    pub database: Option<Database>,
    pub elasticsearch: Elasticsearch,
    pub street: Option<Street>,
    pub poi: Option<Poi>,
    pub admin: Option<Admin>,
}

impl Settings {
    // To create settings, we first retrieve default settings, merge in specific settings if
    // needed, and finally override them with command line arguments.
    pub fn new(args: Args) -> Result<Self, Error> {
        let config_dir = args.config_dir.clone();
        let settings = args.settings.clone();

        let mut config = Config::new();
        // let config_dir = config_dir.clone();
        match config_dir {
            Some(mut dir) => {
                // Start off by merging in the "default" configuration file
                dir.push("osm2mimir-default");

                if let Some(path) = dir.to_str() {
                    info!("using configuration from {}", path);
                    // Now if the file exists, we read it, otherwise, we
                    // read from the compiled version.
                    if dir.exists() {
                        config.merge(File::with_name(path)).with_context(|e| {
                            format!(
                                "Could not merge default configuration from file {}: {}",
                                path, e
                            )
                        })?;
                    } else {
                        config
                            .merge(File::from_str(
                                include_str!("../../config/osm2mimir-default.toml"),
                                FileFormat::Toml,
                            ))
                            .with_context(|e| {
                                format!(
                                    "Could not merge default configuration from file {}: {}",
                                    path, e
                                )
                            })?;
                    }
                } else {
                    return Err(failure::err_msg(format!(
                        "Could not read default settings in '{}'",
                        dir.display()
                    )));
                }

                dir.pop(); // remove the default
                           // If we provided a special configuration, merge it.
                if let Some(settings) = settings {
                    dir.push(&settings);

                    if let Some(path) = dir.to_str() {
                        info!("using configuration from {}", path);
                        config
                            .merge(File::with_name(path).required(true))
                            .with_context(|e| {
                                format!(
                                    "Could not merge {} configuration in file {}: {}",
                                    settings, path, e
                                )
                            })?;
                    } else {
                        return Err(failure::err_msg(format!(
                            "Could not read configuration for '{}'",
                            settings,
                        )));
                    }
                    dir.pop();
                }
            }
            None => {
                if settings.is_some() {
                    // If the user set the 'settings' at the command line, he should
                    // also have used the 'config_dir' option. So we issue a warning,
                    // and leave with an error because the expected configuration can
                    // not be read.
                    warn!("settings option used without the 'config_dir' option. Please set the config directory with --config-dir.");
                    return Err(failure::err_msg(String::from(
                        "Could not build program settings",
                    )));
                }
                config
                    .merge(File::from_str(
                        include_str!("../../config/osm2mimir-default.toml"),
                        FileFormat::Toml,
                    ))
                    .with_context(|e| {
                        format!(
                            "Could not merge default configuration from file at compile time: {}",
                            e
                        )
                    })?;
            }
        }

        // Now override with command line values
        config
            .merge(args)
            .with_context(|e| format!("Could not merge arguments into configuration: {}", e))?;

        // You can deserialize (and thus freeze) the entire configuration as
        config.try_into().map_err(|e| {
            failure::err_msg(format!(
                "Could not generate settings from configuration: {}",
                e
            ))
        })
    }
}

#[derive(StructOpt, Clone, Debug)]
pub struct Args {
    /// OSM PBF file.
    #[structopt(short = "i", long = "input", parse(from_os_str))]
    pub input: PathBuf,
    /// Admin levels to keep.
    #[structopt(short = "l", long = "level")]
    level: Option<Vec<u32>>,
    /// City level to  calculate weight.
    #[structopt(short = "C", long = "city-level")]
    city_level: Option<u32>,
    /// Elasticsearch parameters.
    #[structopt(short = "c", long = "connection-string")]
    connection_string: Option<String>,
    /// Import ways.
    #[structopt(short = "w", long = "import-way")]
    import_way: bool,
    /// Import admins.
    #[structopt(short = "a", long = "import-admin")]
    import_admin: bool,
    /// Import POIs.
    #[structopt(short = "p", long = "import-poi")]
    import_poi: bool,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset")]
    pub dataset: Option<String>,
    /// Number of shards for the admin es index
    #[structopt(long = "nb-admin-shards")]
    nb_admin_shards: Option<usize>,
    /// Number of replicas for the es index
    #[structopt(long = "nb-admin-replicas")]
    nb_admin_replicas: Option<usize>,
    /// Number of shards for the street es index
    #[structopt(long = "nb-street-shards")]
    nb_street_shards: Option<usize>,
    /// Number of replicas for the street es index
    #[structopt(long = "nb-street-replicas")]
    nb_street_replicas: Option<usize>,
    /// Number of shards for the es index
    #[structopt(long = "nb-poi-shards")]
    nb_poi_shards: Option<usize>,
    /// Number of replicas for the es index
    #[structopt(long = "nb-poi-replicas")]
    nb_poi_replicas: Option<usize>,
    /// If you use this option by providing a filename, then we
    /// will use a SQlite database that will be persisted. You
    /// can only do that if osm2mimir was compiled with the
    /// 'db-storage' feature. If you don't provide a value, then
    /// we will use in memory storage.
    #[cfg(feature = "db-storage")]
    #[structopt(long = "db-file", parse(from_os_str))]
    pub db_file: Option<PathBuf>,

    /// DB buffer size.
    #[cfg(feature = "db-storage")]
    #[structopt(long = "db-buffer-size")]
    pub db_buffer_size: Option<usize>,
    /// Number of threads to use to insert into Elasticsearch. Note that Elasticsearch is not able
    /// to handle values that are too high.
    #[structopt(short = "T", long = "nb-insert-threads")]
    nb_insert_threads: Option<usize>,

    /// Path to the config directory
    /// osm2mimir will read the default configuration in there, and maybe
    /// more depending on the settings option.
    /// If no option is given, we'll just read the ./config/osm2mimir-default.toml
    /// at compile time.
    #[structopt(short = "D", long = "config-dir")]
    config_dir: Option<PathBuf>,

    /// Specific configuration, on top of the default ones.
    /// You should provide the basename of the file, eg acme, so that
    /// osm2mimir will use {config-dir}/acme.toml. (Requires config_dir to
    /// be set)
    #[structopt(short = "s", long = "settings")]
    settings: Option<String>,
}

impl Source for Args {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>, ConfigError> {
        let mut m = HashMap::new();

        // DATASET
        if let Some(dataset) = self.dataset.clone() {
            m.insert(String::from("dataset"), Value::new(None, dataset));
        }

        // ADMIN
        m.insert(
            String::from("admin.import"),
            Value::new(None, self.import_admin),
        );
        if let Some(city_level) = self.city_level {
            m.insert(
                String::from("admin.city_level"),
                Value::new(
                    None,
                    i64::try_from(city_level).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert admin city_level to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }
        if let Some(level) = self.level.clone() {
            m.insert(
                String::from("admin.levels"),
                Value::new(
                    None,
                    level.into_iter().try_fold(Vec::new(), |mut acc, l| {
                        let i = i64::try_from(l).map_err(|e| {
                            ConfigError::Message(format!(
                                "Could not convert admin city_level to integer: {}",
                                e
                            ))
                        })?;
                        acc.push(i);
                        Ok(acc)
                    })?,
                ),
            );
        }

        // WAY
        m.insert(
            String::from("street.import"),
            Value::new(None, self.import_way),
        );

        // POI
        m.insert(
            String::from("poi.import"),
            Value::new(None, self.import_poi),
        );

        // ELASTICSEARCH SETTINGS

        if let Some(connection_string) = self.connection_string.clone() {
            m.insert(
                String::from("elasticsearch.connection_string"),
                Value::new(None, connection_string),
            );
        }

        if let Some(nb_way_shards) = self.nb_street_shards {
            m.insert(
                String::from("elasticsearch.streets_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_way_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of streets shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_way_replicas) = self.nb_street_replicas {
            m.insert(
                String::from("elasticsearch.streets_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_way_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of way replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_poi_shards) = self.nb_poi_shards {
            m.insert(
                String::from("elasticsearch.pois_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_poi_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of poi shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_poi_replicas) = self.nb_poi_replicas {
            m.insert(
                String::from("elasticsearch.pois_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_poi_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of poi replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_admin_shards) = self.nb_admin_shards {
            m.insert(
                String::from("elasticsearch.admins_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_admin_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of admin shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_admin_replicas) = self.nb_admin_replicas {
            m.insert(
                String::from("elasticsearch.admins_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_admin_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of admin replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_insert_threads) = self.nb_insert_threads {
            m.insert(
                String::from("elasticsearch.insert_thread_count"),
                Value::new(
                    None,
                    i64::try_from(nb_insert_threads).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert elasticsearch insert thread count to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        // DATABASE
        #[cfg(feature = "db-storage")]
        if let Some(db_file) = self.db_file.clone() {
            m.insert(
                String::from("database.file"),
                Value::new(None, db_file.to_str().expect("valid utf-8 filename")),
            );
        }

        #[cfg(feature = "db-storage")]
        if let Some(db_buffer_size) = self.db_buffer_size {
            m.insert(
                String::from("database.buffer_size"),
                Value::new(
                    None,
                    i64::try_from(db_buffer_size).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert database buffer size to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        Ok(m)
    }
}
<commit_msg>[Changed] Add negative flags<commit_after>use config::{Config, ConfigError, File, FileFormat, Source, Value};
use failure::ResultExt;
use serde::Deserialize;
use slog_scope::{info, warn};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::PathBuf;
use structopt::StructOpt;

use crate::osm_reader::poi;
use crate::Error;

#[derive(Debug, Clone, Deserialize)]
pub struct StreetExclusion {
    pub highways: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Street {
    pub import: bool,
    pub exclusion: StreetExclusion,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Admin {
    pub import: bool,
    pub levels: Vec<u32>,
    pub city_level: u32,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Poi {
    pub import: bool,
    pub config: Option<poi::PoiConfig>,
}

#[cfg(feature = "db-storage")]
#[derive(Debug, Clone, Deserialize)]
pub struct Database {
    pub file: PathBuf,
    pub buffer_size: usize,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Elasticsearch {
    pub connection_string: String,
    pub insert_thread_count: usize,
    pub streets_shards: usize,
    pub streets_replicas: usize,
    pub admins_shards: usize,
    pub admins_replicas: usize,
    pub pois_shards: usize,
    pub pois_replicas: usize,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
    pub dataset: String,
    #[cfg(feature = "db-storage")]
    pub database: Option<Database>,
    pub elasticsearch: Elasticsearch,
    pub street: Option<Street>,
    pub poi: Option<Poi>,
    pub admin: Option<Admin>,
}

impl Settings {
    // To create settings, we first retrieve default settings, merge in specific settings if
    // needed, and finally override them with command line arguments.
    pub fn new(args: Args) -> Result<Self, Error> {
        let config_dir = args.config_dir.clone();
        let settings = args.settings.clone();

        let mut config = Config::new();
        // let config_dir = config_dir.clone();
        match config_dir {
            Some(mut dir) => {
                // Start off by merging in the "default" configuration file
                dir.push("osm2mimir-default");

                if let Some(path) = dir.to_str() {
                    info!("using configuration from {}", path);
                    // Now if the file exists, we read it, otherwise, we
                    // read from the compiled version.
                    if dir.exists() {
                        config.merge(File::with_name(path)).with_context(|e| {
                            format!(
                                "Could not merge default configuration from file {}: {}",
                                path, e
                            )
                        })?;
                    } else {
                        config
                            .merge(File::from_str(
                                include_str!("../../config/osm2mimir-default.toml"),
                                FileFormat::Toml,
                            ))
                            .with_context(|e| {
                                format!(
                                    "Could not merge default configuration from file {}: {}",
                                    path, e
                                )
                            })?;
                    }
                } else {
                    return Err(failure::err_msg(format!(
                        "Could not read default settings in '{}'",
                        dir.display()
                    )));
                }

                dir.pop(); // remove the default
                           // If we provided a special configuration, merge it.
                if let Some(settings) = settings {
                    dir.push(&settings);

                    if let Some(path) = dir.to_str() {
                        info!("using configuration from {}", path);
                        config
                            .merge(File::with_name(path).required(true))
                            .with_context(|e| {
                                format!(
                                    "Could not merge {} configuration in file {}: {}",
                                    settings, path, e
                                )
                            })?;
                    } else {
                        return Err(failure::err_msg(format!(
                            "Could not read configuration for '{}'",
                            settings,
                        )));
                    }
                    dir.pop();
                }
            }
            None => {
                if settings.is_some() {
                    // If the user set the 'settings' at the command line, he should
                    // also have used the 'config_dir' option. So we issue a warning,
                    // and leave with an error because the expected configuration can
                    // not be read.
                    warn!("settings option used without the 'config_dir' option. Please set the config directory with --config-dir.");
                    return Err(failure::err_msg(String::from(
                        "Could not build program settings",
                    )));
                }
                config
                    .merge(File::from_str(
                        include_str!("../../config/osm2mimir-default.toml"),
                        FileFormat::Toml,
                    ))
                    .with_context(|e| {
                        format!(
                            "Could not merge default configuration from file at compile time: {}",
                            e
                        )
                    })?;
            }
        }

        // Now override with command line values
        config
            .merge(args)
            .with_context(|e| format!("Could not merge arguments into configuration: {}", e))?;

        // You can deserialize (and thus freeze) the entire configuration as
        config.try_into().map_err(|e| {
            failure::err_msg(format!(
                "Could not generate settings from configuration: {}",
                e
            ))
        })
    }
}

#[derive(StructOpt, Clone, Debug)]
pub struct Args {
    /// OSM PBF file.
    #[structopt(short = "i", long = "input", parse(from_os_str))]
    pub input: PathBuf,
    /// Admin levels to keep.
    #[structopt(short = "l", long = "level")]
    level: Option<Vec<u32>>,
    /// City level to  calculate weight.
    #[structopt(short = "C", long = "city-level")]
    city_level: Option<u32>,
    /// Elasticsearch parameters.
    #[structopt(short = "c", long = "connection-string")]
    connection_string: Option<String>,
    /// Import ways.
    #[structopt(short = "w", long = "import-way")]
    import_way: Option<bool>,
    /// Don't import ways.
    #[structopt(long = "no-import-way")]
    no_import_way: Option<bool>,
    /// Import admins.
    #[structopt(short = "a", long = "import-admin")]
    import_admin: Option<bool>,
    /// Don't import admins.
    #[structopt(long = "no-import-admin")]
    no_import_admin: Option<bool>,
    /// Import POIs.
    #[structopt(short = "p", long = "import-poi")]
    import_poi: Option<bool>,
    /// Don't import POIs.
    #[structopt(long = "--no-import-poi")]
    no_import_poi: Option<bool>,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset")]
    pub dataset: Option<String>,
    /// Number of shards for the admin es index
    #[structopt(long = "nb-admin-shards")]
    nb_admin_shards: Option<usize>,
    /// Number of replicas for the es index
    #[structopt(long = "nb-admin-replicas")]
    nb_admin_replicas: Option<usize>,
    /// Number of shards for the street es index
    #[structopt(long = "nb-street-shards")]
    nb_street_shards: Option<usize>,
    /// Number of replicas for the street es index
    #[structopt(long = "nb-street-replicas")]
    nb_street_replicas: Option<usize>,
    /// Number of shards for the es index
    #[structopt(long = "nb-poi-shards")]
    nb_poi_shards: Option<usize>,
    /// Number of replicas for the es index
    #[structopt(long = "nb-poi-replicas")]
    nb_poi_replicas: Option<usize>,
    /// If you use this option by providing a filename, then we
    /// will use a SQlite database that will be persisted. You
    /// can only do that if osm2mimir was compiled with the
    /// 'db-storage' feature. If you don't provide a value, then
    /// we will use in memory storage.
    #[cfg(feature = "db-storage")]
    #[structopt(long = "db-file", parse(from_os_str))]
    pub db_file: Option<PathBuf>,

    /// DB buffer size.
    #[cfg(feature = "db-storage")]
    #[structopt(long = "db-buffer-size")]
    pub db_buffer_size: Option<usize>,
    /// Number of threads to use to insert into Elasticsearch. Note that Elasticsearch is not able
    /// to handle values that are too high.
    #[structopt(short = "T", long = "nb-insert-threads")]
    nb_insert_threads: Option<usize>,

    /// Path to the config directory
    /// osm2mimir will read the default configuration in there, and maybe
    /// more depending on the settings option.
    /// If no option is given, we'll just read the ./config/osm2mimir-default.toml
    /// at compile time.
    #[structopt(short = "D", long = "config-dir")]
    config_dir: Option<PathBuf>,

    /// Specific configuration, on top of the default ones.
    /// You should provide the basename of the file, eg acme, so that
    /// osm2mimir will use {config-dir}/acme.toml. (Requires config_dir to
    /// be set)
    #[structopt(short = "s", long = "settings")]
    settings: Option<String>,
}

impl Source for Args {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>, ConfigError> {
        let mut m = HashMap::new();

        // DATASET
        if let Some(dataset) = self.dataset.clone() {
            m.insert(String::from("dataset"), Value::new(None, dataset));
        }

        // ADMIN
        if self.import_admin.is_some() {
            m.insert(String::from("admin.import"), Value::new(None, true));
        } else if self.no_import_admin.is_some() {
            m.insert(String::from("admin.import"), Value::new(None, false));
        }

        if let Some(city_level) = self.city_level {
            m.insert(
                String::from("admin.city_level"),
                Value::new(
                    None,
                    i64::try_from(city_level).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert admin city_level to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }
        if let Some(level) = self.level.clone() {
            m.insert(
                String::from("admin.levels"),
                Value::new(
                    None,
                    level.into_iter().try_fold(Vec::new(), |mut acc, l| {
                        let i = i64::try_from(l).map_err(|e| {
                            ConfigError::Message(format!(
                                "Could not convert admin city_level to integer: {}",
                                e
                            ))
                        })?;
                        acc.push(i);
                        Ok(acc)
                    })?,
                ),
            );
        }

        // WAY
        if self.import_way.is_some() {
            m.insert(String::from("street.import"), Value::new(None, true));
        } else if self.no_import_way.is_some() {
            m.insert(String::from("street.import"), Value::new(None, false));
        }

        // POI
        if self.import_poi.is_some() {
            m.insert(String::from("poi.import"), Value::new(None, true));
        } else if self.no_import_poi.is_some() {
            m.insert(String::from("poi.import"), Value::new(None, false));
        }

        // ELASTICSEARCH SETTINGS

        if let Some(connection_string) = self.connection_string.clone() {
            m.insert(
                String::from("elasticsearch.connection_string"),
                Value::new(None, connection_string),
            );
        }

        if let Some(nb_way_shards) = self.nb_street_shards {
            m.insert(
                String::from("elasticsearch.streets_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_way_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of streets shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_way_replicas) = self.nb_street_replicas {
            m.insert(
                String::from("elasticsearch.streets_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_way_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of way replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_poi_shards) = self.nb_poi_shards {
            m.insert(
                String::from("elasticsearch.pois_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_poi_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of poi shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_poi_replicas) = self.nb_poi_replicas {
            m.insert(
                String::from("elasticsearch.pois_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_poi_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of poi replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_admin_shards) = self.nb_admin_shards {
            m.insert(
                String::from("elasticsearch.admins_shards"),
                Value::new(
                    None,
                    i64::try_from(nb_admin_shards).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of admin shards to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_admin_replicas) = self.nb_admin_replicas {
            m.insert(
                String::from("elasticsearch.admins_replicas"),
                Value::new(
                    None,
                    i64::try_from(nb_admin_replicas).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert count of admin replicas to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        if let Some(nb_insert_threads) = self.nb_insert_threads {
            m.insert(
                String::from("elasticsearch.insert_thread_count"),
                Value::new(
                    None,
                    i64::try_from(nb_insert_threads).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert elasticsearch insert thread count to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        // DATABASE
        #[cfg(feature = "db-storage")]
        if let Some(db_file) = self.db_file.clone() {
            m.insert(
                String::from("database.file"),
                Value::new(None, db_file.to_str().expect("valid utf-8 filename")),
            );
        }

        #[cfg(feature = "db-storage")]
        if let Some(db_buffer_size) = self.db_buffer_size {
            m.insert(
                String::from("database.buffer_size"),
                Value::new(
                    None,
                    i64::try_from(db_buffer_size).map_err(|e| {
                        ConfigError::Message(format!(
                            "Could not convert database buffer size to integer: {}",
                            e
                        ))
                    })?,
                ),
            );
        }

        Ok(m)
    }
}
<|endoftext|>


<commit_before>//use nom::types::CompleteStr;
use regex::{Regex, RegexBuilder, escape};
use crate::types::{State, Group, Tab};

/*named!(atom<CompleteStr, Parsed>,
    do_parse!(
        many0!(char!(' ')) >>
        // TODO better literal test
        literal: is_not!(" ") >>
        (Parsed::Literal(RegExp::new(&RegExp::escape(&literal), "i")))
    )
);

named!(parse<CompleteStr, Parsed>,
    do_parse!(
        init: atom >>
        res: fold_many0!(
            tuple!(
                many1!(char!(' ')),
                atom
            ),
            init,
            |acc, v: (_, _)| {
                Parsed::And(Box::new(acc), Box::new(v.1))
            }
        ) >>
        (res)
    )
);*/

impl Tab {
    pub(crate) fn set_matches_search(&self, matches: bool) {
        self.matches_search.set_neq(matches);
    }
}

impl State {
    pub(crate) fn search_tab(&self, tab: &Tab) {
        let tab_matches = {
            let search_parser = self.search_parser.lock_ref();
            search_parser.matches_tab(tab)
        };

        tab.set_matches_search(tab_matches);
    }
}

#[derive(Debug)]
pub(crate) enum Parsed {
    True,
    Literal(Regex),
    And(Box<Parsed>, Box<Parsed>),
}

impl Parsed {
    pub(crate) fn new(input: &str) -> Self {
        // TODO a bit hacky
        //parse(CompleteStr(input)).unwrap().1

        input.split(" ")
            .filter(|x| *x != "")
            .map(|x| {
                Parsed::Literal(RegexBuilder::new(&escape(x))
                    .case_insensitive(true)
                    .unicode(false)
                    .build()
                    .unwrap())
            })
            .fold(Parsed::True, |old, new| {
                if let Parsed::True = old {
                    new

                } else {
                    Parsed::And(Box::new(old), Box::new(new))
                }
            })
    }

    pub(crate) fn matches(&self, input: &str) -> bool {
        match self {
            Parsed::True => true,
            Parsed::Literal(regexp) => regexp.is_match(input),
            Parsed::And(left, right) => left.matches(input) && right.matches(input),
        }
    }

    pub(crate) fn matches_tab(&self, tab: &Tab) -> bool {
        let title = tab.title.lock_ref();
        let url = tab.url.lock_ref();

        // TODO make this more efficient ?
        let title = title.as_ref().map(|x| x.as_str()).unwrap_or("");
        let url = url.as_ref().map(|x| x.as_str()).unwrap_or("");

        self.matches(title) || self.matches(url)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn parse() {
        let parsed = super::Parsed::new("foo.\\d\\D\\w\\W\\s\\S\\t\\r\\n\\v\\f[\\b]\\0\\cM\\x00\\u0000\\u{0000}\\u{00000}\\\\[xyz][a-c][^xyz][^a-c]x|y^$\\b\\B(x)\\1(?:x)x*x+x?x{5}x{5,}x{5,6}x*?x+?x??x{5}?x{5,}?x{5,6}?x(?=y)x(?!y)bar");

        //assert!(parsed.pattern.len() == 1);
        assert!(parsed.matches("foo.\\d\\D\\w\\W\\s\\S\\t\\r\\n\\v\\f[\\b]\\0\\cM\\x00\\u0000\\u{0000}\\u{00000}\\\\[xyz][a-c][^xyz][^a-c]x|y^$\\b\\B(x)\\1(?:x)x*x+x?x{5}x{5,}x{5,6}x*?x+?x??x{5}?x{5,}?x{5,6}?x(?=y)x(?!y)bar"));
    }

    #[test]
    fn and() {
        let parsed = super::Parsed::new("   foo   bar     ");

        //assert!(parsed.pattern.len() == 2);
        assert!(parsed.matches("foo"));
        assert!(parsed.matches("bar"));
        assert!(parsed.matches("Foo"));
        assert!(parsed.matches("bAR"));
    }
}
<commit_msg>Adding in is:loaded search term<commit_after>//use nom::types::CompleteStr;
use regex::{Regex, RegexBuilder, escape};
use crate::types::{State, Group, Tab};

/*named!(atom<CompleteStr, Parsed>,
    do_parse!(
        many0!(char!(' ')) >>
        // TODO better literal test
        literal: is_not!(" ") >>
        (Parsed::Literal(RegExp::new(&RegExp::escape(&literal), "i")))
    )
);

named!(parse<CompleteStr, Parsed>,
    do_parse!(
        init: atom >>
        res: fold_many0!(
            tuple!(
                many1!(char!(' ')),
                atom
            ),
            init,
            |acc, v: (_, _)| {
                Parsed::And(Box::new(acc), Box::new(v.1))
            }
        ) >>
        (res)
    )
);*/

impl Tab {
    pub(crate) fn set_matches_search(&self, matches: bool) {
        self.matches_search.set_neq(matches);
    }
}

impl State {
    pub(crate) fn search_tab(&self, tab: &Tab) {
        let tab_matches = {
            let search_parser = self.search_parser.lock_ref();
            search_parser.matches_tab(tab)
        };

        tab.set_matches_search(tab_matches);
    }
}

#[derive(Debug)]
pub(crate) enum Parsed {
    True,
    Literal(Regex),
    And(Box<Parsed>, Box<Parsed>),
    IsLoaded,
}

impl Parsed {
    // TODO proper parser
    pub(crate) fn new(input: &str) -> Self {
        // TODO a bit hacky
        //parse(CompleteStr(input)).unwrap().1

        input.split(" ")
            .filter(|x| *x != "")
            .map(|x| {
                // TODO make this faster
                match x.splitn(2, ":").collect::<Vec<_>>().as_slice() {
                    [x] => {
                        Parsed::Literal(RegexBuilder::new(&escape(x))
                            .case_insensitive(true)
                            .unicode(false)
                            .build()
                            .unwrap())
                    },
                    [x, y] => {
                        if *x == "is" && *y == "loaded" {
                            Parsed::IsLoaded

                        } else {
                            // TODO error on invalid input
                            Parsed::True
                        }
                    },
                    _ => unreachable!(),
                }
            })
            .fold(Parsed::True, |old, new| {
                if let Parsed::True = old {
                    new

                } else {
                    Parsed::And(Box::new(old), Box::new(new))
                }
            })
    }

    pub(crate) fn matches_tab(&self, tab: &Tab) -> bool {
        match self {
            Parsed::True => true,

            Parsed::Literal(regexp) => {
                let title = tab.title.lock_ref();
                let url = tab.url.lock_ref();

                // TODO make this more efficient ?
                let title = title.as_ref().map(|x| x.as_str()).unwrap_or("");
                let url = url.as_ref().map(|x| x.as_str()).unwrap_or("");

                regexp.is_match(title) || regexp.is_match(url)
            },

            Parsed::And(left, right) => left.matches_tab(tab) && right.matches_tab(tab),

            Parsed::IsLoaded => !tab.state.status.get().is_unloaded(),
        }
    }
}

/*#[cfg(test)]
mod tests {
    #[test]
    fn parse() {
        let parsed = super::Parsed::new("foo.\\d\\D\\w\\W\\s\\S\\t\\r\\n\\v\\f[\\b]\\0\\cM\\x00\\u0000\\u{0000}\\u{00000}\\\\[xyz][a-c][^xyz][^a-c]x|y^$\\b\\B(x)\\1(?:x)x*x+x?x{5}x{5,}x{5,6}x*?x+?x??x{5}?x{5,}?x{5,6}?x(?=y)x(?!y)bar");

        //assert!(parsed.pattern.len() == 1);
        assert!(parsed.matches("foo.\\d\\D\\w\\W\\s\\S\\t\\r\\n\\v\\f[\\b]\\0\\cM\\x00\\u0000\\u{0000}\\u{00000}\\\\[xyz][a-c][^xyz][^a-c]x|y^$\\b\\B(x)\\1(?:x)x*x+x?x{5}x{5,}x{5,6}x*?x+?x??x{5}?x{5,}?x{5,6}?x(?=y)x(?!y)bar"));
    }

    #[test]
    fn and() {
        let parsed = super::Parsed::new("   foo   bar     ");

        //assert!(parsed.pattern.len() == 2);
        assert!(parsed.matches("foo"));
        assert!(parsed.matches("bar"));
        assert!(parsed.matches("Foo"));
        assert!(parsed.matches("bAR"));
    }
}*/
<|endoftext|>