Skip to content

Conversation

@psteinroe
Copy link
Collaborator

@psteinroe psteinroe commented Oct 26, 2025

this PR refactors the cli crate. it currently has a bunch of issues that stem from its original implementation in biome, where file/stdin execution is the main use case.

  • a CommandRunner trait that tries to abstract a few common code blocks away.
  • a Dummy traversal mode which essentially means "do not traverse".
  • reports are tightly coupled to execution. we also need to report output of commands that do not traverse (e.g. dblint)
  • execute_mode does everything: stdin handling + traversal + reporting + exit codes. it cannot be re-used by commands that do not traverse

all of these issues cause my brain to have a hard time understanding what is going on. This PR refactors the crate to gear it more towards our use-case. I spent a few cycles designing the refactor, and at least for me it makes much more sense now.

I also added a dummy dblint command that will be implemented in follow-ups. right now, it prints an empty report.

Below is a richer summary of the issues described above.

Current Problems

1. CommandRunner Trait Forces All Commands Through Same Pipeline

pub(crate) trait CommandRunner: Sized {
    fn run(&mut self, session: CliSession, cli_options: &CliOptions) -> Result<()> {
        // Forces: config loading → VCS setup → file traversal → reporting
        let (execution, paths) = self.configure_workspace(fs, console, workspace, cli_options)?;
        execute_mode(execution, session, cli_options, paths)
    }
}

Issues:

  • Only Check command uses this trait
  • Commands like version, clean, init are ad-hoc functions
  • No way for commands to skip parts they don't need (e.g., dblint needs config but not traversal)
  • Forces unnecessary complexity on simple commands

2. TraversalMode::Dummy Exists For Wrong Reasons

pub enum TraversalMode {
    Dummy,  // Only exists because commands are forced through traversal
    Check { stdin: Option<Stdin>, vcs_targeted: VcsTargeted },
}

Commands that don't need traversal shouldn't have a "dummy" mode - they just shouldn't traverse.

3. Execution Struct Conflates Concerns

pub struct Execution {
    report_mode: ReportMode,      // How to report (Terminal/GitHub/GitLab)
    traversal_mode: TraversalMode, // How to process files
    max_diagnostics: u32,
}

Problem: Bundles processing config with reporting config. Commands that don't traverse (like dblint) still need reporting but don't need traversal config.

4. execute_mode() is Monolithic

pub fn execute_mode(execution: Execution, session: CliSession, ...) -> Result<()> {
    // Does: stdin handling + traversal + reporting + exit codes
    // Can't be reused by commands that don't traverse
}

5. Scattered and Inconsistent Structure

  • commands/ has mix of trait impls and functions
  • execute/ has traversal logic tightly coupled to reporting
  • No clear separation of workspace setup, execution, and reporting
  • Commands repeat boilerplate for config loading, setup, reporting

@psteinroe psteinroe marked this pull request as draft October 26, 2025 15:14
@psteinroe psteinroe marked this pull request as ready for review October 28, 2025 08:36
Copy link
Collaborator

@juleswritescode juleswritescode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much simpler! love it.

}
}

/// An entry in the GitLab Code Quality report.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these seem like useful comments?

session.setup_workspace(configuration, VcsIntegration::Disabled)?;

// TODO: Implement actual dblint logic here
let report = Report::new(vec![], std::time::Duration::new(0, 0), 0, None);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is clearly a peak MVP

Comment on lines 22 to 23
pub apply: bool,
pub apply_unsafe: bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these seem to be dead, do we need them?
if not, let's remove the unnecessary complexity 👍🏻

pub trait ReporterVisitor {
/// Writes the summary in the underling writer
fn report_summary(
pub trait ReportWriter {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love that the trait was called "Visitor" before, with a command specifying that it would write

/// The maximum number of diagnostics the console thread is allowed to print
_config: &'ctx ExecutionConfig,
max_diagnostics: u32,
/// The approximate number of diagnostics the console will print before
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sanity check: is the comment regarding "skipped diagnostics" counter useful?

guard: FileGuard<'app, dyn Workspace + 'ctx>,
#[allow(dead_code)]
file: Box<dyn File>,
file: Option<Box<dyn File>>, // only present when backed by a real fs entry
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still confusing to me:

  1. apparently file is never read – should we delete it?
  2. if we want to keep it, how can a WorkspaceFile not be backed by a real fs entry? In case it's a temporary buffer I suppose – but let's add some clarification in a comment 👍🏻

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its for stdin execution - which we currently do not have a real use for since we never write in our commands. this will become used once we have autofix.

@psteinroe psteinroe merged commit 8f7626a into main Oct 28, 2025
8 checks passed
@psteinroe psteinroe deleted the feat/dblint branch October 28, 2025 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants