-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.rs
More file actions
130 lines (112 loc) · 3.68 KB
/
cli.rs
File metadata and controls
130 lines (112 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use super::error::AnalyzeError;
use crate::cli::InquireInteraction;
use crate::prelude::{
CaptureError, CaptureOpts, DefaultExecutionProvider, ExecutionProvider, OutputDestination,
};
use crate::shared::analyze;
use crate::shared::prelude::FoundConfig;
use anyhow::Result;
use clap::{Args, Subcommand};
use std::env;
use std::io::Cursor;
use std::path::PathBuf;
use tokio::fs::File;
use tokio::io::{BufReader, Stdin};
#[derive(Debug, Args)]
pub struct AnalyzeArgs {
#[clap(subcommand)]
command: AnalyzeCommands,
}
#[derive(Debug, Subcommand)]
enum AnalyzeCommands {
/// Reads a log file and detects errors in it
#[clap(alias("log"))]
Logs(AnalyzeLogsArgs),
/// Runs a command and detects errors in the output
#[clap()]
Command(AnalyzeCommandArgs),
}
#[derive(Debug, Args)]
struct AnalyzeLogsArgs {
/// Location that the logs should be searched, for stdin use '-'
location: String,
}
#[derive(Debug, Args)]
struct AnalyzeCommandArgs {
/// The command to run
#[arg(last = true, required = true)]
command: Vec<String>,
}
pub async fn analyze_root(found_config: &FoundConfig, args: &AnalyzeArgs) -> Result<i32> {
match &args.command {
AnalyzeCommands::Logs(args) => analyze_logs(found_config, args).await,
AnalyzeCommands::Command(args) => analyze_command(found_config, args).await,
}
}
async fn analyze_logs(found_config: &FoundConfig, args: &AnalyzeLogsArgs) -> Result<i32> {
let interaction = InquireInteraction;
let result = match args.location.as_str() {
"-" => {
analyze::process_lines(
&found_config.known_error,
&found_config.working_dir,
read_from_stdin().await?,
&interaction,
)
.await?
}
file_path => {
analyze::process_lines(
&found_config.known_error,
&found_config.working_dir,
read_from_file(file_path).await?,
&interaction,
)
.await?
}
};
analyze::report_result(&result);
Ok(result.to_exit_code())
}
async fn analyze_command(found_config: &FoundConfig, args: &AnalyzeCommandArgs) -> Result<i32> {
let exec_runner = DefaultExecutionProvider::default();
let interaction = InquireInteraction;
let command = args.command.clone();
let path = env::var("PATH").unwrap_or_default();
let capture_opts: CaptureOpts = CaptureOpts {
working_dir: &found_config.working_dir,
env_vars: Default::default(),
path: &path,
args: &command,
output_dest: OutputDestination::StandardOutWithPrefix("analyzing".to_string()),
};
let result = analyze::process_lines(
&found_config.known_error,
&found_config.working_dir,
read_from_command(&exec_runner, capture_opts).await?,
&interaction,
)
.await?;
analyze::report_result(&result);
Ok(result.to_exit_code())
}
async fn read_from_command(
exec_runner: &DefaultExecutionProvider,
capture_opts: CaptureOpts<'_>,
) -> Result<BufReader<Cursor<String>>, CaptureError> {
let output = exec_runner.run_command(capture_opts).await?;
let cursor = Cursor::new(output.generate_user_output());
Ok(BufReader::new(cursor))
}
async fn read_from_stdin() -> Result<BufReader<Stdin>, AnalyzeError> {
Ok(BufReader::new(tokio::io::stdin()))
}
async fn read_from_file(file_name: &str) -> Result<BufReader<File>, AnalyzeError> {
let file_path = PathBuf::from(file_name);
if !file_path.exists() {
return Err(AnalyzeError::FileNotFound {
file_name: file_name.to_string(),
});
}
Ok(BufReader::new(File::open(file_path).await?))
}