1use std::collections::HashMap;
2
3use rmux_proto::types::OptionScopeSelector;
4use rmux_proto::{OptionName, PaneTarget, SessionName, WindowTarget};
5
6use super::mutation::{
7 default_array_items, default_scalar_text, normalize_scalar_value, split_array_assignment,
8};
9use super::registry::{option_metadata, registry, resolve_option_name, DefaultValue, GlobalRoot};
10use super::render::format_rendered_option_value;
11use super::scope::{
12 push_known_global_roots, scope_allows_pane, scope_allows_session, scope_allows_window,
13 ResolveContext,
14};
15use super::storage::{OptionEntry, OptionNode};
16use super::{OptionQuery, OptionStore};
17
18impl OptionStore {
19 #[must_use]
21 pub fn global_value(&self, option: OptionName) -> Option<&str> {
22 let query = OptionQuery::known(option);
23 self.node_for_global_root(option_metadata(option).global_root())
24 .and_then(|node| node.value(query.canonical_name(), None))
25 }
26
27 #[must_use]
29 pub fn session_value(&self, session_name: &SessionName, option: OptionName) -> Option<&str> {
30 let query = OptionQuery::known(option);
31 self.sessions
32 .get(session_name)
33 .and_then(|node| node.value(query.canonical_name(), None))
34 }
35
36 #[must_use]
38 pub fn window_value(&self, target: &WindowTarget, option: OptionName) -> Option<&str> {
39 let query = OptionQuery::known(option);
40 self.windows
41 .get(target)
42 .and_then(|node| node.value(query.canonical_name(), None))
43 }
44
45 #[must_use]
47 pub fn pane_value(&self, target: &PaneTarget, option: OptionName) -> Option<&str> {
48 let query = OptionQuery::known(option);
49 self.panes
50 .get(target)
51 .and_then(|node| node.value(query.canonical_name(), None))
52 }
53
54 #[must_use]
56 pub fn resolve<'a>(
57 &'a self,
58 session_name: Option<&'a SessionName>,
59 option: OptionName,
60 ) -> Option<&'a str> {
61 let query = OptionQuery::known(option);
62 let resolved = match session_name {
63 Some(session_name) => self
64 .resolve_query(&ResolveContext::Session(session_name), &query)
65 .map(OptionEntry::rendered),
66 None => self
67 .resolve_query(&ResolveContext::Server, &query)
68 .map(OptionEntry::rendered),
69 };
70 if let Some(resolved) = resolved {
71 Some(resolved)
72 } else {
73 self.default_rendered_value(&query)
74 }
75 }
76
77 #[must_use]
79 pub fn resolve_for_window<'a>(
80 &'a self,
81 session_name: &'a SessionName,
82 window_index: u32,
83 option: OptionName,
84 ) -> Option<&'a str> {
85 let query = OptionQuery::known(option);
86 let resolved = self
87 .resolve_query(&ResolveContext::Window(session_name, window_index), &query)
88 .map(OptionEntry::rendered);
89 if let Some(resolved) = resolved {
90 Some(resolved)
91 } else {
92 self.default_rendered_value(&query)
93 }
94 }
95
96 #[must_use]
98 pub fn resolve_for_pane<'a>(
99 &'a self,
100 session_name: &'a SessionName,
101 window_index: u32,
102 pane_index: u32,
103 option: OptionName,
104 ) -> Option<&'a str> {
105 let query = OptionQuery::known(option);
106 let resolved = self
107 .resolve_query(
108 &ResolveContext::Pane(session_name, window_index, pane_index),
109 &query,
110 )
111 .map(OptionEntry::rendered);
112 if let Some(resolved) = resolved {
113 Some(resolved)
114 } else {
115 self.default_rendered_value(&query)
116 }
117 }
118
119 #[must_use]
121 pub fn resolve_array_values(
122 &self,
123 session_name: Option<&SessionName>,
124 option: OptionName,
125 ) -> Vec<String> {
126 let query = OptionQuery::known(option);
127 if !query.is_array() {
128 return Vec::new();
129 }
130
131 let resolved = match session_name {
132 Some(session_name) => self
133 .resolve_query(&ResolveContext::Session(session_name), &query)
134 .map(OptionEntry::array_values),
135 None => self
136 .resolve_query(&ResolveContext::Server, &query)
137 .map(OptionEntry::array_values),
138 };
139 if let Some(resolved) = resolved {
140 return resolved;
141 }
142
143 match query.default_value() {
144 Some(DefaultValue::Scalar(value)) => split_array_assignment(value, query.separator()),
145 Some(DefaultValue::Array(values)) => {
146 values.iter().map(|value| (*value).to_owned()).collect()
147 }
148 None => Vec::new(),
149 }
150 }
151
152 #[must_use]
154 pub fn resolve_name(&self, session_name: Option<&SessionName>, name: &str) -> Option<String> {
155 let query = resolve_option_name(name).ok()?;
156 match session_name {
157 Some(session_name) => self
160 .resolve_name_from_nodes(
161 &query,
162 [
163 Some(&self.server_global),
164 self.sessions.get(session_name),
165 Some(&self.session_global),
166 ],
167 )
168 .or_else(|| self.default_value_as_string(&query)),
169 None => self
170 .resolve_name_from_nodes(&query, [Some(&self.server_global)])
171 .or_else(|| self.default_value_as_string(&query)),
172 }
173 }
174
175 #[must_use]
177 pub fn resolve_name_for_format(
178 &self,
179 session_name: Option<&SessionName>,
180 name: &str,
181 ) -> Option<String> {
182 let query = resolve_option_name(name).ok()?;
183 let value = match session_name {
184 Some(session_name) => self
185 .resolve_name_from_nodes(
186 &query,
187 [
188 Some(&self.server_global),
189 self.sessions.get(session_name),
190 Some(&self.session_global),
191 ],
192 )
193 .or_else(|| self.default_value_as_string(&query)),
194 None => self
195 .resolve_name_from_nodes(&query, [Some(&self.server_global)])
196 .or_else(|| self.default_value_as_string(&query)),
197 }?;
198 Some(format_rendered_option_value(&query, value))
199 }
200
201 #[must_use]
203 pub fn resolve_name_for_window(
204 &self,
205 session_name: &SessionName,
206 window_index: u32,
207 name: &str,
208 ) -> Option<String> {
209 let query = resolve_option_name(name).ok()?;
210 let target = WindowTarget::with_window(session_name.clone(), window_index);
211 self.resolve_name_from_nodes(
212 &query,
213 [
214 Some(&self.server_global),
215 self.windows.get(&target),
216 Some(&self.window_global),
217 self.sessions.get(session_name),
218 Some(&self.session_global),
219 ],
220 )
221 .or_else(|| self.default_value_as_string(&query))
222 }
223
224 #[must_use]
226 pub fn resolve_name_for_window_format(
227 &self,
228 session_name: &SessionName,
229 window_index: u32,
230 name: &str,
231 ) -> Option<String> {
232 let query = resolve_option_name(name).ok()?;
233 let target = WindowTarget::with_window(session_name.clone(), window_index);
234 let value = self
235 .resolve_name_from_nodes(
236 &query,
237 [
238 Some(&self.server_global),
239 self.windows.get(&target),
240 Some(&self.window_global),
241 self.sessions.get(session_name),
242 Some(&self.session_global),
243 ],
244 )
245 .or_else(|| self.default_value_as_string(&query))?;
246 Some(format_rendered_option_value(&query, value))
247 }
248
249 #[must_use]
251 pub fn resolve_name_for_pane(
252 &self,
253 session_name: &SessionName,
254 window_index: u32,
255 pane_index: u32,
256 name: &str,
257 ) -> Option<String> {
258 let query = resolve_option_name(name).ok()?;
259 let pane_target = PaneTarget::with_window(session_name.clone(), window_index, pane_index);
260 let window_target = WindowTarget::with_window(session_name.clone(), window_index);
261 self.resolve_name_from_nodes(
262 &query,
263 [
264 Some(&self.server_global),
265 self.panes.get(&pane_target),
266 self.windows.get(&window_target),
267 Some(&self.window_global),
268 self.sessions.get(session_name),
269 Some(&self.session_global),
270 ],
271 )
272 .or_else(|| self.default_value_as_string(&query))
273 }
274
275 #[must_use]
277 pub fn resolve_name_for_pane_format(
278 &self,
279 session_name: &SessionName,
280 window_index: u32,
281 pane_index: u32,
282 name: &str,
283 ) -> Option<String> {
284 let query = resolve_option_name(name).ok()?;
285 let pane_target = PaneTarget::with_window(session_name.clone(), window_index, pane_index);
286 let window_target = WindowTarget::with_window(session_name.clone(), window_index);
287 let value = self
288 .resolve_name_from_nodes(
289 &query,
290 [
291 Some(&self.server_global),
292 self.panes.get(&pane_target),
293 self.windows.get(&window_target),
294 Some(&self.window_global),
295 self.sessions.get(session_name),
296 Some(&self.session_global),
297 ],
298 )
299 .or_else(|| self.default_value_as_string(&query))?;
300 Some(format_rendered_option_value(&query, value))
301 }
302
303 #[must_use]
305 pub fn resolved(&self, session_name: &SessionName) -> HashMap<OptionName, String> {
306 registry()
307 .iter()
308 .map(|metadata| {
309 let query = OptionQuery::known(metadata.option());
310 let value = self
311 .resolve(Some(session_name), metadata.option())
312 .map(str::to_owned)
313 .or_else(|| self.default_value_as_string(&query))
314 .unwrap_or_default();
315 (metadata.option(), value)
316 })
317 .collect()
318 }
319
320 #[must_use]
322 pub fn resolved_for_pane(
323 &self,
324 session_name: &SessionName,
325 window_index: u32,
326 pane_index: u32,
327 ) -> HashMap<OptionName, String> {
328 registry()
329 .iter()
330 .map(|metadata| {
331 let query = OptionQuery::known(metadata.option());
332 let value = self
333 .resolve_for_pane(session_name, window_index, pane_index, metadata.option())
334 .map(str::to_owned)
335 .or_else(|| self.default_value_as_string(&query))
336 .unwrap_or_default();
337 (metadata.option(), value)
338 })
339 .collect()
340 }
341
342 pub(super) fn node_for_exact_scope_mut(
343 &mut self,
344 scope: &OptionScopeSelector,
345 ) -> &mut OptionNode {
346 match scope {
347 OptionScopeSelector::ServerGlobal => &mut self.server_global,
348 OptionScopeSelector::SessionGlobal => &mut self.session_global,
349 OptionScopeSelector::WindowGlobal => &mut self.window_global,
350 OptionScopeSelector::Session(session_name) => {
351 self.sessions.entry(session_name.clone()).or_default()
352 }
353 OptionScopeSelector::Window(target) => self.windows.entry(target.clone()).or_default(),
354 OptionScopeSelector::Pane(target) => self.panes.entry(target.clone()).or_default(),
355 }
356 }
357
358 pub(super) fn node_for_exact_scope(&self, scope: &OptionScopeSelector) -> Option<&OptionNode> {
359 match scope {
360 OptionScopeSelector::ServerGlobal => Some(&self.server_global),
361 OptionScopeSelector::SessionGlobal => Some(&self.session_global),
362 OptionScopeSelector::WindowGlobal => Some(&self.window_global),
363 OptionScopeSelector::Session(session_name) => self.sessions.get(session_name),
364 OptionScopeSelector::Window(target) => self.windows.get(target),
365 OptionScopeSelector::Pane(target) => self.panes.get(target),
366 }
367 }
368
369 pub(super) fn resolve_query<'a>(
370 &'a self,
371 context: &ResolveContext<'a>,
372 query: &OptionQuery,
373 ) -> Option<&'a OptionEntry> {
374 for node in self.chain_for_context(context, query) {
375 if let Some(entry) = node.entry(query.canonical_name()) {
376 if query.index().is_none() || entry.value(query.index()).is_some() {
377 return Some(entry);
378 }
379 }
380 }
381 None
382 }
383
384 pub(super) fn effective_value_for_scope(
385 &self,
386 scope: &OptionScopeSelector,
387 query: &OptionQuery,
388 ) -> Option<String> {
389 match scope {
390 OptionScopeSelector::ServerGlobal => self
391 .resolve_query(&ResolveContext::Server, query)
392 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
393 OptionScopeSelector::SessionGlobal => self
394 .resolve_query(&ResolveContext::SessionGlobal, query)
395 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
396 OptionScopeSelector::WindowGlobal => self
397 .resolve_query(&ResolveContext::WindowGlobal, query)
398 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
399 OptionScopeSelector::Session(session_name) => self
400 .resolve_query(&ResolveContext::Session(session_name), query)
401 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
402 OptionScopeSelector::Window(target) => self
403 .resolve_query(
404 &ResolveContext::Window(target.session_name(), target.window_index()),
405 query,
406 )
407 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
408 OptionScopeSelector::Pane(target) => self
409 .resolve_query(
410 &ResolveContext::Pane(
411 target.session_name(),
412 target.window_index(),
413 target.pane_index(),
414 ),
415 query,
416 )
417 .and_then(|entry| entry.value(query.index()).map(str::to_owned)),
418 }
419 }
420
421 pub(super) fn explicit_value_for_scope(
422 &self,
423 scope: &OptionScopeSelector,
424 query: &OptionQuery,
425 ) -> Option<String> {
426 self.node_for_exact_scope(scope)
427 .and_then(|node| node.value(query.canonical_name(), query.index()))
428 .map(str::to_owned)
429 }
430
431 pub(super) fn default_entry_for_scope(
432 &self,
433 query: &OptionQuery,
434 scope: OptionScopeSelector,
435 ) -> Option<OptionEntry> {
436 let default = query.default_value()?;
437
438 if query.is_array() {
439 let items = default_array_items(query, default).ok()?;
440 Some(OptionEntry::new_array(query, scope, items))
441 } else {
442 let value =
443 normalize_scalar_value(query, Some(default_scalar_text(default)), None).ok()?;
444 Some(OptionEntry::new_scalar(query, scope, value))
445 }
446 }
447
448 pub(super) fn default_value_as_string(&self, query: &OptionQuery) -> Option<String> {
449 let default = query.default_value()?;
450 match default {
451 DefaultValue::Scalar(value) => Some(value.to_owned()),
452 DefaultValue::Array(values) => Some(values.join(query.separator())),
453 }
454 }
455
456 fn node_for_global_root(&self, root: GlobalRoot) -> Option<&OptionNode> {
457 match root {
458 GlobalRoot::Server => Some(&self.server_global),
459 GlobalRoot::Session => Some(&self.session_global),
460 GlobalRoot::Window => Some(&self.window_global),
461 }
462 }
463
464 fn chain_for_context<'a>(
465 &'a self,
466 context: &ResolveContext<'a>,
467 query: &OptionQuery,
468 ) -> Vec<&'a OptionNode> {
469 match context {
470 ResolveContext::Server => vec![&self.server_global],
471 ResolveContext::SessionGlobal => {
474 if query.is_user() {
475 vec![&self.session_global, &self.server_global]
476 } else {
477 vec![&self.session_global]
478 }
479 }
480 ResolveContext::WindowGlobal => {
481 if query.is_user() {
482 vec![
483 &self.window_global,
484 &self.session_global,
485 &self.server_global,
486 ]
487 } else {
488 vec![&self.window_global]
489 }
490 }
491 ResolveContext::Session(session_name) => {
492 let mut chain = Vec::new();
493 if scope_allows_session(query) {
494 if let Some(node) = self.sessions.get(*session_name) {
495 chain.push(node);
496 }
497 }
498 push_known_global_roots(&mut chain, self, query);
499 chain
500 }
501 ResolveContext::Window(session_name, window_index) => {
502 let mut chain = Vec::new();
503 if scope_allows_window(query) {
504 let target = WindowTarget::with_window((*session_name).clone(), *window_index);
505 if let Some(node) = self.windows.get(&target) {
506 chain.push(node);
507 }
508 }
509 if scope_allows_session(query) {
510 if let Some(node) = self.sessions.get(*session_name) {
511 chain.push(node);
512 }
513 }
514 push_known_global_roots(&mut chain, self, query);
515 chain
516 }
517 ResolveContext::Pane(session_name, window_index, pane_index) => {
518 let mut chain = Vec::new();
519 if scope_allows_pane(query) {
520 let pane_target = PaneTarget::with_window(
521 (*session_name).clone(),
522 *window_index,
523 *pane_index,
524 );
525 if let Some(node) = self.panes.get(&pane_target) {
526 chain.push(node);
527 }
528 }
529 if scope_allows_window(query) {
530 let window_target =
531 WindowTarget::with_window((*session_name).clone(), *window_index);
532 if let Some(node) = self.windows.get(&window_target) {
533 chain.push(node);
534 }
535 }
536 if scope_allows_session(query) {
537 if let Some(node) = self.sessions.get(*session_name) {
538 chain.push(node);
539 }
540 }
541 push_known_global_roots(&mut chain, self, query);
542 chain
543 }
544 }
545 }
546
547 fn default_rendered_value(&self, query: &OptionQuery) -> Option<&'static str> {
548 if query.index().is_some() {
549 return None;
550 }
551 match query.default_value()? {
552 DefaultValue::Scalar(value) => Some(value),
553 DefaultValue::Array(_) => None,
557 }
558 }
559
560 fn resolve_name_from_nodes<'a, I>(&self, query: &OptionQuery, nodes: I) -> Option<String>
561 where
562 I: IntoIterator<Item = Option<&'a OptionNode>>,
563 {
564 for node in nodes.into_iter().flatten() {
565 if let Some(entry) = node.entry(query.canonical_name()) {
566 if let Some(value) = entry.value(query.index()) {
567 return Some(value.to_owned());
568 }
569 }
570 }
571 None
572 }
573}