(
level: Option<String>,
filter: Option<String>,
num: usize,
follow: bool,
)
| 844 | } |
| 845 | |
| 846 | async fn cmd_logs( |
| 847 | level: Option<String>, |
| 848 | filter: Option<String>, |
| 849 | num: usize, |
| 850 | follow: bool, |
| 851 | ) -> Result<(), Box<dyn std::error::Error>> { |
| 852 | tui::print_banner("Logs"); |
| 853 | |
| 854 | let log_path = process::log_file_path(); |
| 855 | |
| 856 | if !log_path.exists() { |
| 857 | tui::print_warning(&format!( |
| 858 | "No logs available. Log file not found at: {}", |
| 859 | log_path.display() |
| 860 | )); |
| 861 | return Ok(()); |
| 862 | } |
| 863 | |
| 864 | let file = std::fs::File::open(&log_path)?; |
| 865 | let reader = BufReader::new(file); |
| 866 | |
| 867 | let lines: Vec<String> = reader |
| 868 | .lines() |
| 869 | .map_while(Result::ok) |
| 870 | .filter(|line| { |
| 871 | if let Some(ref lvl) = level { |
| 872 | let lvl_upper = lvl.to_uppercase(); |
| 873 | line.to_uppercase().contains(&lvl_upper) |
| 874 | } else { |
| 875 | true |
| 876 | } |
| 877 | }) |
| 878 | .filter(|line| { |
| 879 | if let Some(ref keyword) = filter { |
| 880 | line.contains(keyword) |
| 881 | } else { |
| 882 | true |
| 883 | } |
| 884 | }) |
| 885 | .collect(); |
| 886 | |
| 887 | if lines.is_empty() { |
| 888 | tui::print_warning("No matching log entries found."); |
| 889 | return Ok(()); |
| 890 | } |
| 891 | |
| 892 | // Show last `num` lines |
| 893 | let start = if lines.len() > num { |
| 894 | lines.len() - num |
| 895 | } else { |
| 896 | 0 |
| 897 | }; |
| 898 | |
| 899 | for line in &lines[start..] { |
| 900 | println!("{}", line); |
| 901 | } |
| 902 | |
| 903 | if follow { |
no test coverage detected