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