1use std::collections::BTreeMap;
2
3use rmux_proto::types::OptionScopeSelector;
4use rmux_proto::{PaneTarget, RmuxError, WindowTarget};
5
6use super::registry::{registry, resolve_option_name, GlobalRoot};
7use super::render::{
8 default_array_show_values, render_entry_show_lines, render_known_show_line, render_show_line,
9 show_option_name,
10};
11use super::scope::{ResolveContext, ShowScope};
12use super::storage::{OptionEntry, OptionNode};
13use super::{OptionQuery, OptionStore, ShowOptionsMode};
14
15impl OptionStore {
16 pub fn show_options_lines(
18 &self,
19 scope: &OptionScopeSelector,
20 value_only: bool,
21 ) -> Result<Vec<String>, RmuxError> {
22 self.show_options_lines_filtered(scope, None, value_only)
23 }
24
25 pub fn show_options_lines_filtered(
27 &self,
28 scope: &OptionScopeSelector,
29 name: Option<&str>,
30 value_only: bool,
31 ) -> Result<Vec<String>, RmuxError> {
32 self.show_options_lines_with_mode_filtered(
33 scope,
34 name,
35 value_only,
36 ShowOptionsMode::Resolved,
37 )
38 }
39
40 pub fn show_options_lines_with_mode(
42 &self,
43 scope: &OptionScopeSelector,
44 value_only: bool,
45 mode: ShowOptionsMode,
46 ) -> Result<Vec<String>, RmuxError> {
47 self.show_options_lines_with_mode_filtered(scope, None, value_only, mode)
48 }
49
50 pub fn show_options_lines_with_mode_filtered(
52 &self,
53 scope: &OptionScopeSelector,
54 name: Option<&str>,
55 value_only: bool,
56 mode: ShowOptionsMode,
57 ) -> Result<Vec<String>, RmuxError> {
58 let show_scope = ShowScope::from_selector(scope);
59 if let Some(name) = name {
60 return self.show_options_lines_for_name(&show_scope, name, value_only, mode);
61 }
62
63 let mut lines = Vec::new();
64
65 match mode {
66 ShowOptionsMode::Resolved => {
67 for metadata in registry()
68 .iter()
69 .filter(|metadata| metadata.visible_in(show_scope.mask()))
70 {
71 let query = OptionQuery::known(metadata.option());
72 lines.extend(self.render_show_lines_for_query(
73 &show_scope,
74 &query,
75 value_only,
76 ShowOptionsMode::Resolved,
77 ));
78 }
79
80 for (name, value) in self.resolved_user_values_for_show_scope(&show_scope) {
81 lines.push(render_show_line(&name, &value, value_only));
82 }
83 }
84 ShowOptionsMode::Explicit => {
85 if let Some(node) = self.node_for_show_scope(&show_scope) {
86 for entry in node.entries.values() {
87 if entry.known_option.is_some() {
88 lines.extend(render_entry_show_lines(entry, value_only));
89 }
90 }
91 for entry in node
92 .entries
93 .values()
94 .filter(|entry| entry.known_option.is_none())
95 {
96 lines.extend(render_entry_show_lines(entry, value_only));
97 }
98 }
99 }
100 }
101
102 Ok(lines)
103 }
104
105 fn resolved_value_for_show_scope(
106 &self,
107 scope: &ShowScope,
108 query: &OptionQuery,
109 ) -> Option<String> {
110 match scope {
111 ShowScope::Server => self
112 .resolve_query(&ResolveContext::Server, query)
113 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
114 .or_else(|| self.default_value_as_string(query)),
115 ShowScope::SessionGlobal => self
116 .resolve_query(&ResolveContext::SessionGlobal, query)
117 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
118 .or_else(|| self.default_value_as_string(query)),
119 ShowScope::WindowGlobal => self
120 .resolve_query(&ResolveContext::WindowGlobal, query)
121 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
122 .or_else(|| self.default_value_as_string(query)),
123 ShowScope::Session(session_name) => self
124 .resolve_query(&ResolveContext::Session(session_name), query)
125 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
126 .or_else(|| self.default_value_as_string(query)),
127 ShowScope::Window(target) => self
128 .resolve_query(
129 &ResolveContext::Window(target.session_name(), target.window_index()),
130 query,
131 )
132 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
133 .or_else(|| self.default_value_as_string(query)),
134 ShowScope::Pane(target) => self
135 .resolve_query(
136 &ResolveContext::Pane(
137 target.session_name(),
138 target.window_index(),
139 target.pane_index(),
140 ),
141 query,
142 )
143 .and_then(|entry| entry.value(query.index()).map(str::to_owned))
144 .or_else(|| self.default_value_as_string(query)),
145 }
146 }
147
148 fn resolved_user_values_for_show_scope(&self, scope: &ShowScope) -> Vec<(String, String)> {
149 let context = match scope {
150 ShowScope::Server => ResolveContext::Server,
151 ShowScope::SessionGlobal => ResolveContext::SessionGlobal,
152 ShowScope::WindowGlobal => ResolveContext::WindowGlobal,
153 ShowScope::Session(session_name) => ResolveContext::Session(session_name),
154 ShowScope::Window(target) => {
155 ResolveContext::Window(target.session_name(), target.window_index())
156 }
157 ShowScope::Pane(target) => ResolveContext::Pane(
158 target.session_name(),
159 target.window_index(),
160 target.pane_index(),
161 ),
162 };
163
164 let mut values = BTreeMap::<String, String>::new();
165 for node in self.user_chain_for_context(&context) {
166 for (name, entry) in &node.entries {
167 if entry.known_option.is_none() {
168 values
169 .entry(name.clone())
170 .or_insert_with(|| entry.rendered.clone());
171 }
172 }
173 }
174 values.into_iter().collect()
175 }
176
177 fn show_options_lines_for_name(
178 &self,
179 scope: &ShowScope,
180 name: &str,
181 value_only: bool,
182 mode: ShowOptionsMode,
183 ) -> Result<Vec<String>, RmuxError> {
184 let query = resolve_option_name(name)?;
185 let scope = self.show_scope_for_named_query(scope, &query);
186 let lines = self.render_show_lines_for_query(&scope, &query, value_only, mode);
187 if query.is_user() && lines.is_empty() {
188 return Err(RmuxError::Message(format!(
189 "invalid option: {}",
190 query.canonical_name()
191 )));
192 }
193 if query.is_array() && query.index().is_none() && lines.is_empty() && !value_only {
194 return Ok(vec![query.canonical_name().to_owned()]);
195 }
196 Ok(lines)
197 }
198
199 fn render_show_lines_for_query(
200 &self,
201 scope: &ShowScope,
202 query: &OptionQuery,
203 value_only: bool,
204 mode: ShowOptionsMode,
205 ) -> Vec<String> {
206 if query.is_user() {
207 return match mode {
208 ShowOptionsMode::Resolved => self
209 .resolved_user_values_for_show_scope(scope)
210 .into_iter()
211 .find(|(entry_name, _)| entry_name == query.canonical_name())
212 .map(|(_, value)| render_show_line(query.canonical_name(), &value, value_only))
213 .into_iter()
214 .collect(),
215 ShowOptionsMode::Explicit => self
216 .node_for_show_scope(scope)
217 .and_then(|node| node.value(query.canonical_name(), query.index()))
218 .map(|value| render_show_line(query.canonical_name(), value, value_only))
219 .into_iter()
220 .collect(),
221 };
222 }
223
224 if query.is_array() && query.index().is_none() {
225 let values = match mode {
226 ShowOptionsMode::Resolved => {
227 self.resolved_array_values_for_show_scope(scope, query)
228 }
229 ShowOptionsMode::Explicit => self
230 .node_for_show_scope(scope)
231 .and_then(|node| node.entry(query.canonical_name()))
232 .map(OptionEntry::array_entries)
233 .unwrap_or_default(),
234 };
235 if values.is_empty() && !value_only {
236 return vec![query.canonical_name().to_owned()];
237 }
238 return values
239 .into_iter()
240 .map(|(index, value)| {
241 render_known_show_line(
242 query,
243 &show_option_name(query.canonical_name(), Some(index)),
244 &value,
245 value_only,
246 )
247 })
248 .collect();
249 }
250
251 let value = match mode {
252 ShowOptionsMode::Resolved => self.resolved_value_for_show_scope(scope, query),
253 ShowOptionsMode::Explicit => self
254 .node_for_show_scope(scope)
255 .and_then(|node| node.value(query.canonical_name(), query.index()))
256 .map(str::to_owned),
257 };
258
259 value
260 .into_iter()
261 .map(|value| {
262 render_known_show_line(
263 query,
264 &show_option_name(query.canonical_name(), query.index()),
265 &value,
266 value_only,
267 )
268 })
269 .collect()
270 }
271
272 fn resolved_array_values_for_show_scope(
273 &self,
274 scope: &ShowScope,
275 query: &OptionQuery,
276 ) -> Vec<(u32, String)> {
277 let resolved = match scope {
278 ShowScope::Server => self.resolve_query(&ResolveContext::Server, query),
279 ShowScope::SessionGlobal => self.resolve_query(&ResolveContext::SessionGlobal, query),
280 ShowScope::WindowGlobal => self.resolve_query(&ResolveContext::WindowGlobal, query),
281 ShowScope::Session(session_name) => {
282 self.resolve_query(&ResolveContext::Session(session_name), query)
283 }
284 ShowScope::Window(target) => self.resolve_query(
285 &ResolveContext::Window(target.session_name(), target.window_index()),
286 query,
287 ),
288 ShowScope::Pane(target) => self.resolve_query(
289 &ResolveContext::Pane(
290 target.session_name(),
291 target.window_index(),
292 target.pane_index(),
293 ),
294 query,
295 ),
296 };
297
298 resolved
299 .map(OptionEntry::array_entries)
300 .unwrap_or_else(|| default_array_show_values(query))
301 }
302
303 fn show_scope_for_named_query<'a>(
304 &self,
305 scope: &ShowScope<'a>,
306 query: &OptionQuery,
307 ) -> ShowScope<'a> {
308 let Some(metadata) = query.metadata() else {
309 return *scope;
310 };
311 if metadata.visible_in(scope.mask()) {
312 return *scope;
313 }
314
315 match metadata.global_root() {
316 GlobalRoot::Server => ShowScope::Server,
317 GlobalRoot::Session => ShowScope::SessionGlobal,
318 GlobalRoot::Window => ShowScope::WindowGlobal,
319 }
320 }
321
322 fn node_for_show_scope(&self, scope: &ShowScope) -> Option<&OptionNode> {
323 match scope {
324 ShowScope::Server => Some(&self.server_global),
325 ShowScope::SessionGlobal => Some(&self.session_global),
326 ShowScope::WindowGlobal => Some(&self.window_global),
327 ShowScope::Session(session_name) => self.sessions.get(session_name),
328 ShowScope::Window(target) => self.windows.get(target),
329 ShowScope::Pane(target) => self.panes.get(target),
330 }
331 }
332
333 fn user_chain_for_context<'a>(&'a self, context: &ResolveContext<'a>) -> Vec<&'a OptionNode> {
334 match context {
335 ResolveContext::Server => vec![&self.server_global],
336 ResolveContext::SessionGlobal => vec![&self.session_global, &self.server_global],
337 ResolveContext::WindowGlobal => {
338 vec![
339 &self.window_global,
340 &self.session_global,
341 &self.server_global,
342 ]
343 }
344 ResolveContext::Session(session_name) => {
345 let mut chain = Vec::new();
346 if let Some(node) = self.sessions.get(*session_name) {
347 chain.push(node);
348 }
349 chain.push(&self.session_global);
350 chain.push(&self.server_global);
351 chain
352 }
353 ResolveContext::Window(session_name, window_index) => {
354 let mut chain = Vec::new();
355 let target = WindowTarget::with_window((*session_name).clone(), *window_index);
356 if let Some(node) = self.windows.get(&target) {
357 chain.push(node);
358 }
359 if let Some(node) = self.sessions.get(*session_name) {
360 chain.push(node);
361 }
362 chain.push(&self.window_global);
363 chain.push(&self.session_global);
364 chain.push(&self.server_global);
365 chain
366 }
367 ResolveContext::Pane(session_name, window_index, pane_index) => {
368 let mut chain = Vec::new();
369 let target =
370 PaneTarget::with_window((*session_name).clone(), *window_index, *pane_index);
371 if let Some(node) = self.panes.get(&target) {
372 chain.push(node);
373 }
374 let window_target =
375 WindowTarget::with_window((*session_name).clone(), *window_index);
376 if let Some(node) = self.windows.get(&window_target) {
377 chain.push(node);
378 }
379 if let Some(node) = self.sessions.get(*session_name) {
380 chain.push(node);
381 }
382 chain.push(&self.window_global);
383 chain.push(&self.session_global);
384 chain.push(&self.server_global);
385 chain
386 }
387 }
388 }
389}