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 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 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 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 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}