Skip to main content

rmux_core/options/
show.rs

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