1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::data::ast::Interval;
use crate::data::csml_bot::CsmlBot;
use crate::data::position::Position;
use crate::error_format::gen_error_info;
use crate::error_format::ErrorInfo;
use crate::linter::data::Linter;

use lazy_static::*;

////////////////////////////////////////////////////////////////////////////////
// DATA STRUCTURES
////////////////////////////////////////////////////////////////////////////////

type CsmlRules = fn(bot: &CsmlBot, linter: &Linter, error: &mut Vec<ErrorInfo>);

lazy_static! {
    static ref FUNCTIONS: Vec<CsmlRules> = {
        let mut vector: Vec<CsmlRules> = Vec::new();

        vector.push(check_missing_flow);
        vector.push(check_valid_flow);
        vector.push(check_duplicate_step);
        vector.push(check_valid_goto);

        vector
    };
}

////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

fn check_missing_flow(_bot: &CsmlBot, linter: &Linter, error: &mut Vec<ErrorInfo>) {
    if linter.flow.is_empty() {
        error.push(gen_error_info(
            Position::new(Interval::new_as_u32(0, 0)),
            "LINTER: Need to have at least one Flow".to_owned(),
        ));
    }
}

fn check_valid_flow(_bot: &CsmlBot, linter: &Linter, error: &mut Vec<ErrorInfo>) {
    for flow in linter.flow.keys() {
        Position::set_flow(&flow);
        let mut result = false;

        if let Some(hashmap) = linter.flow.get(flow) {
            for step in hashmap.keys() {
                if step == "start" {
                    result = true;
                }
            }
            Position::set_step("");

            if !result {
                error.push(gen_error_info(
                    Position::new(Interval::new_as_u32(0, 0)),
                    format!("LINTER: Flow '{}' need to have a 'start' step", flow),
                ));
            }
        }
    }
}

fn check_duplicate_step(_bot: &CsmlBot, linter: &Linter, error: &mut Vec<ErrorInfo>) {
    for flow in linter.flow.keys() {
        Position::set_flow(&flow);
        if let Some(hashmap_step) = linter.flow.get(flow) {
            for step in hashmap_step.keys() {
                Position::set_step(&step);
                if let Some(vector_step) = hashmap_step.get(step) {
                    if vector_step.len() > 1 {
                        error.push(gen_error_info(
                            Position::new(*vector_step.last().unwrap()),
                            format!("LINTER: Duplicate step '{}' in flow '{}'", step, flow),
                        ));
                    }
                }
            }
        }
    }
}

fn check_valid_goto(_bot: &CsmlBot, linter: &Linter, error: &mut Vec<ErrorInfo>) {
    for goto in linter.goto.iter() {
        Position::set_flow(&goto.src_flow);
        Position::set_step(&goto.src_step);

        match linter.flow.get(&goto.dst_flow) {
            Some(step) => {
                if !step.contains_key(&goto.dst_step) && goto.dst_step != "end" {
                    error.push(gen_error_info(
                        Position::new(goto.interval),
                        format!("LINTER: Step '{}' doesn't exist", goto.dst_step),
                    ));
                }
            }
            None => {
                error.push(gen_error_info(
                    Position::new(goto.interval),
                    format!("LINTER: Flow '{}' doesn't exist", goto.dst_flow),
                ));
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

pub fn lint_flow(bot: &CsmlBot, mut error: &mut Vec<ErrorInfo>) {
    let linter = Linter::get_linter();

    for f in FUNCTIONS.iter() {
        f(bot, &linter, &mut error);
    }
}