azalea_brigadier/context/
command_context_builder.rs

1use std::{
2    collections::HashMap,
3    fmt::{self, Debug},
4    rc::Rc,
5    sync::Arc,
6};
7
8use parking_lot::RwLock;
9
10use super::{
11    ParsedArgument, command_context::CommandContext, parsed_command_node::ParsedCommandNode,
12    string_range::StringRange, suggestion_context::SuggestionContext,
13};
14use crate::{
15    command_dispatcher::CommandDispatcher,
16    modifier::RedirectModifier,
17    tree::{Command, CommandNode},
18};
19
20pub struct CommandContextBuilder<'a, S> {
21    pub arguments: HashMap<String, ParsedArgument>,
22    pub root: Arc<RwLock<CommandNode<S>>>,
23    pub nodes: Vec<ParsedCommandNode<S>>,
24    pub dispatcher: &'a CommandDispatcher<S>,
25    pub source: Arc<S>,
26    pub command: Command<S>,
27    pub child: Option<Rc<CommandContextBuilder<'a, S>>>,
28    pub range: StringRange,
29    pub modifier: Option<Arc<RedirectModifier<S>>>,
30    pub forks: bool,
31}
32
33impl<S> Clone for CommandContextBuilder<'_, S> {
34    fn clone(&self) -> Self {
35        Self {
36            arguments: self.arguments.clone(),
37            root: self.root.clone(),
38            nodes: self.nodes.clone(),
39            dispatcher: self.dispatcher,
40            source: self.source.clone(),
41            command: self.command.clone(),
42            child: self.child.clone(),
43            range: self.range,
44            modifier: self.modifier.clone(),
45            forks: self.forks,
46        }
47    }
48}
49
50impl<'a, S> CommandContextBuilder<'a, S> {
51    pub fn new(
52        dispatcher: &'a CommandDispatcher<S>,
53        source: Arc<S>,
54        root_node: Arc<RwLock<CommandNode<S>>>,
55        start: usize,
56    ) -> Self {
57        Self {
58            arguments: HashMap::new(),
59            root: root_node,
60            source,
61            range: StringRange::at(start),
62            command: None,
63            dispatcher,
64            nodes: vec![],
65            child: None,
66            modifier: None,
67            forks: false,
68        }
69    }
70
71    pub fn with_command(&mut self, command: &Command<S>) -> &Self {
72        self.command.clone_from(command);
73        self
74    }
75    pub fn with_child(&mut self, child: Rc<CommandContextBuilder<'a, S>>) -> &Self {
76        self.child = Some(child);
77        self
78    }
79    pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self {
80        self.arguments.insert(name.to_string(), argument);
81        self
82    }
83    pub fn with_node(&mut self, node: Arc<RwLock<CommandNode<S>>>, range: StringRange) -> &Self {
84        self.nodes.push(ParsedCommandNode {
85            node: node.clone(),
86            range,
87        });
88        self.range = StringRange::encompassing(&self.range, &range);
89        self.modifier.clone_from(&node.read().modifier);
90        self.forks = node.read().forks;
91        self
92    }
93
94    pub fn build(&self, input: &str) -> CommandContext<S> {
95        CommandContext {
96            arguments: self.arguments.clone(),
97            root_node: self.root.clone(),
98            nodes: self.nodes.clone(),
99            source: self.source.clone(),
100            command: self.command.clone(),
101            child: self.child.clone().map(|c| Rc::new(c.build(input))),
102            range: self.range,
103            forks: self.forks,
104            modifier: self.modifier.clone(),
105            input: input.to_string(),
106        }
107    }
108
109    pub fn find_suggestion_context(&self, cursor: usize) -> SuggestionContext<S> {
110        if self.range.start() > cursor {
111            panic!("Can't find node before cursor");
112        }
113
114        if self.range.end() < cursor {
115            match &self.child {
116                Some(child) => child.find_suggestion_context(cursor),
117                _ => match self.nodes.last() {
118                    Some(last) => SuggestionContext {
119                        parent: Arc::clone(&last.node),
120                        start_pos: last.range.end() + 1,
121                    },
122                    _ => SuggestionContext {
123                        parent: Arc::clone(&self.root),
124                        start_pos: self.range.start(),
125                    },
126                },
127            }
128        } else {
129            let mut prev = &self.root;
130            for node in &self.nodes {
131                if node.range.start() <= cursor && cursor <= node.range.end() {
132                    return SuggestionContext {
133                        parent: Arc::clone(prev),
134                        start_pos: node.range.start(),
135                    };
136                }
137                prev = &node.node;
138            }
139            SuggestionContext {
140                parent: Arc::clone(prev),
141                start_pos: self.range.start(),
142            }
143        }
144    }
145}
146
147impl<S> Debug for CommandContextBuilder<'_, S> {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.debug_struct("CommandContextBuilder")
150            // .field("arguments", &self.arguments)
151            .field("root", &self.root)
152            // .field("nodes", &self.nodes)
153            // .field("dispatcher", &self.dispatcher)
154            // .field("source", &self.source)
155            // .field("command", &self.command)
156            .field("child", &self.child)
157            .field("range", &self.range)
158            // .field("modifier", &self.modifier)
159            .field("forks", &self.forks)
160            .finish()
161    }
162}