azalea_brigadier/context/
command_context_builder.rs1use 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("root", &self.root)
152 .field("child", &self.child)
157 .field("range", &self.range)
158 .field("forks", &self.forks)
160 .finish()
161 }
162}