Skip to content

Commit bd5ff58

Browse files
committed
progress
1 parent 60edbaf commit bd5ff58

File tree

19 files changed

+847
-1066
lines changed

19 files changed

+847
-1066
lines changed
Lines changed: 122 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,141 @@
11
use crate::cli_options::CliOptions;
2-
use crate::{CliDiagnostic, Execution, TraversalMode};
3-
use biome_deserialize::Merge;
2+
use crate::commands::get_files_to_process_with_cli_options;
3+
use crate::execute::{StdinPayload, run_files, run_stdin};
4+
use crate::reporter::Report;
5+
use crate::{CliDiagnostic, CliSession, VcsIntegration};
6+
use crate::{ExecutionConfig, ExecutionMode, VcsTargeting};
47
use pgt_configuration::PartialConfiguration;
58
use pgt_console::Console;
9+
use pgt_diagnostics::category;
610
use pgt_fs::FileSystem;
7-
use pgt_workspace::{DynRef, Workspace, WorkspaceError, configuration::LoadedConfiguration};
11+
use pgt_workspace::DynRef;
812
use std::ffi::OsString;
913

10-
use super::{CommandRunner, get_files_to_process_with_cli_options};
14+
#[allow(dead_code)]
15+
pub struct CheckArgs {
16+
pub configuration: Option<PartialConfiguration>,
17+
pub paths: Vec<OsString>,
18+
pub stdin_file_path: Option<String>,
19+
pub staged: bool,
20+
pub changed: bool,
21+
pub since: Option<String>,
22+
pub apply: bool,
23+
pub apply_unsafe: bool,
24+
}
25+
26+
pub fn check(
27+
mut session: CliSession,
28+
cli_options: &CliOptions,
29+
args: CheckArgs,
30+
) -> Result<(), CliDiagnostic> {
31+
validate_args(&args)?;
32+
33+
let configuration = session.prepare_with_config(cli_options, args.configuration.clone())?;
34+
session.setup_workspace(configuration.clone(), VcsIntegration::Enabled)?;
35+
36+
let paths = resolve_paths(session.fs(), &configuration, &args)?;
37+
38+
let vcs = VcsTargeting {
39+
staged: args.staged,
40+
changed: args.changed,
41+
};
42+
43+
let max_diagnostics = if cli_options.reporter.is_default() {
44+
cli_options.max_diagnostics.into()
45+
} else {
46+
u32::MAX
47+
};
48+
49+
let mode = ExecutionMode::Check { vcs };
50+
let execution = ExecutionConfig::new(mode, max_diagnostics);
1151

12-
pub(crate) struct CheckCommandPayload {
13-
pub(crate) configuration: Option<PartialConfiguration>,
14-
pub(crate) paths: Vec<OsString>,
15-
pub(crate) stdin_file_path: Option<String>,
16-
pub(crate) staged: bool,
17-
pub(crate) changed: bool,
18-
pub(crate) since: Option<String>,
52+
if let Some(stdin_path) = args.stdin_file_path.as_deref() {
53+
let payload = read_stdin_payload(stdin_path, session.console())?;
54+
run_stdin(&mut session, &execution, payload)
55+
} else {
56+
let report: Report = run_files(&mut session, &execution, paths)?;
57+
58+
let exit_result = enforce_exit_codes(cli_options, &report);
59+
session.report("check", cli_options, &report)?;
60+
exit_result
61+
}
1962
}
2063

21-
impl CommandRunner for CheckCommandPayload {
22-
const COMMAND_NAME: &'static str = "check";
23-
24-
fn merge_configuration(
25-
&mut self,
26-
loaded_configuration: LoadedConfiguration,
27-
_fs: &DynRef<'_, dyn FileSystem>,
28-
_console: &mut dyn Console,
29-
) -> Result<PartialConfiguration, WorkspaceError> {
30-
let LoadedConfiguration {
31-
configuration: mut fs_configuration,
32-
..
33-
} = loaded_configuration;
34-
35-
if let Some(configuration) = self.configuration.clone() {
36-
// overwrite fs config with cli args
37-
fs_configuration.merge_with(configuration);
64+
fn resolve_paths(
65+
fs: &DynRef<'_, dyn FileSystem>,
66+
configuration: &PartialConfiguration,
67+
args: &CheckArgs,
68+
) -> Result<Vec<OsString>, CliDiagnostic> {
69+
let mut paths = get_files_to_process_with_cli_options(
70+
args.since.as_deref(),
71+
args.changed,
72+
args.staged,
73+
fs,
74+
configuration,
75+
)?
76+
.unwrap_or_else(|| args.paths.clone());
77+
78+
if paths.is_empty() && args.stdin_file_path.is_none() {
79+
if let Some(current_dir) = fs.working_directory() {
80+
paths.push(current_dir.into_os_string());
3881
}
82+
}
3983

40-
Ok(fs_configuration)
84+
Ok(paths)
85+
}
86+
87+
fn read_stdin_payload(
88+
path: &str,
89+
console: &mut dyn Console,
90+
) -> Result<StdinPayload, CliDiagnostic> {
91+
let input_code = console.read();
92+
if let Some(input_code) = input_code {
93+
Ok(StdinPayload {
94+
path: path.into(),
95+
content: input_code,
96+
})
97+
} else {
98+
Err(CliDiagnostic::missing_argument("stdin", "check"))
4199
}
100+
}
101+
102+
fn enforce_exit_codes(cli_options: &CliOptions, payload: &Report) -> Result<(), CliDiagnostic> {
103+
let traversal = payload.traversal.as_ref();
104+
let processed = traversal.map_or(0, |t| t.changed + t.unchanged);
105+
let skipped = traversal.map_or(0, |t| t.skipped);
42106

43-
fn get_files_to_process(
44-
&self,
45-
fs: &DynRef<'_, dyn FileSystem>,
46-
configuration: &PartialConfiguration,
47-
) -> Result<Vec<OsString>, CliDiagnostic> {
48-
let paths = get_files_to_process_with_cli_options(
49-
self.since.as_deref(),
50-
self.changed,
51-
self.staged,
52-
fs,
53-
configuration,
54-
)?
55-
.unwrap_or(self.paths.clone());
56-
57-
Ok(paths)
107+
if processed.saturating_sub(skipped) == 0 && !cli_options.no_errors_on_unmatched {
108+
return Err(CliDiagnostic::no_files_processed());
58109
}
59110

60-
fn get_stdin_file_path(&self) -> Option<&str> {
61-
self.stdin_file_path.as_deref()
111+
let warnings = payload.warnings;
112+
let errors = payload.errors;
113+
let category = category!("check");
114+
115+
if errors > 0 {
116+
return Err(CliDiagnostic::check_error(category));
62117
}
63118

64-
fn get_execution(
65-
&self,
66-
cli_options: &CliOptions,
67-
console: &mut dyn Console,
68-
_workspace: &dyn Workspace,
69-
) -> Result<Execution, CliDiagnostic> {
70-
Ok(Execution::new(TraversalMode::Check {
71-
stdin: self.get_stdin(console)?,
72-
vcs_targeted: (self.staged, self.changed).into(),
73-
})
74-
.set_report(cli_options))
119+
if warnings > 0 && cli_options.error_on_warnings {
120+
return Err(CliDiagnostic::check_warnings(category));
75121
}
122+
123+
Ok(())
124+
}
125+
126+
fn validate_args(args: &CheckArgs) -> Result<(), CliDiagnostic> {
127+
if args.since.is_some() {
128+
if !args.changed {
129+
return Err(CliDiagnostic::incompatible_arguments("since", "changed"));
130+
}
131+
if args.staged {
132+
return Err(CliDiagnostic::incompatible_arguments("since", "staged"));
133+
}
134+
}
135+
136+
if args.changed && args.staged {
137+
return Err(CliDiagnostic::incompatible_arguments("changed", "staged"));
138+
}
139+
140+
Ok(())
76141
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::cli_options::CliOptions;
2+
use crate::reporter::Report;
3+
use crate::{CliDiagnostic, CliSession, VcsIntegration};
4+
use pgt_configuration::PartialConfiguration;
5+
6+
pub fn dblint(
7+
mut session: CliSession,
8+
cli_options: &CliOptions,
9+
cli_configuration: Option<PartialConfiguration>,
10+
) -> Result<(), CliDiagnostic> {
11+
let configuration = session.prepare_with_config(cli_options, cli_configuration)?;
12+
session.setup_workspace(configuration, VcsIntegration::Disabled)?;
13+
14+
// TODO: Implement actual dblint logic here
15+
let report = Report::new(vec![], std::time::Duration::new(0, 0), 0, None);
16+
17+
session.report("dblint", cli_options, &report)
18+
}

crates/pgt_cli/src/commands/mod.rs

Lines changed: 5 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
use crate::changed::{get_changed_files, get_staged_files};
22
use crate::cli_options::{CliOptions, CliReporter, ColorsArg, cli_options};
3-
use crate::execute::Stdin;
43
use crate::logging::LoggingKind;
5-
use crate::{
6-
CliDiagnostic, CliSession, Execution, LoggingLevel, VERSION, execute_mode, setup_cli_subscriber,
7-
};
4+
use crate::{CliDiagnostic, LoggingLevel, VERSION};
85
use bpaf::Bpaf;
96
use pgt_configuration::{PartialConfiguration, partial_configuration};
10-
use pgt_console::{Console, ConsoleExt, markup};
11-
use pgt_fs::{ConfigName, FileSystem};
12-
use pgt_workspace::PartialConfigurationExt;
13-
use pgt_workspace::configuration::{LoadedConfiguration, load_configuration};
14-
use pgt_workspace::workspace::{RegisterProjectFolderParams, UpdateSettingsParams};
15-
use pgt_workspace::{DynRef, Workspace, WorkspaceError};
7+
use pgt_fs::FileSystem;
8+
use pgt_workspace::DynRef;
169
use std::ffi::OsString;
1710
use std::path::PathBuf;
1811
pub(crate) mod check;
1912
pub(crate) mod clean;
2013
pub(crate) mod daemon;
14+
pub(crate) mod dblint;
2115
pub(crate) mod init;
2216
pub(crate) mod version;
2317

@@ -286,145 +280,7 @@ impl PgtCommand {
286280
}
287281
}
288282

289-
/// Generic interface for executing commands.
290-
///
291-
/// Consumers must implement the following methods:
292-
///
293-
/// - [CommandRunner::merge_configuration]
294-
/// - [CommandRunner::get_files_to_process]
295-
/// - [CommandRunner::get_stdin_file_path]
296-
/// - [CommandRunner::should_write]
297-
/// - [CommandRunner::get_execution]
298-
///
299-
/// Optional methods:
300-
/// - [CommandRunner::check_incompatible_arguments]
301-
pub(crate) trait CommandRunner: Sized {
302-
const COMMAND_NAME: &'static str;
303-
304-
/// The main command to use.
305-
fn run(&mut self, session: CliSession, cli_options: &CliOptions) -> Result<(), CliDiagnostic> {
306-
setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
307-
let fs = &session.app.fs;
308-
let console = &mut *session.app.console;
309-
let workspace = &*session.app.workspace;
310-
self.check_incompatible_arguments()?;
311-
let (execution, paths) = self.configure_workspace(fs, console, workspace, cli_options)?;
312-
execute_mode(execution, session, cli_options, paths)
313-
}
314-
315-
/// This function prepares the workspace with the following:
316-
/// - Loading the configuration file.
317-
/// - Configure the VCS integration
318-
/// - Computes the paths to traverse/handle. This changes based on the VCS arguments that were passed.
319-
/// - Register a project folder using the working directory.
320-
/// - Updates the settings that belong to the project registered
321-
fn configure_workspace(
322-
&mut self,
323-
fs: &DynRef<'_, dyn FileSystem>,
324-
console: &mut dyn Console,
325-
workspace: &dyn Workspace,
326-
cli_options: &CliOptions,
327-
) -> Result<(Execution, Vec<OsString>), CliDiagnostic> {
328-
let loaded_configuration =
329-
load_configuration(fs, cli_options.as_configuration_path_hint())?;
330-
331-
// Check for deprecated config filename
332-
if let Some(config_path) = &loaded_configuration.file_path {
333-
if let Some(file_name) = config_path.file_name().and_then(|n| n.to_str()) {
334-
if ConfigName::is_deprecated(file_name) {
335-
console.log(markup! {
336-
<Warn>"Warning: "</Warn>"You are using the deprecated config filename '"<Emphasis>"postgrestools.jsonc"</Emphasis>"'. \
337-
Please rename it to '"<Emphasis>"postgres-language-server.jsonc"</Emphasis>"'. \
338-
Support for the old filename will be removed in a future version.\n"
339-
});
340-
}
341-
}
342-
}
343-
344-
let configuration_path = loaded_configuration.directory_path.clone();
345-
let configuration = self.merge_configuration(loaded_configuration, fs, console)?;
346-
let vcs_base_path = configuration_path.or(fs.working_directory());
347-
let (vcs_base_path, gitignore_matches) =
348-
configuration.retrieve_gitignore_matches(fs, vcs_base_path.as_deref())?;
349-
let paths = self.get_files_to_process(fs, &configuration)?;
350-
workspace.register_project_folder(RegisterProjectFolderParams {
351-
path: fs.working_directory(),
352-
set_as_current_workspace: true,
353-
})?;
354-
355-
workspace.update_settings(UpdateSettingsParams {
356-
workspace_directory: fs.working_directory(),
357-
configuration,
358-
vcs_base_path,
359-
gitignore_matches,
360-
})?;
361-
362-
let execution = self.get_execution(cli_options, console, workspace)?;
363-
Ok((execution, paths))
364-
}
365-
366-
/// Computes [Stdin] if the CLI has the necessary information.
367-
///
368-
/// ## Errors
369-
/// - If the user didn't provide anything via `stdin` but the option `--stdin-file-path` is passed.
370-
fn get_stdin(&self, console: &mut dyn Console) -> Result<Option<Stdin>, CliDiagnostic> {
371-
let stdin = if let Some(stdin_file_path) = self.get_stdin_file_path() {
372-
let input_code = console.read();
373-
if let Some(input_code) = input_code {
374-
let path = PathBuf::from(stdin_file_path);
375-
Some((path, input_code).into())
376-
} else {
377-
// we provided the argument without a piped stdin, we bail
378-
return Err(CliDiagnostic::missing_argument("stdin", Self::COMMAND_NAME));
379-
}
380-
} else {
381-
None
382-
};
383-
384-
Ok(stdin)
385-
}
386-
387-
// Below, the methods that consumers must implement.
388-
389-
/// Implements this method if you need to merge CLI arguments to the loaded configuration.
390-
///
391-
/// The CLI arguments take precedence over the option configured in the configuration file.
392-
fn merge_configuration(
393-
&mut self,
394-
loaded_configuration: LoadedConfiguration,
395-
fs: &DynRef<'_, dyn FileSystem>,
396-
console: &mut dyn Console,
397-
) -> Result<PartialConfiguration, WorkspaceError>;
398-
399-
/// It returns the paths that need to be handled/traversed.
400-
fn get_files_to_process(
401-
&self,
402-
fs: &DynRef<'_, dyn FileSystem>,
403-
configuration: &PartialConfiguration,
404-
) -> Result<Vec<OsString>, CliDiagnostic>;
405-
406-
/// It returns the file path to use in `stdin` mode.
407-
fn get_stdin_file_path(&self) -> Option<&str>;
408-
409-
/// Returns the [Execution] mode.
410-
fn get_execution(
411-
&self,
412-
cli_options: &CliOptions,
413-
console: &mut dyn Console,
414-
workspace: &dyn Workspace,
415-
) -> Result<Execution, CliDiagnostic>;
416-
417-
// Below, methods that consumers can implement
418-
419-
/// Optional method that can be implemented to check if some CLI arguments aren't compatible.
420-
///
421-
/// The method is called before loading the configuration from disk.
422-
fn check_incompatible_arguments(&self) -> Result<(), CliDiagnostic> {
423-
Ok(())
424-
}
425-
}
426-
427-
fn get_files_to_process_with_cli_options(
283+
pub(crate) fn get_files_to_process_with_cli_options(
428284
since: Option<&str>,
429285
changed: bool,
430286
staged: bool,

0 commit comments

Comments
 (0)