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 self
297 .node_for_show_scope(scope)
298 .is_some_and(|node| node.contains(query.canonical_name(), index))
299 {
300 name.to_owned()
301 } else {
302 format!("{name}*")
303 }
304 }
305
306 fn resolved_array_values_for_show_scope(
307 &self,
308 scope: &ShowScope,
309 query: &OptionQuery,
310 ) -> Vec<(u32, String)> {
311 let resolved = match scope {
312 ShowScope::Server => self.resolve_query(&ResolveContext::Server, query),
313 ShowScope::SessionGlobal => self.resolve_query(&ResolveContext::SessionGlobal, query),
314 ShowScope::WindowGlobal => self.resolve_query(&ResolveContext::WindowGlobal, query),
315 ShowScope::Session(session_name) => {
316 self.resolve_query(&ResolveContext::Session(session_name), query)
317 }
318 ShowScope::Window(target) => self.resolve_query(
319 &ResolveContext::Window(target.session_name(), target.window_index()),
320 query,
321 ),
322 ShowScope::Pane(target) => self.resolve_query(
323 &ResolveContext::Pane(
324 target.session_name(),
325 target.window_index(),
326 target.pane_index(),
327 ),
328 query,
329 ),
330 };
331
332 resolved
333 .map(OptionEntry::array_entries)
334 .unwrap_or_else(|| default_array_show_values(query))
335 }
336
337 fn show_scope_for_named_query<'a>(
338 &self,
339 scope: &ShowScope<'a>,
340 query: &OptionQuery,
341 ) -> ShowScope<'a> {
342 let Some(metadata) = query.metadata() else {
343 return *scope;
344 };
345 if metadata.visible_in(scope.mask()) {
346 return *scope;
347 }
348
349 match metadata.global_root() {
350 GlobalRoot::Server => ShowScope::Server,
351 GlobalRoot::Session => ShowScope::SessionGlobal,
352 GlobalRoot::Window => ShowScope::WindowGlobal,
353 }
354 }
355
356 fn node_for_show_scope(&self, scope: &ShowScope) -> Option<&OptionNode> {
357 match scope {
358 ShowScope::Server => Some(&self.server_global),
359 ShowScope::SessionGlobal => Some(&self.session_global),
360 ShowScope::WindowGlobal => Some(&self.window_global),
361 ShowScope::Session(session_name) => self.sessions.get(session_name),
362 ShowScope::Window(target) => self.windows.get(target),
363 ShowScope::Pane(target) => self.panes.get(target),
364 }
365 }
366
367 fn user_chain_for_context<'a>(&'a self, context: &ResolveContext<'a>) -> Vec<&'a OptionNode> {
368 match context {
369 ResolveContext::Server => vec![&self.server_global],
370 ResolveContext::SessionGlobal => vec![&self.session_global, &self.server_global],
371 ResolveContext::WindowGlobal => {
372 vec![
373 &self.window_global,
374 &self.session_global,
375 &self.server_global,
376 ]
377 }
378 ResolveContext::Session(session_name) => {
379 let mut chain = Vec::new();
380 if let Some(node) = self.sessions.get(*session_name) {
381 chain.push(node);
382 }
383 chain.push(&self.session_global);
384 chain.push(&self.server_global);
385 chain
386 }
387 ResolveContext::Window(session_name, window_index) => {
388 let mut chain = Vec::new();
389 let target = WindowTarget::with_window((*session_name).clone(), *window_index);
390 if let Some(node) = self.windows.get(&target) {
391 chain.push(node);
392 }
393 if let Some(node) = self.sessions.get(*session_name) {
394 chain.push(node);
395 }
396 chain.push(&self.window_global);
397 chain.push(&self.session_global);
398 chain.push(&self.server_global);
399 chain
400 }
401 ResolveContext::Pane(session_name, window_index, pane_index) => {
402 let mut chain = Vec::new();
403 let target =
404 PaneTarget::with_window((*session_name).clone(), *window_index, *pane_index);
405 if let Some(node) = self.panes.get(&target) {
406 chain.push(node);
407 }
408 let window_target =
409 WindowTarget::with_window((*session_name).clone(), *window_index);
410 if let Some(node) = self.windows.get(&window_target) {
411 chain.push(node);
412 }
413 if let Some(node) = self.sessions.get(*session_name) {
414 chain.push(node);
415 }
416 chain.push(&self.window_global);
417 chain.push(&self.session_global);
418 chain.push(&self.server_global);
419 chain
420 }
421 }
422 }
423}