log: use structured logging everywhere #30
check.yml
on: push
stable / fmt
9s
nightly / doc
1m 7s
ubuntu / stable / features
1m 11s
Matrix: clippy
Matrix: msrv
Annotations
2 errors and 12 warnings
|
stable / fmt
Process completed with exit code 1.
|
|
ubuntu / 1.74.0
Process completed with exit code 101.
|
|
[clippy] src/lib.rs#L6:
src/lib.rs#L6
warning: unused import: `info`
--> src/lib.rs:6:22
|
6 | use tracing::{debug, info, warn};
| ^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
|
|
[clippy] src/tui/mod.rs#L407:
src/tui/mod.rs#L407
warning: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> src/tui/mod.rs:407:13
|
407 | / match event::read()? {
408 | | Event::Key(key) => match app.mode {
409 | | AppMode::Normal => match key.code {
410 | | KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
... |
614 | | _ => {}
615 | | }
| |_____________^
|
= note: you might want to preserve the comments from inside the `match`
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#single_match
= note: `#[warn(clippy::single_match)]` on by default
help: try
|
407 ~ if let Event::Key(key) = event::read()? { match app.mode {
408 + AppMode::Normal => match key.code {
409 + KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
410 + return Ok(Action::None);
411 + }
412 + KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
413 + // Ctrl+D = drop workspace repo(s) to library
414 + if app.active_section == Section::Workspace
415 + && let Some(node) = app.selected_workspace_node()
416 + {
417 + let repo_paths = node.collect_repo_paths();
418 + if !repo_paths.is_empty() {
419 + return Ok(Action::DropToLibrary(repo_paths));
420 + }
421 + }
422 + }
423 + KeyCode::Right => {
424 + // Right arrow = expand node
425 + app.toggle_expand();
426 + }
427 + KeyCode::Left => {
428 + // Left arrow = collapse node
429 + app.toggle_expand();
430 + }
431 + KeyCode::Char('a') if key.modifiers.contains(KeyModifiers::CONTROL) => {
432 + // Ctrl+A = clone repo dialog
433 + app.mode = AppMode::CloneRepo;
434 + app.clone_repo_input.clear();
435 +
436 + // Fetch suggestions in background (blocking for now)
437 + let mut suggestions = Vec::new();
438 + suggestions.extend(get_github_suggestions());
439 + suggestions.extend(get_gitlab_suggestions());
440 + suggestions.sort();
441 + suggestions.dedup();
442 +
443 + // Filter out repos that already exist in workspace or library
444 + let existing_repos: std::collections::HashSet<String> = app
445 + .get_flattened_workspace()
446 + .iter()
447 + .chain(app.get_flattened_library().iter())
448 + .filter_map(|(node, _, _, _)| {
449 + node.repo_info.as_ref().map(|r| r.display_name.clone())
450 + })
451 + .collect();
452 +
453 + suggestions.retain(|s| {
454 + // Extract the repo name from the suggestion (e.g., "github.com/user/repo" -> "github.com/user/repo")
455 + !existing_repos.contains(s)
456 + });
457 +
458 + app.clone_repo_suggestions = suggestions;
459 +
460 + if !app.clone_repo_suggestions.is_empty() {
461 + app.clone_repo_state.select(Some(0));
462 + }
463 + }
464 + KeyCode::Esc => {
465 + // Return None to exit - cleanup happens in the outer loop
466 + return Ok(Action::None);
467 + }
468 + KeyCode::Tab => {
469 + // Tab switches between workspace and library
470 + match app.active_section {
471 + Section::Workspace => {
472 + let library_items = app.get_flattened_library();
473 + if !library_items.is_empty() {
474 + app.workspace_state.select(None);
475 + app.library_state.select(Some(0));
476 + app.active_section = Section::Library;
477 + }
478 + }
479 + Section::Library => {
480 + let workspace_items = app.get_flattened_workspace();
481 + if !workspace_items.is_empty() {
482 + app.library_state.select(None);
483 + app.workspace_state.select(Some(0));
484 + app.active_section = Section::Workspace;
485 + }
486 + }
487 + }
488 + }
489 + KeyCode::Down => app.next(),
490 + KeyCode::Up => app.previous(),
491 + KeyCode::Enter => {
492 + // Enter on workspace repo = open shell
493 + // Enter on library repo = restore from library
494 + return Ok(match app.active_section {
495 + Section::Workspace => {
496 + if let Some(node) = app.selected_workspace_node() {
497 + if let Some(repo) = node.repo_info {
498 + Action::OpenShell(repo.path.clone())
499 + } else {
500 + // Just a directory node, toggle expand
501 + app.toggle_expand();
502 + Action::None
503 + }
504 + } else {
505 + Action::None
506 + }
507 + }
508 + Section::Library => {
509 + if let Some(node) = app.selected_library_node() {
510 + let repo_paths = node.collect_repo_paths();
511 + if !repo_paths.is_empty() {
512 + Action::RestoreFromLibrary(repo_paths)
513 + } else {
514 + // Just a directory node, toggle expand
515 + app.toggle_expand();
516 + Action::None
517 + }
518 + } else {
519 + Action::None
520 + }
521 + }
522 + });
523 + }
524 + KeyCode::Char(c) => {
525 + app.search_query.push(c);
526 + app.filter_repos();
527 + }
528 + KeyCode::Backspace => {
529 + app.search_query.pop();
530 + app.filter_repos();
531 + }
532 + _ => {}
533 + },
534 + AppMode::CloneRepo => match key.code {
535 + KeyCode::Esc => {
536 + app.mode = AppMode::Normal;
537 + app.clone_repo_input.clear();
538 + }
539 + KeyCode::Enter => {
540 + // Use selected suggestion or manual input
541 + let repo = if let Some(idx) = app.clone_repo_state.selected() {
542 + // Filter suggestions by current input
543 + let filtered: Vec<_> = app
544 + .clone_repo_suggestions
545 + .iter()
546 + .filter(|s| {
547 + s.to_lowercase()
548 + .contains(&app.clone_repo_input.to_lowercase())
549 + })
550 + .collect();
551 +
552 + filtered.get(idx).map(|s| (*s).to_string())
553 + } else {
554 + None
555 + };
556 +
557 + let repo = repo.unwrap_or_else(|| app.clone_repo_input.clone());
558 +
559 + if !repo.is_empty() {
560 + app.mode = AppMode::Normal;
561 + return Ok(Action::CloneRepo(repo));
562 + }
563 + }
564 + KeyCode::Down => {
565 + let filtered: Vec<_> = app
566 + .clone_repo_suggestions
567 + .iter()
568 + .filter(|s| {
569 + s.to_lowercase()
570 + .contains(&app.clone_repo_input.to_lowercase())
571 + })
572 + .collect();
573 +
574 + if !filtered.is_empty() {
575 + let next = match app.clone_repo_state.selected() {
576 + Some(i) if i >= filtered.len() - 1 => 0,
577 + Some(i) => i + 1,
578 + None => 0,
579 + };
580 + app.clone_repo_state.select(Some(next));
581 + }
582 + }
583 + KeyCode::Up => {
584 + let filtered: Vec<_> = app
585 + .clone_repo_suggestions
586 + .iter()
587 + .filter(|s| {
588 + s.to_lowercase()
589 + .contains(&app.clone_repo_input.to_lowercase())
590 + })
591 + .collect();
592 +
593 + if !filtered.is_empty() {
594 + let prev = match app.clone_repo_state.selected() {
595 + Some(0) => filtered.len() - 1,
596 + Some(i) => i - 1,
597 + None => 0,
598 + };
599 + app.clone_repo_state.select(Some(prev));
600 + }
601 + }
602 + KeyCode::Char(c) => {
603 + app.clone_repo_input.push(c);
604 + app.clone_repo_state.select(Some(0));
605 + }
606 + KeyCode::Backspace => {
607 + app.clone_repo_input.pop();
608 + }
609 + _ => {}
610 + },
611 + } }
|
|
|
[clippy] src/tui/mod.rs#L1325:
src/tui/mod.rs#L1325
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1325:5
|
1325 | / if let Ok(output) = std::process::Command::new("gh")
1326 | | .args(["auth", "status", "--active", "--json", "hosts"])
1327 | | .output()
1328 | | && output.status.success()
... |
1337 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#collapsible_if
= note: `#[warn(clippy::collapsible_if)]` on by default
help: collapse nested if block
|
1328 ~ && output.status.success()
1329 ~ && let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
1330 | if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
...
1334 | }
1335 ~ }
|
|
|
[clippy] src/tui/mod.rs#L1330:
src/tui/mod.rs#L1330
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1330:9
|
1330 | / if let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
1331 | | if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | | if let Some(hostname) = hosts.keys().next() {
1333 | | return hostname.clone();
... |
1336 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#collapsible_if
help: collapse nested if block
|
1330 ~ if let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout)
1331 ~ && let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | if let Some(hostname) = hosts.keys().next() {
1333 | return hostname.clone();
1334 | }
1335 ~ }
|
|
|
[clippy] src/tui/mod.rs#L1331:
src/tui/mod.rs#L1331
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1331:13
|
1331 | / if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | | if let Some(hostname) = hosts.keys().next() {
1333 | | return hostname.clone();
1334 | | }
1335 | | }
| |_____________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#collapsible_if
help: collapse nested if block
|
1331 ~ if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object())
1332 ~ && let Some(hostname) = hosts.keys().next() {
1333 | return hostname.clone();
1334 ~ }
|
|
|
[clippy] src/lib.rs#L871:
src/lib.rs#L871
warning: this `if` statement can be collapsed
--> src/lib.rs:871:13
|
871 | / if let Ok(remote) = source_repo.find_remote(remote_name.as_str()) {
872 | | if let Some(url) = remote.url(gix::remote::Direction::Fetch) {
873 | | let remote_url = url.to_bstring().to_string();
874 | | debug!(remote = %remote_name, url = %remote_url, "Restoring remote");
... |
895 | | }
| |_____________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#collapsible_if
help: collapse nested if block
|
871 ~ if let Ok(remote) = source_repo.find_remote(remote_name.as_str())
872 ~ && let Some(url) = remote.url(gix::remote::Direction::Fetch) {
873 | let remote_url = url.to_bstring().to_string();
...
893 | }
894 ~ }
|
|
|
[clippy] src/lib.rs#L6:
src/lib.rs#L6
warning: unused import: `info`
--> src/lib.rs:6:22
|
6 | use tracing::{debug, info, warn};
| ^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
|
|
[clippy] src/tui/mod.rs#L407:
src/tui/mod.rs#L407
warning: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> src/tui/mod.rs:407:13
|
407 | / match event::read()? {
408 | | Event::Key(key) => match app.mode {
409 | | AppMode::Normal => match key.code {
410 | | KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
... |
614 | | _ => {}
615 | | }
| |_____________^
|
= note: you might want to preserve the comments from inside the `match`
= help: for further information visit https://rust-lang.github.io/rust-clippy/beta/index.html#single_match
= note: `#[warn(clippy::single_match)]` on by default
help: try
|
407 ~ if let Event::Key(key) = event::read()? { match app.mode {
408 + AppMode::Normal => match key.code {
409 + KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
410 + return Ok(Action::None);
411 + }
412 + KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
413 + // Ctrl+D = drop workspace repo(s) to library
414 + if app.active_section == Section::Workspace
415 + && let Some(node) = app.selected_workspace_node()
416 + {
417 + let repo_paths = node.collect_repo_paths();
418 + if !repo_paths.is_empty() {
419 + return Ok(Action::DropToLibrary(repo_paths));
420 + }
421 + }
422 + }
423 + KeyCode::Right => {
424 + // Right arrow = expand node
425 + app.toggle_expand();
426 + }
427 + KeyCode::Left => {
428 + // Left arrow = collapse node
429 + app.toggle_expand();
430 + }
431 + KeyCode::Char('a') if key.modifiers.contains(KeyModifiers::CONTROL) => {
432 + // Ctrl+A = clone repo dialog
433 + app.mode = AppMode::CloneRepo;
434 + app.clone_repo_input.clear();
435 +
436 + // Fetch suggestions in background (blocking for now)
437 + let mut suggestions = Vec::new();
438 + suggestions.extend(get_github_suggestions());
439 + suggestions.extend(get_gitlab_suggestions());
440 + suggestions.sort();
441 + suggestions.dedup();
442 +
443 + // Filter out repos that already exist in workspace or library
444 + let existing_repos: std::collections::HashSet<String> = app
445 + .get_flattened_workspace()
446 + .iter()
447 + .chain(app.get_flattened_library().iter())
448 + .filter_map(|(node, _, _, _)| {
449 + node.repo_info.as_ref().map(|r| r.display_name.clone())
450 + })
451 + .collect();
452 +
453 + suggestions.retain(|s| {
454 + // Extract the repo name from the suggestion (e.g., "github.com/user/repo" -> "github.com/user/repo")
455 + !existing_repos.contains(s)
456 + });
457 +
458 + app.clone_repo_suggestions = suggestions;
459 +
460 + if !app.clone_repo_suggestions.is_empty() {
461 + app.clone_repo_state.select(Some(0));
462 + }
463 + }
464 + KeyCode::Esc => {
465 + // Return None to exit - cleanup happens in the outer loop
466 + return Ok(Action::None);
467 + }
468 + KeyCode::Tab => {
469 + // Tab switches between workspace and library
470 + match app.active_section {
471 + Section::Workspace => {
472 + let library_items = app.get_flattened_library();
473 + if !library_items.is_empty() {
474 + app.workspace_state.select(None);
475 + app.library_state.select(Some(0));
476 + app.active_section = Section::Library;
477 + }
478 + }
479 + Section::Library => {
480 + let workspace_items = app.get_flattened_workspace();
481 + if !workspace_items.is_empty() {
482 + app.library_state.select(None);
483 + app.workspace_state.select(Some(0));
484 + app.active_section = Section::Workspace;
485 + }
486 + }
487 + }
488 + }
489 + KeyCode::Down => app.next(),
490 + KeyCode::Up => app.previous(),
491 + KeyCode::Enter => {
492 + // Enter on workspace repo = open shell
493 + // Enter on library repo = restore from library
494 + return Ok(match app.active_section {
495 + Section::Workspace => {
496 + if let Some(node) = app.selected_workspace_node() {
497 + if let Some(repo) = node.repo_info {
498 + Action::OpenShell(repo.path.clone())
499 + } else {
500 + // Just a directory node, toggle expand
501 + app.toggle_expand();
502 + Action::None
503 + }
504 + } else {
505 + Action::None
506 + }
507 + }
508 + Section::Library => {
509 + if let Some(node) = app.selected_library_node() {
510 + let repo_paths = node.collect_repo_paths();
511 + if !repo_paths.is_empty() {
512 + Action::RestoreFromLibrary(repo_paths)
513 + } else {
514 + // Just a directory node, toggle expand
515 + app.toggle_expand();
516 + Action::None
517 + }
518 + } else {
519 + Action::None
520 + }
521 + }
522 + });
523 + }
524 + KeyCode::Char(c) => {
525 + app.search_query.push(c);
526 + app.filter_repos();
527 + }
528 + KeyCode::Backspace => {
529 + app.search_query.pop();
530 + app.filter_repos();
531 + }
532 + _ => {}
533 + },
534 + AppMode::CloneRepo => match key.code {
535 + KeyCode::Esc => {
536 + app.mode = AppMode::Normal;
537 + app.clone_repo_input.clear();
538 + }
539 + KeyCode::Enter => {
540 + // Use selected suggestion or manual input
541 + let repo = if let Some(idx) = app.clone_repo_state.selected() {
542 + // Filter suggestions by current input
543 + let filtered: Vec<_> = app
544 + .clone_repo_suggestions
545 + .iter()
546 + .filter(|s| {
547 + s.to_lowercase()
548 + .contains(&app.clone_repo_input.to_lowercase())
549 + })
550 + .collect();
551 +
552 + filtered.get(idx).map(|s| (*s).to_string())
553 + } else {
554 + None
555 + };
556 +
557 + let repo = repo.unwrap_or_else(|| app.clone_repo_input.clone());
558 +
559 + if !repo.is_empty() {
560 + app.mode = AppMode::Normal;
561 + return Ok(Action::CloneRepo(repo));
562 + }
563 + }
564 + KeyCode::Down => {
565 + let filtered: Vec<_> = app
566 + .clone_repo_suggestions
567 + .iter()
568 + .filter(|s| {
569 + s.to_lowercase()
570 + .contains(&app.clone_repo_input.to_lowercase())
571 + })
572 + .collect();
573 +
574 + if !filtered.is_empty() {
575 + let next = match app.clone_repo_state.selected() {
576 + Some(i) if i >= filtered.len() - 1 => 0,
577 + Some(i) => i + 1,
578 + None => 0,
579 + };
580 + app.clone_repo_state.select(Some(next));
581 + }
582 + }
583 + KeyCode::Up => {
584 + let filtered: Vec<_> = app
585 + .clone_repo_suggestions
586 + .iter()
587 + .filter(|s| {
588 + s.to_lowercase()
589 + .contains(&app.clone_repo_input.to_lowercase())
590 + })
591 + .collect();
592 +
593 + if !filtered.is_empty() {
594 + let prev = match app.clone_repo_state.selected() {
595 + Some(0) => filtered.len() - 1,
596 + Some(i) => i - 1,
597 + None => 0,
598 + };
599 + app.clone_repo_state.select(Some(prev));
600 + }
601 + }
602 + KeyCode::Char(c) => {
603 + app.clone_repo_input.push(c);
604 + app.clone_repo_state.select(Some(0));
605 + }
606 + KeyCode::Backspace => {
607 + app.clone_repo_input.pop();
608 + }
609 + _ => {}
610 + },
611 + } }
|
|
|
[clippy] src/tui/mod.rs#L1325:
src/tui/mod.rs#L1325
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1325:5
|
1325 | / if let Ok(output) = std::process::Command::new("gh")
1326 | | .args(["auth", "status", "--active", "--json", "hosts"])
1327 | | .output()
1328 | | && output.status.success()
... |
1337 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/beta/index.html#collapsible_if
= note: `#[warn(clippy::collapsible_if)]` on by default
help: collapse nested if block
|
1328 ~ && output.status.success()
1329 ~ && let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
1330 | if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
...
1334 | }
1335 ~ }
|
|
|
[clippy] src/tui/mod.rs#L1330:
src/tui/mod.rs#L1330
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1330:9
|
1330 | / if let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
1331 | | if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | | if let Some(hostname) = hosts.keys().next() {
1333 | | return hostname.clone();
... |
1336 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/beta/index.html#collapsible_if
help: collapse nested if block
|
1330 ~ if let Ok(json) = serde_json::from_slice::<serde_json::Value>(&output.stdout)
1331 ~ && let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | if let Some(hostname) = hosts.keys().next() {
1333 | return hostname.clone();
1334 | }
1335 ~ }
|
|
|
[clippy] src/tui/mod.rs#L1331:
src/tui/mod.rs#L1331
warning: this `if` statement can be collapsed
--> src/tui/mod.rs:1331:13
|
1331 | / if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object()) {
1332 | | if let Some(hostname) = hosts.keys().next() {
1333 | | return hostname.clone();
1334 | | }
1335 | | }
| |_____________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/beta/index.html#collapsible_if
help: collapse nested if block
|
1331 ~ if let Some(hosts) = json.get("hosts").and_then(|h| h.as_object())
1332 ~ && let Some(hostname) = hosts.keys().next() {
1333 | return hostname.clone();
1334 ~ }
|
|
|
[clippy] src/lib.rs#L871:
src/lib.rs#L871
warning: this `if` statement can be collapsed
--> src/lib.rs:871:13
|
871 | / if let Ok(remote) = source_repo.find_remote(remote_name.as_str()) {
872 | | if let Some(url) = remote.url(gix::remote::Direction::Fetch) {
873 | | let remote_url = url.to_bstring().to_string();
874 | | debug!(remote = %remote_name, url = %remote_url, "Restoring remote");
... |
895 | | }
| |_____________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/beta/index.html#collapsible_if
help: collapse nested if block
|
871 ~ if let Ok(remote) = source_repo.find_remote(remote_name.as_str())
872 ~ && let Some(url) = remote.url(gix::remote::Direction::Fetch) {
873 | let remote_url = url.to_bstring().to_string();
...
893 | }
894 ~ }
|
|