rusticity_term/lambda/
functions.rs1use crate::app::App;
2use crate::common::{CyclicEnum, InputFocus};
3use crate::keymap::Mode;
4use crate::ui::lambda::{filtered_lambda_functions, DetailTab as LambdaDetailTab, FILTER_CONTROLS};
5use crate::ui::monitoring::MonitoringState;
6
7pub fn next_item(app: &mut App) {
10 if app.lambda_state.current_function.is_some()
11 && app.lambda_state.detail_tab == LambdaDetailTab::Code
12 {
13 if let Some(func_name) = &app.lambda_state.current_function.clone() {
14 if let Some(func) = app
15 .lambda_state
16 .table
17 .items
18 .iter()
19 .find(|f| f.name == *func_name)
20 {
21 let max = func.layers.len().saturating_sub(1);
22 if !func.layers.is_empty() {
23 app.lambda_state.layer_selected =
24 (app.lambda_state.layer_selected + 1).min(max);
25 }
26 }
27 }
28 } else if app.lambda_state.current_function.is_some()
29 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
30 {
31 let filtered: Vec<_> = app
32 .lambda_state
33 .version_table
34 .items
35 .iter()
36 .filter(|v| {
37 app.lambda_state.version_table.filter.is_empty()
38 || v.version
39 .to_lowercase()
40 .contains(&app.lambda_state.version_table.filter.to_lowercase())
41 || v.aliases
42 .to_lowercase()
43 .contains(&app.lambda_state.version_table.filter.to_lowercase())
44 || v.description
45 .to_lowercase()
46 .contains(&app.lambda_state.version_table.filter.to_lowercase())
47 })
48 .collect();
49 if !filtered.is_empty() {
50 app.lambda_state.version_table.selected =
51 (app.lambda_state.version_table.selected + 1).min(filtered.len() - 1);
52 app.lambda_state.version_table.snap_to_page();
53 }
54 } else if app.lambda_state.current_function.is_some()
55 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
56 || (app.lambda_state.current_version.is_some()
57 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
58 {
59 let version_filter = app.lambda_state.current_version.clone();
60 let filtered: Vec<_> = app
61 .lambda_state
62 .alias_table
63 .items
64 .iter()
65 .filter(|a| {
66 (version_filter.is_none() || a.versions.contains(version_filter.as_ref().unwrap()))
67 && (app.lambda_state.alias_table.filter.is_empty()
68 || a.name
69 .to_lowercase()
70 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
71 || a.versions
72 .to_lowercase()
73 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
74 || a.description
75 .to_lowercase()
76 .contains(&app.lambda_state.alias_table.filter.to_lowercase()))
77 })
78 .collect();
79 if !filtered.is_empty() {
80 app.lambda_state.alias_table.selected =
81 (app.lambda_state.alias_table.selected + 1).min(filtered.len() - 1);
82 app.lambda_state.alias_table.snap_to_page();
83 }
84 } else if app.lambda_state.current_function.is_none() {
85 let filtered = filtered_lambda_functions(app);
86 if !filtered.is_empty() {
87 app.lambda_state.table.next_item(filtered.len());
88 app.lambda_state.table.snap_to_page();
89 }
90 }
91}
92
93pub fn prev_item(app: &mut App) {
94 if app.lambda_state.current_function.is_some()
95 && app.lambda_state.detail_tab == LambdaDetailTab::Code
96 {
97 app.lambda_state.layer_selected = app.lambda_state.layer_selected.saturating_sub(1);
98 } else if app.lambda_state.current_function.is_some()
99 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
100 {
101 app.lambda_state.version_table.prev_item();
102 } else if app.lambda_state.current_function.is_some()
103 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
104 || (app.lambda_state.current_version.is_some()
105 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
106 {
107 app.lambda_state.alias_table.prev_item();
108 } else if app.lambda_state.current_function.is_none() {
109 app.lambda_state.table.prev_item();
110 }
111}
112
113pub fn page_down_filter_input(app: &mut App) {
114 if app.lambda_state.current_function.is_some()
115 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
116 && app.lambda_state.version_input_focus == InputFocus::Pagination
117 {
118 let page_size = app.lambda_state.version_table.page_size.value();
119 let filtered_count: usize = app
120 .lambda_state
121 .version_table
122 .items
123 .iter()
124 .filter(|v| {
125 app.lambda_state.version_table.filter.is_empty()
126 || v.version
127 .to_lowercase()
128 .contains(&app.lambda_state.version_table.filter.to_lowercase())
129 || v.aliases
130 .to_lowercase()
131 .contains(&app.lambda_state.version_table.filter.to_lowercase())
132 || v.description
133 .to_lowercase()
134 .contains(&app.lambda_state.version_table.filter.to_lowercase())
135 })
136 .count();
137 let target = app.lambda_state.version_table.selected + page_size;
138 app.lambda_state.version_table.selected = target.min(filtered_count.saturating_sub(1));
139 } else if app.lambda_state.current_function.is_some()
140 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
141 || (app.lambda_state.current_version.is_some()
142 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
143 && app.lambda_state.alias_input_focus == InputFocus::Pagination
144 {
145 let page_size = app.lambda_state.alias_table.page_size.value();
146 let version_filter = app.lambda_state.current_version.clone();
147 let filtered_count = app
148 .lambda_state
149 .alias_table
150 .items
151 .iter()
152 .filter(|a| {
153 (version_filter.is_none() || a.versions.contains(version_filter.as_ref().unwrap()))
154 && (app.lambda_state.alias_table.filter.is_empty()
155 || a.name
156 .to_lowercase()
157 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
158 || a.versions
159 .to_lowercase()
160 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
161 || a.description
162 .to_lowercase()
163 .contains(&app.lambda_state.alias_table.filter.to_lowercase()))
164 })
165 .count();
166 let target = app.lambda_state.alias_table.selected + page_size;
167 app.lambda_state.alias_table.selected = target.min(filtered_count.saturating_sub(1));
168 } else if app.lambda_state.current_function.is_none() {
169 let page_size = app.lambda_state.table.page_size.value();
170 let filtered_count = filtered_lambda_functions(app).len();
171 app.lambda_state.input_focus.handle_page_down(
172 &mut app.lambda_state.table.selected,
173 &mut app.lambda_state.table.scroll_offset,
174 page_size,
175 filtered_count,
176 );
177 }
178}
179
180pub fn page_down_normal(app: &mut App) {
181 let len = filtered_lambda_functions(app).len();
182 app.lambda_state.table.page_down(len);
183}
184
185pub fn page_up_filter_input(app: &mut App) {
186 if app.lambda_state.current_function.is_some()
187 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
188 && app.lambda_state.version_input_focus == InputFocus::Pagination
189 {
190 let page_size = app.lambda_state.version_table.page_size.value();
191 app.lambda_state.version_table.selected = app
192 .lambda_state
193 .version_table
194 .selected
195 .saturating_sub(page_size);
196 } else if app.lambda_state.current_function.is_some()
197 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
198 || (app.lambda_state.current_version.is_some()
199 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
200 && app.lambda_state.alias_input_focus == InputFocus::Pagination
201 {
202 let page_size = app.lambda_state.alias_table.page_size.value();
203 app.lambda_state.alias_table.selected = app
204 .lambda_state
205 .alias_table
206 .selected
207 .saturating_sub(page_size);
208 } else if app.lambda_state.current_function.is_none() {
209 let page_size = app.lambda_state.table.page_size.value();
210 app.lambda_state.input_focus.handle_page_up(
211 &mut app.lambda_state.table.selected,
212 &mut app.lambda_state.table.scroll_offset,
213 page_size,
214 );
215 }
216}
217
218pub fn page_up_normal(app: &mut App) {
219 app.lambda_state.table.page_up();
220}
221
222pub fn scroll_up(app: &mut App) {
223 if app.lambda_state.current_function.is_some()
224 && app.lambda_state.detail_tab == LambdaDetailTab::Monitor
225 && !app.lambda_state.is_metrics_loading()
226 {
227 app.lambda_state
228 .set_monitoring_scroll(app.lambda_state.monitoring_scroll().saturating_sub(1));
229 }
230}
231
232pub fn scroll_down(app: &mut App) {
233 if app.lambda_state.current_function.is_some()
234 && app.lambda_state.detail_tab == LambdaDetailTab::Monitor
235 && !app.lambda_state.is_metrics_loading()
236 {
237 app.lambda_state
238 .set_monitoring_scroll((app.lambda_state.monitoring_scroll() + 1).min(9));
239 }
240}
241
242pub fn expand_row(app: &mut App) {
243 if app.lambda_state.current_function.is_some()
244 && app.lambda_state.detail_tab == LambdaDetailTab::Code
245 {
246 if app.lambda_state.layer_expanded != Some(app.lambda_state.layer_selected) {
247 app.lambda_state.layer_expanded = Some(app.lambda_state.layer_selected);
248 } else {
249 app.lambda_state.layer_expanded = None;
250 }
251 } else if app.lambda_state.current_function.is_some()
252 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
253 {
254 app.lambda_state.version_table.toggle_expand();
255 } else if app.lambda_state.current_function.is_some()
256 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
257 || (app.lambda_state.current_version.is_some()
258 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
259 {
260 app.lambda_state.alias_table.toggle_expand();
261 } else if app.lambda_state.current_function.is_none() {
262 app.lambda_state.table.expand();
263 }
264}
265
266pub fn collapse_row(app: &mut App) {
267 app.lambda_state.table.collapse();
268}
269
270pub fn prev_pane(app: &mut App) {
271 if app.lambda_state.current_function.is_some()
272 && app.lambda_state.detail_tab == LambdaDetailTab::Code
273 {
274 app.lambda_state.layer_expanded = None;
275 } else if app.lambda_state.current_function.is_some()
276 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
277 {
278 app.lambda_state.version_table.collapse();
279 } else if app.lambda_state.current_function.is_some()
280 && (app.lambda_state.detail_tab == LambdaDetailTab::Aliases
281 || (app.lambda_state.current_version.is_some()
282 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration))
283 {
284 app.lambda_state.alias_table.collapse();
285 } else if app.lambda_state.current_function.is_none() {
286 app.lambda_state.table.collapse();
287 }
288}
289
290pub fn go_to_page(app: &mut App, page: usize) {
291 if app.lambda_state.current_function.is_some()
292 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
293 {
294 let filtered_count = app
295 .lambda_state
296 .version_table
297 .filtered(|v| {
298 app.lambda_state.version_table.filter.is_empty()
299 || v.version
300 .to_lowercase()
301 .contains(&app.lambda_state.version_table.filter.to_lowercase())
302 || v.aliases
303 .to_lowercase()
304 .contains(&app.lambda_state.version_table.filter.to_lowercase())
305 || v.description
306 .to_lowercase()
307 .contains(&app.lambda_state.version_table.filter.to_lowercase())
308 })
309 .len();
310 app.lambda_state
311 .version_table
312 .goto_page(page, filtered_count);
313 } else {
314 let filtered_count = filtered_lambda_functions(app).len();
315 app.lambda_state.table.goto_page(page, filtered_count);
316 }
317}
318
319pub fn apply_filter_reset(app: &mut App) {
322 if app.lambda_state.current_version.is_some() || app.lambda_state.current_function.is_some() {
323 app.lambda_state.version_table.reset();
324 app.lambda_state.alias_table.reset();
325 } else {
326 app.lambda_state.table.reset();
327 }
328}
329
330pub fn start_filter(app: &mut App) {
331 if app.lambda_state.current_version.is_some()
332 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration
333 {
334 app.lambda_state.alias_input_focus = InputFocus::Filter;
335 } else if app.lambda_state.current_function.is_some()
336 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
337 {
338 app.lambda_state.version_input_focus = InputFocus::Filter;
339 } else if app.lambda_state.current_function.is_none() {
340 app.lambda_state.input_focus = InputFocus::Filter;
341 }
342}
343
344pub fn is_pagination_focused(app: &App) -> bool {
345 if app.lambda_state.current_function.is_some()
346 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
347 {
348 app.lambda_state.version_input_focus == InputFocus::Pagination
349 } else if app.lambda_state.current_function.is_none() {
350 app.lambda_state.input_focus == InputFocus::Pagination
351 } else {
352 false
353 }
354}
355
356pub fn next_filter_focus(app: &mut App) {
357 if app.lambda_state.current_version.is_some()
358 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration
359 {
360 app.lambda_state.alias_input_focus =
361 app.lambda_state.alias_input_focus.next(&FILTER_CONTROLS);
362 } else if app.lambda_state.current_function.is_some()
363 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
364 {
365 app.lambda_state.version_input_focus =
366 app.lambda_state.version_input_focus.next(&FILTER_CONTROLS);
367 } else if app.lambda_state.current_function.is_some()
368 && app.lambda_state.detail_tab == LambdaDetailTab::Aliases
369 {
370 app.lambda_state.alias_input_focus =
371 app.lambda_state.alias_input_focus.next(&FILTER_CONTROLS);
372 } else if app.lambda_state.current_function.is_none() {
373 app.lambda_state.input_focus = app.lambda_state.input_focus.next(&FILTER_CONTROLS);
374 }
375}
376
377pub fn prev_filter_focus(app: &mut App) {
378 if app.lambda_state.current_version.is_some()
379 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration
380 {
381 app.lambda_state.alias_input_focus =
382 app.lambda_state.alias_input_focus.prev(&FILTER_CONTROLS);
383 } else if app.lambda_state.current_function.is_some()
384 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
385 {
386 app.lambda_state.version_input_focus =
387 app.lambda_state.version_input_focus.prev(&FILTER_CONTROLS);
388 } else if app.lambda_state.current_function.is_some()
389 && app.lambda_state.detail_tab == LambdaDetailTab::Aliases
390 {
391 app.lambda_state.alias_input_focus =
392 app.lambda_state.alias_input_focus.prev(&FILTER_CONTROLS);
393 } else if app.lambda_state.current_function.is_none() {
394 app.lambda_state.input_focus = app.lambda_state.input_focus.prev(&FILTER_CONTROLS);
395 }
396}
397
398pub fn filter_char_allowed(app: &App) -> bool {
399 if app.lambda_state.current_function.is_none() {
400 app.lambda_state.input_focus == InputFocus::Filter
401 } else if app.lambda_state.current_version.is_some()
402 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration
403 {
404 app.lambda_state.alias_input_focus == InputFocus::Filter
405 } else if app.lambda_state.current_function.is_some()
406 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
407 {
408 app.lambda_state.version_input_focus == InputFocus::Filter
409 } else if app.lambda_state.current_function.is_some()
410 && app.lambda_state.detail_tab == LambdaDetailTab::Aliases
411 {
412 app.lambda_state.alias_input_focus == InputFocus::Filter
413 } else {
414 false
415 }
416}
417
418pub fn select_item(app: &mut App) {
421 if app.lambda_state.current_function.is_some()
422 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
423 {
424 if app.mode == Mode::Normal {
425 let page_size = app.lambda_state.version_table.page_size.value();
426 let filtered: Vec<_> = app
427 .lambda_state
428 .version_table
429 .items
430 .iter()
431 .filter(|v| {
432 app.lambda_state.version_table.filter.is_empty()
433 || v.version
434 .to_lowercase()
435 .contains(&app.lambda_state.version_table.filter.to_lowercase())
436 || v.aliases
437 .to_lowercase()
438 .contains(&app.lambda_state.version_table.filter.to_lowercase())
439 })
440 .collect();
441 let current_page = app.lambda_state.version_table.selected / page_size;
442 let start_idx = current_page * page_size;
443 let end_idx = (start_idx + page_size).min(filtered.len());
444 let paginated: Vec<_> = filtered[start_idx..end_idx].to_vec();
445 let page_index = app.lambda_state.version_table.selected % page_size;
446 if let Some(version) = paginated.get(page_index) {
447 app.lambda_state.current_version = Some(version.version.clone());
448 app.lambda_state.detail_tab = LambdaDetailTab::Code;
449 }
450 } else {
451 if app.lambda_state.version_table.expanded_item
452 == Some(app.lambda_state.version_table.selected)
453 {
454 app.lambda_state.version_table.collapse();
455 } else {
456 app.lambda_state.version_table.expanded_item =
457 Some(app.lambda_state.version_table.selected);
458 }
459 }
460 } else if app.lambda_state.current_function.is_some()
461 && app.lambda_state.detail_tab == LambdaDetailTab::Aliases
462 {
463 let filtered: Vec<_> = app
464 .lambda_state
465 .alias_table
466 .items
467 .iter()
468 .filter(|a| {
469 app.lambda_state.alias_table.filter.is_empty()
470 || a.name
471 .to_lowercase()
472 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
473 || a.versions
474 .to_lowercase()
475 .contains(&app.lambda_state.alias_table.filter.to_lowercase())
476 })
477 .collect();
478 if let Some(alias) = app.lambda_state.alias_table.get_selected(&filtered) {
479 app.lambda_state.current_alias = Some(alias.name.clone());
480 }
481 } else if app.lambda_state.current_function.is_none() {
482 let filtered = filtered_lambda_functions(app);
483 if let Some(func) = app.lambda_state.table.get_selected(&filtered) {
484 app.lambda_state.current_function = Some(func.name.clone());
485 app.lambda_state.detail_tab = LambdaDetailTab::Code;
486 app.update_current_tab_breadcrumb();
487 }
488 }
489}
490
491pub fn go_back(app: &mut App) {
492 if app.lambda_state.current_version.is_some() {
493 app.lambda_state.current_version = None;
494 app.lambda_state.detail_tab = LambdaDetailTab::Versions;
495 } else if app.lambda_state.current_alias.is_some() {
496 app.lambda_state.current_alias = None;
497 app.lambda_state.detail_tab = LambdaDetailTab::Aliases;
498 } else if app.lambda_state.current_function.is_some() {
499 app.lambda_state.current_function = None;
500 app.update_current_tab_breadcrumb();
501 }
502}
503
504pub fn next_detail_tab(app: &mut App) {
505 if app.lambda_state.current_version.is_some() {
506 app.lambda_state.version_detail_tab = app.lambda_state.version_detail_tab.next();
507 app.lambda_state.detail_tab = app.lambda_state.version_detail_tab.to_detail_tab();
508 if app.lambda_state.detail_tab == LambdaDetailTab::Monitor {
509 app.lambda_state.set_metrics_loading(true);
510 app.lambda_state.set_monitoring_scroll(0);
511 app.lambda_state.clear_metrics();
512 }
513 } else {
514 app.lambda_state.detail_tab = app.lambda_state.detail_tab.next();
515 if app.lambda_state.detail_tab == LambdaDetailTab::Monitor {
516 app.lambda_state.set_metrics_loading(true);
517 app.lambda_state.set_monitoring_scroll(0);
518 app.lambda_state.clear_metrics();
519 }
520 }
521}
522
523pub fn prev_detail_tab(app: &mut App) {
524 if app.lambda_state.current_version.is_some() {
525 app.lambda_state.version_detail_tab = app.lambda_state.version_detail_tab.prev();
526 app.lambda_state.detail_tab = app.lambda_state.version_detail_tab.to_detail_tab();
527 if app.lambda_state.detail_tab == LambdaDetailTab::Monitor {
528 app.lambda_state.set_metrics_loading(true);
529 app.lambda_state.set_monitoring_scroll(0);
530 app.lambda_state.clear_metrics();
531 }
532 } else {
533 app.lambda_state.detail_tab = app.lambda_state.detail_tab.prev();
534 if app.lambda_state.detail_tab == LambdaDetailTab::Monitor {
535 app.lambda_state.set_metrics_loading(true);
536 app.lambda_state.set_monitoring_scroll(0);
537 app.lambda_state.clear_metrics();
538 }
539 }
540}
541
542pub fn refresh(app: &mut App) {
543 app.lambda_state.table.loading = true;
544}
545
546pub fn yank(app: &App) {
547 let filtered = filtered_lambda_functions(app);
548 if let Some(func) = app.lambda_state.table.get_selected(&filtered) {
549 app.clipboard.request_text(&func.arn);
550 }
551}
552
553pub fn toggle_column(app: &mut App) {
556 crate::preferences::bindings::activate_for(app, crate::app::Service::LambdaFunctions);
557}
558
559pub fn next_preferences(app: &mut App) {
560 crate::preferences::bindings::move_section_for(app, crate::app::Service::LambdaFunctions, true);
561}
562
563pub fn prev_preferences(app: &mut App) {
564 crate::preferences::bindings::move_section_for(
565 app,
566 crate::app::Service::LambdaFunctions,
567 false,
568 );
569}
570
571pub fn column_selector_max(app: &App) -> usize {
572 crate::preferences::bindings::model_for(app, crate::app::Service::LambdaFunctions).max_index()
573}
574
575pub fn column_count(app: &App) -> usize {
576 crate::preferences::bindings::model_for(app, crate::app::Service::LambdaFunctions)
577 .column_count()
578}
579
580pub fn breadcrumb(app: &App) -> Vec<String> {
583 let mut parts = vec!["Lambda".to_string()];
584 if let Some(func) = &app.lambda_state.current_function {
585 parts.push(func.clone());
586 } else {
587 parts.push("Functions".to_string());
588 }
589 parts
590}
591
592pub fn console_url(app: &App) -> String {
593 use crate::lambda;
594 if let Some(func_name) = &app.lambda_state.current_function {
595 if let Some(version) = &app.lambda_state.current_version {
596 lambda::console_url_function_version(
597 &app.config.region,
598 func_name,
599 version,
600 &app.lambda_state.detail_tab,
601 )
602 } else {
603 lambda::console_url_function_detail(&app.config.region, func_name)
604 }
605 } else {
606 lambda::console_url_functions(&app.config.region)
607 }
608}
609
610pub fn get_active_filter_mut(app: &mut App) -> Option<&mut String> {
611 if app.lambda_state.current_version.is_some()
612 && app.lambda_state.detail_tab == LambdaDetailTab::Configuration
613 {
614 Some(&mut app.lambda_state.alias_table.filter)
615 } else if app.lambda_state.current_function.is_some()
616 && app.lambda_state.detail_tab == LambdaDetailTab::Versions
617 {
618 Some(&mut app.lambda_state.version_table.filter)
619 } else if app.lambda_state.current_function.is_some()
620 && app.lambda_state.detail_tab == LambdaDetailTab::Aliases
621 {
622 Some(&mut app.lambda_state.alias_table.filter)
623 } else {
624 Some(&mut app.lambda_state.table.filter)
625 }
626}
627
628#[cfg(test)]
629mod filter_input_regressions {
630 use super::*;
631 use crate::app::Service;
632 use crate::keymap::{handle_key, Action};
633 use crate::lambda::Function;
634 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
635
636 fn press(app: &mut App, code: KeyCode) {
637 let action = handle_key(KeyEvent::new(code, KeyModifiers::NONE), app.mode).unwrap();
638 app.handle_action(action);
639 }
640
641 fn function(name: &str) -> Function {
642 Function {
643 name: name.into(),
644 arn: String::new(),
645 application: None,
646 description: String::new(),
647 package_type: "Zip".into(),
648 runtime: String::new(),
649 architecture: String::new(),
650 code_size: 0,
651 code_sha256: String::new(),
652 memory_mb: 128,
653 timeout_seconds: 30,
654 last_modified: String::new(),
655 layers: vec![],
656 }
657 }
658
659 #[test]
660 fn lambda_list_filter_accepts_typing_and_backspace_through_keymap() {
661 let mut app = App::new_without_client("test".into(), Some("us-east-1".into()));
662 app.current_service = Service::LambdaFunctions;
663 app.service_selected = true;
664 app.mode = Mode::Normal;
665 app.lambda_state.table.items = vec![
666 function("BETA-GrafanaWorkspace-Provisioner"),
667 function("BETA-SagemakerHyperpodEks-FsxDraKmsGrantGrantFnDEE-test"),
668 ];
669 app.lambda_state.table.selected = 1;
670 app.lambda_state.table.scroll_offset = 50;
671 app.lambda_state.table.expanded_item = Some(1);
672 app.lambda_state.input_focus = InputFocus::Pagination;
673 press(&mut app, KeyCode::Char('i'));
674 assert_eq!(app.mode, Mode::FilterInput);
675 assert_eq!(app.lambda_state.input_focus, InputFocus::Filter);
676 for c in "Fsx".chars() {
677 press(&mut app, KeyCode::Char(c));
678 }
679 assert_eq!(app.lambda_state.table.filter, "Fsx");
680 assert_eq!(filtered_lambda_functions(&app).len(), 1);
681 assert!(filtered_lambda_functions(&app)[0]
682 .name
683 .contains("FsxDraKmsGrant"));
684 assert_eq!(app.lambda_state.table.selected, 0);
685 assert_eq!(app.lambda_state.table.scroll_offset, 0);
686 assert_eq!(app.lambda_state.table.expanded_item, None);
687 press(&mut app, KeyCode::Backspace);
688 assert_eq!(app.lambda_state.table.filter, "Fs");
689 app.handle_action(Action::DeleteWord);
690 assert!(app.lambda_state.table.filter.is_empty());
691 assert_eq!(filtered_lambda_functions(&app).len(), 2);
692 press(&mut app, KeyCode::Enter);
693 assert_eq!(app.mode, Mode::Normal);
694 }
695
696 #[test]
697 fn lambda_list_pagination_focus_does_not_accept_filter_text() {
698 let mut app = App::new_without_client("test".into(), None);
699 app.current_service = Service::LambdaFunctions;
700 app.service_selected = true;
701 app.mode = Mode::Normal;
702 press(&mut app, KeyCode::Char('i'));
703 press(&mut app, KeyCode::Char('F'));
704 press(&mut app, KeyCode::Tab);
705 assert_eq!(app.lambda_state.input_focus, InputFocus::Pagination);
706 press(&mut app, KeyCode::Char('s'));
707 press(&mut app, KeyCode::Char('2'));
708 assert_eq!(app.lambda_state.table.filter, "F");
709 assert_eq!(app.page_input, "2");
710 press(&mut app, KeyCode::BackTab);
711 press(&mut app, KeyCode::Char('s'));
712 assert_eq!(app.lambda_state.table.filter, "Fs");
713 }
714
715 #[test]
716 fn lambda_detail_filter_permission_stays_scoped_to_version_and_alias_controls() {
717 let mut app = App::new_without_client("test".into(), None);
718 app.lambda_state.current_function = Some("fn".into());
719 for tab in [LambdaDetailTab::Versions, LambdaDetailTab::Aliases] {
720 app.lambda_state.detail_tab = tab;
721 app.lambda_state.version_input_focus = InputFocus::Filter;
722 app.lambda_state.alias_input_focus = InputFocus::Filter;
723 assert!(filter_char_allowed(&app));
724 app.lambda_state.version_input_focus = InputFocus::Pagination;
725 app.lambda_state.alias_input_focus = InputFocus::Pagination;
726 assert!(!filter_char_allowed(&app));
727 }
728 app.lambda_state.detail_tab = LambdaDetailTab::Code;
729 app.lambda_state.input_focus = InputFocus::Filter;
730 assert!(!filter_char_allowed(&app));
731 }
732}