Skip to main content

rusticity_term/
inventory.rs

1//! Shared input handling for flat, read-only inventories (RDS, VPC).
2//! Service adapters provide state and the same filtered count used by rendering.
3use crate::clipboard::ClipboardState;
4use crate::common::InputFocus;
5use crate::keymap::{Action, Mode};
6use crate::table::TableState;
7
8const FILTER_CONTROLS: [InputFocus; 2] = [InputFocus::Filter, InputFocus::Pagination];
9
10pub struct InventoryInput<'a, T> {
11    pub table: &'a mut TableState<T>,
12    pub focus: &'a mut InputFocus,
13    pub mode: &'a mut Mode,
14    pub page_input: &'a mut String,
15    pub error_message: &'a mut Option<String>,
16    pub error_scroll: &'a mut usize,
17    pub filtered_count: usize,
18    pub selected_arn: Option<String>,
19    pub clipboard: &'a ClipboardState,
20}
21
22pub fn go_to_page<T>(table: &mut TableState<T>, page: usize, count: usize) {
23    if page == 0 {
24        return;
25    }
26    let pages = table.total_pages(count).max(1);
27    table.goto_page(page.min(pages), count);
28    table.collapse();
29}
30
31pub fn handle_action<T>(input: InventoryInput<'_, T>, action: &Action) -> bool {
32    let InventoryInput {
33        table,
34        focus,
35        mode,
36        page_input,
37        error_message,
38        error_scroll,
39        filtered_count,
40        selected_arn,
41        clipboard,
42    } = input;
43    if *mode == Mode::ErrorModal && *action == Action::RetryLoad {
44        *error_message = None;
45        *error_scroll = 0;
46        *mode = Mode::Normal;
47        table.loading = true;
48        return true;
49    }
50    if *mode == Mode::Normal {
51        match action {
52            Action::NextItem => table.next_item(filtered_count),
53            Action::PrevItem => table.prev_item(),
54            Action::PageDown | Action::ScrollDown => {
55                table.page_down(filtered_count);
56                table.collapse();
57            }
58            Action::PageUp | Action::ScrollUp => {
59                table.page_up();
60                table.collapse();
61            }
62            Action::Select | Action::ExpandRow | Action::NextPane => {
63                if table.selected < filtered_count {
64                    table.toggle_expand();
65                }
66            }
67            Action::CollapseRow | Action::PrevPane | Action::GoBack => table.collapse(),
68            Action::StartFilter => {
69                *mode = Mode::FilterInput;
70                *focus = InputFocus::Filter;
71            }
72            Action::Refresh => table.loading = true,
73            Action::Yank => {
74                if let Some(arn) = selected_arn {
75                    clipboard.request_text(arn);
76                }
77            }
78            // Flat inventory views do not implement tabs or additional sort/filter switches.
79            Action::NextDetailTab
80            | Action::PrevDetailTab
81            | Action::CycleSortColumn
82            | Action::ToggleSortDirection
83            | Action::ToggleExactMatch
84            | Action::ToggleShowExpired => {}
85            _ => return false,
86        }
87        return true;
88    }
89    if *mode == Mode::FilterInput {
90        match action {
91            Action::NextFilterFocus => *focus = focus.next(&FILTER_CONTROLS),
92            Action::PrevFilterFocus => *focus = focus.prev(&FILTER_CONTROLS),
93            Action::PageDown | Action::PageUp => {
94                if *focus == InputFocus::Pagination {
95                    if *action == Action::PageDown {
96                        focus.handle_page_down(
97                            &mut table.selected,
98                            &mut table.scroll_offset,
99                            table.page_size.value(),
100                            filtered_count,
101                        );
102                    } else {
103                        focus.handle_page_up(
104                            &mut table.selected,
105                            &mut table.scroll_offset,
106                            table.page_size.value(),
107                        );
108                    }
109                    table.collapse();
110                }
111            }
112            Action::NextItem | Action::PrevItem => {}
113            Action::FilterInput(c) if *focus == InputFocus::Filter => table.filter_push(*c),
114            Action::FilterBackspace if *focus == InputFocus::Filter => table.filter_pop(),
115            Action::ToggleFilterCheckbox if *focus == InputFocus::Filter => table.filter_push(' '),
116            Action::FilterInput(c) if c.is_ascii_digit() => page_input.push(*c),
117            Action::FilterBackspace => {
118                page_input.pop();
119            }
120            Action::FilterInput(_) | Action::ToggleFilterCheckbox => {}
121            Action::ApplyFilter => {
122                if let Ok(page) = page_input.parse() {
123                    go_to_page(table, page, filtered_count);
124                }
125                page_input.clear();
126                *mode = Mode::Normal;
127            }
128            Action::CloseMenu => *mode = Mode::Normal,
129            _ => return false,
130        }
131        return true;
132    }
133    false
134}