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
use duckscript::types::instruction::{Instruction, InstructionType};
use std::cmp::min;

#[cfg(test)]
#[path = "./instruction_query_test.rs"]
mod instruction_query_test;

#[derive(Debug, Clone)]
pub(crate) struct Positions {
    pub(crate) middle: Vec<usize>,
    pub(crate) end: usize,
}

fn get_start(start: Option<usize>) -> usize {
    match start {
        Some(value) => value,
        None => 0,
    }
}

fn get_end(end: Option<usize>, instructions: &Vec<Instruction>) -> usize {
    match end {
        Some(value) => min(instructions.len(), value),
        None => instructions.len(),
    }
}

pub(crate) fn find_commands(
    instructions: &Vec<Instruction>,
    start_names: &Vec<String>,
    middle_names: &Vec<String>,
    end_names: &Vec<String>,
    start: Option<usize>,
    end: Option<usize>,
    allow_recursive: bool,
    start_blocks: &Vec<String>,
    end_blocks: &Vec<String>,
) -> Result<Option<Positions>, String> {
    if start_names.is_empty() || end_names.is_empty() {
        Err("No command names/aliases provided for search.".to_string())
    } else {
        let start_index = get_start(start);
        let end_index = get_end(end, instructions);

        let mut positions = Positions {
            middle: vec![],
            end: 0,
        };
        let mut skip_to = start_index;
        let mut block_delta = 0;
        for line in start_index..end_index {
            if line >= skip_to {
                let instruction = &instructions[line];

                match instruction.instruction_type {
                    InstructionType::Script(ref script_instruction) => {
                        match script_instruction.command {
                            Some(ref command) => {
                                if start_blocks.contains(command) {
                                    block_delta = block_delta + 1;
                                } else if middle_names.contains(command) {
                                    positions.middle.push(line);
                                } else if end_blocks.contains(command) && block_delta > 0 {
                                    block_delta = block_delta - 1;
                                } else if end_names.contains(command) {
                                    positions.end = line;
                                    return Ok(Some(positions));
                                } else if start_names.contains(command) {
                                    if allow_recursive {
                                        match find_commands(
                                            instructions,
                                            start_names,
                                            middle_names,
                                            end_names,
                                            Some(line + 1),
                                            Some(end_index),
                                            allow_recursive,
                                            start_blocks,
                                            end_blocks,
                                        ) {
                                            Ok(positions_options) => match positions_options {
                                                Some(sub_positions) => {
                                                    skip_to = sub_positions.end + 1;
                                                    ()
                                                }
                                                None => return Err(format!("Unsupported nested structure: {} found but end of structure not found.",command).to_string()),
                                            },
                                            Err(error) => return Err(error),
                                        };
                                    } else {
                                        return Err(format!(
                                            "Unsupported nested structure: {} found.",
                                            command
                                        )
                                        .to_string());
                                    }
                                }

                                ()
                            }
                            None => (),
                        }
                    }
                    _ => (),
                }
            }
        }

        Err(format!(
            "Missing end of structure for start names: {:?} start: {} end: {}",
            &start_names, start_index, end_index
        )
        .to_string())
    }
}