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
use serde::{Deserialize, Serialize};
use tree_sitter::Node;

use crate::{
    AbstractTree, Assignment, Async, Error, Expression, Filter, Find, For, IfElse, Insert, Match,
    Remove, Result, Select, Transform, Value, VariableMap, While,
};

/// Abstract representation of a statement.
///
/// A statement may evaluate to an Empty value when run. If a Statement is an
/// Expression, it will always return a non-empty value when run.
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Statement {
    Assignment(Box<Assignment>),
    Expression(Expression),
    IfElse(Box<IfElse>),
    Match(Match),
    While(Box<While>),
    Async(Box<Async>),
    For(Box<For>),
    Transform(Box<Transform>),
    Filter(Box<Filter>),
    Find(Box<Find>),
    Remove(Box<Remove>),
    Select(Box<Select>),
    Insert(Box<Insert>),
}

impl AbstractTree for Statement {
    fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
        debug_assert_eq!("statement", node.kind());

        let child = node.child(0).unwrap();

        match child.kind() {
            "assignment" => Ok(Statement::Assignment(Box::new(
                Assignment::from_syntax_node(source, child)?,
            ))),
            "expression" => Ok(Self::Expression(Expression::from_syntax_node(
                source, child,
            )?)),
            "if_else" => Ok(Statement::IfElse(Box::new(IfElse::from_syntax_node(
                source, child,
            )?))),
            "tool" => Ok(Statement::IfElse(Box::new(IfElse::from_syntax_node(
                source, child,
            )?))),
            "while" => Ok(Statement::While(Box::new(While::from_syntax_node(
                source, child,
            )?))),
            "async" => Ok(Statement::Async(Box::new(Async::from_syntax_node(
                source, child,
            )?))),
            "for" => Ok(Statement::For(Box::new(For::from_syntax_node(
                source, child,
            )?))),
            "transform" => Ok(Statement::Transform(Box::new(Transform::from_syntax_node(
                source, child,
            )?))),
            "filter" => Ok(Statement::Filter(Box::new(Filter::from_syntax_node(
                source, child,
            )?))),
            "find" => Ok(Statement::Find(Box::new(Find::from_syntax_node(
                source, child,
            )?))),
            "remove" => Ok(Statement::Remove(Box::new(Remove::from_syntax_node(
                source, child,
            )?))),
            "select" => Ok(Statement::Select(Box::new(Select::from_syntax_node(
                source, child,
            )?))),
            "insert" => Ok(Statement::Insert(Box::new(Insert::from_syntax_node(
                source, child,
            )?))),
            _ => Err(Error::UnexpectedSyntaxNode {
                expected: "assignment, expression, if...else, while, for, transform, filter, tool, async, find, remove, select or insert",
                actual: child.kind(),
                location: child.start_position(),
                relevant_source: source[child.byte_range()].to_string(),
            }),
        }
    }

    fn run(&self, source: &str, context: &mut VariableMap) -> Result<Value> {
        match self {
            Statement::Assignment(assignment) => assignment.run(source, context),
            Statement::Expression(expression) => expression.run(source, context),
            Statement::IfElse(if_else) => if_else.run(source, context),
            Statement::Match(r#match) => r#match.run(source, context),
            Statement::While(r#while) => r#while.run(source, context),
            Statement::Async(run) => run.run(source, context),
            Statement::For(r#for) => r#for.run(source, context),
            Statement::Transform(transform) => transform.run(source, context),
            Statement::Filter(filter) => filter.run(source, context),
            Statement::Find(find) => find.run(source, context),
            Statement::Remove(remove) => remove.run(source, context),
            Statement::Select(select) => select.run(source, context),
            Statement::Insert(insert) => insert.run(source, context),
        }
    }
}