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 | ShowOptionsMode::ResolvedWithInheritanceMarkers => {
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 mode,
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 | ShowOptionsMode::ResolvedWithInheritanceMarkers => self
209 .resolved_user_values_for_show_scope(scope)
210 .into_iter()
211 .find(|(entry_name, _)| entry_name == query.canonical_name())
212 .map(|(_, value)| {
213 render_show_line(
214 &self.show_name_for_mode(scope, query, mode),
215 &value,
216 value_only,
217 )
218 })
219 .into_iter()
220 .collect(),
221 ShowOptionsMode::Explicit => self
222 .node_for_show_scope(scope)
223 .and_then(|node| node.value(query.canonical_name(), query.index()))
224 .map(|value| render_show_line(query.canonical_name(), value, value_only))
225 .into_iter()
226 .collect(),
227 };
228 }
229
230 if query.is_array() && query.index().is_none() {
231 let values = match mode {
232 ShowOptionsMode::Resolved | ShowOptionsMode::ResolvedWithInheritanceMarkers => {
233 self.resolved_array_values_for_show_scope(scope, query)
234 }
235 ShowOptionsMode::Explicit => self
236 .node_for_show_scope(scope)
237 .and_then(|node| node.entry(query.canonical_name()))
238 .map(OptionEntry::array_entries)
239 .unwrap_or_default(),
240 };
241 if values.is_empty() && !value_only {
242 return vec![query.canonical_name().to_owned()];
243 }
244 return values
245 .into_iter()
246 .map(|(index, value)| {
247 let name = show_option_name(query.canonical_name(), Some(index));
248 let name =
249 self.show_index_name_for_mode(scope, query, Some(index), mode, &name);
250 render_known_show_line(query, &name, &value, value_only)
251 })
252 .collect();
253 }
254
255 let value = match mode {
256 ShowOptionsMode::Resolved | ShowOptionsMode::ResolvedWithInheritanceMarkers => {
257 self.resolved_value_for_show_scope(scope, query)
258 }
259 ShowOptionsMode::Explicit => self
260 .node_for_show_scope(scope)
261 .and_then(|node| node.value(query.canonical_name(), query.index()))
262 .map(str::to_owned),
263 };
264
265 value
266 .into_iter()
267 .map(|value| {
268 let name = show_option_name(query.canonical_name(), query.index());
269 let name = self.show_index_name_for_mode(scope, query, query.index(), mode, &name);
270 render_known_show_line(query, &name, &value, value_only)
271 })
272 .collect()
273 }
274
275 fn show_name_for_mode(
276 &self,
277 scope: &ShowScope,
278 query: &OptionQuery,
279 mode: ShowOptionsMode,
280 ) -> String {
281 let name = query.canonical_name();
282 self.show_index_name_for_mode(scope, query, query.index(), mode, name)
283 }
284
285 fn show_index_name_for_mode(
286 &self,
287 scope: &ShowScope,
288 query: &OptionQuery,
289 index: Option<u32>,
290 mode: ShowOptionsMode,
291 name: &str,
292 ) -> String {
293 if mode != ShowOptionsMode::ResolvedWithInheritanceMarkers {
294 return name.to_owned();
295 }
296 if matches!(
297 scope,
298 ShowScope::Server | ShowScope::SessionGlobal | ShowScope::WindowGlobal
299 ) {
300 return name.to_owned();
301 }
302 if self
303 .node_for_show_scope(scope)
304 .is_some_and(|node| node.contains(query.canonical_name(), index))
305 {
306 name.to_owned()
307 } else {
308 format!("{name}*")
309 }
310 }
311
312 fn resolved_array_values_for_show_scope(
313 &self,
314 scope: &ShowScope,
315 query: &OptionQuery,
316 ) -> Vec<(u32, String)> {
317 let resolved = match scope {
318 ShowScope::Server => self.resolve_query(&ResolveContext::Server, query),
319 ShowScope::SessionGlobal => self.resolve_query(&ResolveContext::SessionGlobal, query),
320 ShowScope::WindowGlobal => self.resolve_query(&ResolveContext::WindowGlobal, query),
321 ShowScope::Session(session_name) => {
322 self.resolve_query(&ResolveContext::Session(session_name), query)
323 }
324 ShowScope::Window(target) => self.resolve_query(
325 &ResolveContext::Window(target.session_name(), target.window_index()),
326 query,
327 ),
328 ShowScope::Pane(target) => self.resolve_query(
329 &ResolveContext::Pane(
330 target.session_name(),
331 target.window_index(),
332 target.pane_index(),
333 ),
334 query,
335 ),
336 };
337
338 resolved
339 .map(OptionEntry::array_entries)
340 .unwrap_or_else(|| default_array_show_values(query))
341 }
342
343 fn show_scope_for_named_query<'a>(
344 &self,
345 scope: &ShowScope<'a>,
346 query: &OptionQuery,
347 ) -> ShowScope<'a> {
348 let Some(metadata) = query.metadata() else {
349 return *scope;
350 };
351 if metadata.visible_in(scope.mask()) {
352 return *scope;
353 }
354
355 match metadata.global_root() {
356 GlobalRoot::Server => ShowScope::Server,
357 GlobalRoot::Session => ShowScope::SessionGlobal,
358 GlobalRoot::Window => ShowScope::WindowGlobal,
359 }
360 }
361
362 fn node_for_show_scope(&self, scope: &ShowScope) -> Option<&OptionNode> {
363 match scope {
364 ShowScope::Server => Some(&self.server_global),
365 ShowScope::SessionGlobal => Some(&self.session_global),
366 ShowScope::WindowGlobal => Some(&self.window_global),
367 ShowScope::Session(session_name) => self.sessions.get(session_name),
368 ShowScope::Window(target) => self.windows.get(target),
369 ShowScope::Pane(target) => self.panes.get(target),
370 }
371 }
372
373 fn user_chain_for_context<'a>(&'a self, context: &ResolveContext<'a>) -> Vec<&'a OptionNode> {
374 match context {
375 ResolveContext::Server => vec![&self.server_global],
376 ResolveContext::SessionGlobal => vec![&self.session_global, &self.server_global],
377 ResolveContext::WindowGlobal => {
378 vec![
379 &self.window_global,
380 &self.session_global,
381 &self.server_global,
382 ]
383 }
384 ResolveContext::Session(session_name) => {
385 let mut chain = Vec::new();
386 if let Some(node) = self.sessions.get(*session_name) {
387 chain.push(node);
388 }
389 chain.push(&self.session_global);
390 chain.push(&self.server_global);
391 chain
392 }
393 ResolveContext::Window(session_name, window_index) => {
394 let mut chain = Vec::new();
395 let target = WindowTarget::with_window((*session_name).clone(), *window_index);
396 if let Some(node) = self.windows.get(&target) {
397 chain.push(node);
398 }
399 if let Some(node) = self.sessions.get(*session_name) {
400 chain.push(node);
401 }
402 chain.push(&self.window_global);
403 chain.push(&self.session_global);
404 chain.push(&self.server_global);
405 chain
406 }
407 ResolveContext::Pane(session_name, window_index, pane_index) => {
408 let mut chain = Vec::new();
409 let target =
410 PaneTarget::with_window((*session_name).clone(), *window_index, *pane_index);
411 if let Some(node) = self.panes.get(&target) {
412 chain.push(node);
413 }
414 let window_target =
415 WindowTarget::with_window((*session_name).clone(), *window_index);
416 if let Some(node) = self.windows.get(&window_target) {
417 chain.push(node);
418 }
419 if let Some(node) = self.sessions.get(*session_name) {
420 chain.push(node);
421 }
422 chain.push(&self.window_global);
423 chain.push(&self.session_global);
424 chain.push(&self.server_global);
425 chain
426 }
427 }
428 }
429}