aboutsummaryrefslogtreecommitdiff
path: root/azalea-brigadier/src/context/command_context_builder.rs
blob: 88e263438790d30d2a325a4e83f4e4c002dc7378 (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use std::collections::HashMap;

use crate::{
    arguments::argument_type::ArgumentType, command::Command,
    command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier,
    tree::command_node::CommandNode,
};

use super::{
    command_context::CommandContext, parsed_argument::ParsedArgument,
    parsed_command_node::ParsedCommandNode, string_range::StringRange,
    suggestion_context::SuggestionContext,
};

// public class CommandContextBuilder<S> {
//     private final Map<String, ParsedArgument<S, ?>> arguments = new LinkedHashMap<>();
//     private final CommandNode<S> rootNode;
//     private final List<ParsedCommandNode<S>> nodes = new ArrayList<>();
//     private final CommandDispatcher<S> dispatcher;
//     private S source;
//     private Command<S> command;
//     private CommandContextBuilder<S> child;
//     private StringRange range;
//     private RedirectModifier<S> modifier = null;
//     private boolean forks;

#[derive(Clone)]
pub struct CommandContextBuilder<'a, S, T> {
    arguments: HashMap<String, ParsedArgument<T>>,
    root_node: &'a dyn CommandNode<S, T>,
    nodes: Vec<ParsedCommandNode<S, T>>,
    dispatcher: CommandDispatcher<'a, S, T>,
    source: S,
    command: Box<dyn Command<S, T>>,
    child: Option<CommandContextBuilder<'a, S, T>>,
    range: StringRange,
    modifier: Option<Box<dyn RedirectModifier<S, T>>>,
    forks: bool,
}

// public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start) {
// 	this.rootNode = rootNode;
// 	this.dispatcher = dispatcher;
// 	this.source = source;
// 	this.range = StringRange.at(start);
// }

impl<S, T> CommandContextBuilder<'_, S, T> {
    pub fn new(
        dispatcher: CommandDispatcher<S, T>,
        source: S,
        root_node: dyn CommandNode<S, T>,
        start: usize,
    ) -> Self {
        Self {
            root_node: &root_node,
            dispatcher,
            source,
            range: StringRange::at(start),
            ..Default::default()
        }
    }

    pub fn with_source(mut self, source: S) -> Self {
        self.source = source;
        self
    }

    pub fn source(&self) -> &S {
        &self.source
    }

    pub fn root_node(&self) -> &dyn CommandNode<S, T> {
        &self.root_node
    }

    pub fn with_argument(mut self, name: String, argument: ParsedArgument<T>) -> Self {
        self.arguments.insert(name, argument);
        self
    }

    pub fn arguments(&self) -> &HashMap<String, ParsedArgument<T>> {
        &self.arguments
    }

    pub fn with_command(mut self, command: &dyn Command<S, T>) -> Self {
        self.command = command;
        self
    }

    pub fn with_node(mut self, node: dyn CommandNode<S, T>, range: StringRange) -> Self {
        self.nodes.push(ParsedCommandNode::new(node, range));
        self.range = StringRange::encompassing(&self.range, &range);
        self.modifier = node.redirect_modifier();
        self.forks = node.is_fork();
        self
    }

    pub fn with_child(mut self, child: CommandContextBuilder<S, T>) -> Self {
        self.child = Some(child);
        self
    }

    pub fn child(&self) -> Option<&CommandContextBuilder<S, T>> {
        self.child.as_ref()
    }

    pub fn last_child(&self) -> Option<&CommandContextBuilder<S, T>> {
        let mut result = self;
        while let Some(child) = result.child() {
            result = child;
        }
        Some(result)
    }

    pub fn command(&self) -> &dyn Command<S, T> {
        &*self.command
    }

    pub fn nodes(&self) -> &Vec<ParsedCommandNode<S, T>> {
        &self.nodes
    }

    pub fn build(self, input: &str) -> CommandContext<S, T> {
        CommandContext {
            source: self.source,
            input,
            arguments: self.arguments,
            command: self.command,
            root_node: self.root_node,
            nodes: self.nodes,
            range: self.range,
            child: self.child.map(|child| child.build(input)),
            modifier: self.modifier,
            forks: self.forks,
        }
    }

    pub fn dispatcher(&self) -> &CommandDispatcher<S, T> {
        &self.dispatcher
    }

    pub fn range(&self) -> &StringRange {
        &self.range
    }

    pub fn find_suggestion_context(&self, cursor: i32) -> Result<SuggestionContext<S, T>, String> {
        if self.range.start() <= cursor {
            if self.range.end() < cursor {
                if let Some(child) = self.child() {
                    child.find_suggestion_context(cursor);
                } else if !self.nodes.is_empty() {
                    let last = self.nodes.last().unwrap();
                    let end = last.range().end() + 1;
                    return SuggestionContext::new(last.node(), end);
                } else {
                    return SuggestionContext::new(self.root_node, self.range.start());
                }
            } else {
                let prev = self.root_node;
                for node in &self.nodes {
                    let node_range = node.range();
                    if node_range.start() <= cursor && cursor <= node_range.end() {
                        return SuggestionContext::new(prev, node_range.start());
                    }
                    prev = node.node();
                }
                if prev.is_none() {
                    return Err(String::from("Can't find node before cursor"));
                }
                return SuggestionContext::new(prev.unwrap(), self.range.start());
            }
        }
        Err(String::from("Can't find node before cursor"))
    }
}