Skip to main content

rusticity_term/lambda/
applications.rs

1use crate::app::App;
2use crate::common::{CyclicEnum, InputFocus, PageSize};
3use crate::ui::lambda::{
4    filtered_lambda_applications, ApplicationDetailTab as LambdaApplicationDetailTab,
5    FILTER_CONTROLS,
6};
7
8// ── Navigation ────────────────────────────────────────────────────────────────
9
10pub fn next_item(app: &mut App) {
11    if app.lambda_application_state.current_application.is_some() {
12        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview {
13            let len = app.lambda_application_state.resources.items.len();
14            if len > 0 {
15                app.lambda_application_state.resources.next_item(len);
16            }
17        } else {
18            let len = app.lambda_application_state.deployments.items.len();
19            if len > 0 {
20                app.lambda_application_state.deployments.next_item(len);
21            }
22        }
23    } else {
24        let filtered = filtered_lambda_applications(app);
25        if !filtered.is_empty() {
26            app.lambda_application_state.table.selected =
27                (app.lambda_application_state.table.selected + 1).min(filtered.len() - 1);
28            app.lambda_application_state.table.snap_to_page();
29        }
30    }
31}
32
33pub fn prev_item(app: &mut App) {
34    if app.lambda_application_state.current_application.is_some()
35        && app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview
36    {
37        app.lambda_application_state.resources.selected = app
38            .lambda_application_state
39            .resources
40            .selected
41            .saturating_sub(1);
42    } else if app.lambda_application_state.current_application.is_some()
43        && app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments
44    {
45        app.lambda_application_state.deployments.selected = app
46            .lambda_application_state
47            .deployments
48            .selected
49            .saturating_sub(1);
50    } else {
51        app.lambda_application_state.table.selected = app
52            .lambda_application_state
53            .table
54            .selected
55            .saturating_sub(1);
56        app.lambda_application_state.table.snap_to_page();
57    }
58}
59
60pub fn page_down_filter_input(app: &mut App) {
61    if app.lambda_application_state.current_application.is_some() {
62        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
63            let page_size = app.lambda_application_state.deployments.page_size.value();
64            let filtered_count = app.lambda_application_state.deployments.items.len();
65            app.lambda_application_state
66                .deployment_input_focus
67                .handle_page_down(
68                    &mut app.lambda_application_state.deployments.selected,
69                    &mut app.lambda_application_state.deployments.scroll_offset,
70                    page_size,
71                    filtered_count,
72                );
73        } else {
74            let page_size = app.lambda_application_state.resources.page_size.value();
75            let filtered_count = app.lambda_application_state.resources.items.len();
76            app.lambda_application_state
77                .resource_input_focus
78                .handle_page_down(
79                    &mut app.lambda_application_state.resources.selected,
80                    &mut app.lambda_application_state.resources.scroll_offset,
81                    page_size,
82                    filtered_count,
83                );
84        }
85    } else {
86        let page_size = app.lambda_application_state.table.page_size.value();
87        let filtered_count = filtered_lambda_applications(app).len();
88        app.lambda_application_state.input_focus.handle_page_down(
89            &mut app.lambda_application_state.table.selected,
90            &mut app.lambda_application_state.table.scroll_offset,
91            page_size,
92            filtered_count,
93        );
94    }
95}
96
97pub fn page_down_normal(app: &mut App) {
98    let len = filtered_lambda_applications(app).len();
99    app.lambda_application_state.table.page_down(len);
100}
101
102pub fn page_up_filter_input(app: &mut App) {
103    if app.lambda_application_state.current_application.is_some() {
104        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
105            let page_size = app.lambda_application_state.deployments.page_size.value();
106            app.lambda_application_state
107                .deployment_input_focus
108                .handle_page_up(
109                    &mut app.lambda_application_state.deployments.selected,
110                    &mut app.lambda_application_state.deployments.scroll_offset,
111                    page_size,
112                );
113        } else {
114            let page_size = app.lambda_application_state.resources.page_size.value();
115            app.lambda_application_state
116                .resource_input_focus
117                .handle_page_up(
118                    &mut app.lambda_application_state.resources.selected,
119                    &mut app.lambda_application_state.resources.scroll_offset,
120                    page_size,
121                );
122        }
123    } else {
124        let page_size = app.lambda_application_state.table.page_size.value();
125        app.lambda_application_state.input_focus.handle_page_up(
126            &mut app.lambda_application_state.table.selected,
127            &mut app.lambda_application_state.table.scroll_offset,
128            page_size,
129        );
130    }
131}
132
133pub fn page_up_normal(app: &mut App) {
134    app.lambda_application_state.table.page_up();
135}
136
137pub fn expand_row(app: &mut App) {
138    if app.lambda_application_state.current_application.is_some() {
139        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview {
140            app.lambda_application_state.resources.toggle_expand();
141        } else {
142            app.lambda_application_state.deployments.toggle_expand();
143        }
144    } else {
145        app.lambda_application_state.table.expand();
146    }
147}
148
149pub fn prev_pane(app: &mut App) {
150    if app.lambda_application_state.current_application.is_some() {
151        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview {
152            app.lambda_application_state.resources.collapse();
153        } else {
154            app.lambda_application_state.deployments.collapse();
155        }
156    } else if app.lambda_application_state.table.has_expanded_item() {
157        app.lambda_application_state.table.collapse();
158    }
159}
160
161pub fn go_to_page(app: &mut App, page: usize) {
162    let filtered_count = filtered_lambda_applications(app).len();
163    app.lambda_application_state
164        .table
165        .goto_page(page, filtered_count);
166}
167
168// ── Filter ────────────────────────────────────────────────────────────────────
169
170pub fn apply_filter_reset(app: &mut App) {
171    if app.lambda_application_state.current_application.is_some() {
172        app.lambda_application_state.deployments.reset();
173        app.lambda_application_state.resources.reset();
174    } else {
175        app.lambda_application_state.table.reset();
176    }
177}
178
179pub fn start_filter(app: &mut App) {
180    if app.lambda_application_state.current_application.is_some() {
181        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview {
182            app.lambda_application_state.resource_input_focus = InputFocus::Filter;
183        } else {
184            app.lambda_application_state.deployment_input_focus = InputFocus::Filter;
185        }
186    } else {
187        app.lambda_application_state.input_focus = InputFocus::Filter;
188    }
189}
190
191pub fn is_pagination_focused(app: &App) -> bool {
192    if app.lambda_application_state.current_application.is_some() {
193        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
194            app.lambda_application_state.deployment_input_focus == InputFocus::Pagination
195        } else {
196            app.lambda_application_state.resource_input_focus == InputFocus::Pagination
197        }
198    } else {
199        app.lambda_application_state.input_focus == InputFocus::Pagination
200    }
201}
202
203pub fn is_filter_focused(app: &App) -> bool {
204    if app.lambda_application_state.current_application.is_some() {
205        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
206            app.lambda_application_state.deployment_input_focus == InputFocus::Filter
207        } else {
208            app.lambda_application_state.resource_input_focus == InputFocus::Filter
209        }
210    } else {
211        app.lambda_application_state.input_focus == InputFocus::Filter
212    }
213}
214
215pub fn next_filter_focus(app: &mut App) {
216    if app.lambda_application_state.current_application.is_some() {
217        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
218            app.lambda_application_state.deployment_input_focus = app
219                .lambda_application_state
220                .deployment_input_focus
221                .next(&FILTER_CONTROLS);
222        } else {
223            app.lambda_application_state.resource_input_focus = app
224                .lambda_application_state
225                .resource_input_focus
226                .next(&FILTER_CONTROLS);
227        }
228    } else {
229        app.lambda_application_state.input_focus = app
230            .lambda_application_state
231            .input_focus
232            .next(&FILTER_CONTROLS);
233    }
234}
235
236pub fn prev_filter_focus(app: &mut App) {
237    if app.lambda_application_state.current_application.is_some() {
238        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Deployments {
239            app.lambda_application_state.deployment_input_focus = app
240                .lambda_application_state
241                .deployment_input_focus
242                .prev(&FILTER_CONTROLS);
243        } else {
244            app.lambda_application_state.resource_input_focus = app
245                .lambda_application_state
246                .resource_input_focus
247                .prev(&FILTER_CONTROLS);
248        }
249    } else {
250        app.lambda_application_state.input_focus = app
251            .lambda_application_state
252            .input_focus
253            .prev(&FILTER_CONTROLS);
254    }
255}
256
257// ── Actions ───────────────────────────────────────────────────────────────────
258
259pub fn select_item(app: &mut App) {
260    if app.lambda_application_state.current_application.is_some() {
261        return; // already in detail view, Enter does nothing at the application level
262    }
263    let filtered = filtered_lambda_applications(app);
264    if let Some(app_item) = app.lambda_application_state.table.get_selected(&filtered) {
265        let app_name = app_item.name.clone();
266        app.lambda_application_state.current_application = Some(app_name.clone());
267        app.lambda_application_state.detail_tab = LambdaApplicationDetailTab::Overview;
268
269        use crate::lambda::{Deployment, Resource};
270        app.lambda_application_state.resources.items = vec![
271            Resource {
272                logical_id: "ApiGatewayRestApi".to_string(),
273                physical_id: "abc123xyz".to_string(),
274                resource_type: "AWS::ApiGateway::RestApi".to_string(),
275                last_modified: "2025-01-10 14:30:00 (UTC)".to_string(),
276            },
277            Resource {
278                logical_id: "LambdaFunction".to_string(),
279                physical_id: format!("{}-function", app_name),
280                resource_type: "AWS::Lambda::Function".to_string(),
281                last_modified: "2025-01-10 14:25:00 (UTC)".to_string(),
282            },
283            Resource {
284                logical_id: "DynamoDBTable".to_string(),
285                physical_id: format!("{}-table", app_name),
286                resource_type: "AWS::DynamoDB::Table".to_string(),
287                last_modified: "2025-01-09 10:15:00 (UTC)".to_string(),
288            },
289        ];
290        app.lambda_application_state.deployments.items = vec![Deployment {
291            deployment_id: "d-ABC123XYZ".to_string(),
292            resource_type: "AWS::Serverless::Application".to_string(),
293            last_updated: "2025-01-10 14:30:00 (UTC)".to_string(),
294            status: "Succeeded".to_string(),
295        }];
296        app.update_current_tab_breadcrumb();
297    }
298}
299
300pub fn go_back(app: &mut App) {
301    app.lambda_application_state.current_application = None;
302    app.update_current_tab_breadcrumb();
303}
304
305pub fn next_detail_tab(app: &mut App) {
306    app.lambda_application_state.detail_tab = app.lambda_application_state.detail_tab.next();
307}
308
309pub fn prev_detail_tab(app: &mut App) {
310    app.lambda_application_state.detail_tab = app.lambda_application_state.detail_tab.prev();
311}
312
313pub fn refresh(app: &mut App) {
314    app.lambda_application_state.table.loading = true;
315}
316
317// ── Column preferences ────────────────────────────────────────────────────────
318
319pub fn toggle_column(app: &mut App) {
320    let idx = app.column_selector_index;
321    if app.lambda_application_state.current_application.is_some() {
322        if app.lambda_application_state.detail_tab == LambdaApplicationDetailTab::Overview {
323            if idx > 0 && idx <= app.lambda_resource_column_ids.len() {
324                if let Some(col) = app.lambda_resource_column_ids.get(idx - 1) {
325                    if let Some(pos) = app
326                        .lambda_resource_visible_column_ids
327                        .iter()
328                        .position(|c| c == col)
329                    {
330                        if app.lambda_resource_visible_column_ids.len() > 1 {
331                            app.lambda_resource_visible_column_ids.remove(pos);
332                        }
333                    } else {
334                        app.lambda_resource_visible_column_ids.push(*col);
335                    }
336                }
337            } else if idx == app.lambda_resource_column_ids.len() + 3 {
338                app.lambda_application_state.resources.page_size = PageSize::Ten;
339            } else if idx == app.lambda_resource_column_ids.len() + 4 {
340                app.lambda_application_state.resources.page_size = PageSize::TwentyFive;
341            } else if idx == app.lambda_resource_column_ids.len() + 5 {
342                app.lambda_application_state.resources.page_size = PageSize::Fifty;
343            }
344        } else {
345            if idx > 0 && idx <= app.lambda_deployment_column_ids.len() {
346                if let Some(col) = app.lambda_deployment_column_ids.get(idx - 1) {
347                    if let Some(pos) = app
348                        .lambda_deployment_visible_column_ids
349                        .iter()
350                        .position(|c| c == col)
351                    {
352                        if app.lambda_deployment_visible_column_ids.len() > 1 {
353                            app.lambda_deployment_visible_column_ids.remove(pos);
354                        }
355                    } else {
356                        app.lambda_deployment_visible_column_ids.push(*col);
357                    }
358                }
359            } else if idx == app.lambda_deployment_column_ids.len() + 3 {
360                app.lambda_application_state.deployments.page_size = PageSize::Ten;
361            } else if idx == app.lambda_deployment_column_ids.len() + 4 {
362                app.lambda_application_state.deployments.page_size = PageSize::TwentyFive;
363            } else if idx == app.lambda_deployment_column_ids.len() + 5 {
364                app.lambda_application_state.deployments.page_size = PageSize::Fifty;
365            }
366        }
367    } else {
368        if idx > 0 && idx <= app.lambda_application_column_ids.len() {
369            if let Some(col) = app.lambda_application_column_ids.get(idx - 1) {
370                if let Some(pos) = app
371                    .lambda_application_visible_column_ids
372                    .iter()
373                    .position(|c| *c == *col)
374                {
375                    if app.lambda_application_visible_column_ids.len() > 1 {
376                        app.lambda_application_visible_column_ids.remove(pos);
377                    }
378                } else {
379                    app.lambda_application_visible_column_ids.push(*col);
380                }
381            }
382        } else if idx == app.lambda_application_column_ids.len() + 3 {
383            app.lambda_application_state.table.page_size = PageSize::Ten;
384        } else if idx == app.lambda_application_column_ids.len() + 4 {
385            app.lambda_application_state.table.page_size = PageSize::TwentyFive;
386        } else if idx == app.lambda_application_column_ids.len() + 5 {
387            app.lambda_application_state.table.page_size = PageSize::Fifty;
388        }
389    }
390}
391
392pub fn next_preferences(app: &mut App) {
393    let page_size_idx = app.lambda_application_column_ids.len() + 2;
394    if app.column_selector_index < page_size_idx {
395        app.column_selector_index = page_size_idx;
396    } else {
397        app.column_selector_index = 0;
398    }
399}
400
401pub fn prev_preferences(app: &mut App) {
402    let page_size_idx = app.lambda_application_column_ids.len() + 2;
403    if app.column_selector_index >= page_size_idx {
404        app.column_selector_index = 0;
405    } else {
406        app.column_selector_index = page_size_idx;
407    }
408}
409
410pub fn column_selector_max(app: &App) -> usize {
411    app.lambda_application_column_ids.len() + 5
412}
413
414pub fn column_count(app: &App) -> usize {
415    app.lambda_application_column_ids.len()
416}
417
418// ── Breadcrumb / console URL ──────────────────────────────────────────────────
419
420pub fn breadcrumb() -> Vec<String> {
421    vec!["Lambda".to_string(), "Applications".to_string()]
422}
423
424pub fn console_url(app: &App) -> String {
425    use crate::lambda;
426    if let Some(app_name) = &app.lambda_application_state.current_application {
427        lambda::console_url_application_detail(
428            &app.config.region,
429            app_name,
430            &app.lambda_application_state.detail_tab,
431        )
432    } else {
433        lambda::console_url_applications(&app.config.region)
434    }
435}