Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check_diff function rewrite #6390

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion check_diff/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions check_diff/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
tempfile = "3"
walkdir = "2.5.0"
diffy = "0.4.0"
99 changes: 63 additions & 36 deletions check_diff/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use diffy;
use std::env;
use std::fs::OpenOptions;
use std::fmt::Debug;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
Expand All @@ -26,6 +27,12 @@ pub enum CheckDiffError {
IO(std::io::Error),
}

impl Debug for CheckDiffError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CheckDiffError").finish()
}
}
benluiwj marked this conversation as resolved.
Show resolved Hide resolved

impl From<io::Error> for CheckDiffError {
fn from(error: io::Error) -> Self {
CheckDiffError::IO(error)
Expand Down Expand Up @@ -59,7 +66,6 @@ impl From<io::Error> for GitError {
}

// will be used in future PRs, just added to make the compiler happy
#[allow(dead_code)]
pub struct CheckDiffRunners {
pub feature_runner: RustfmtRunner,
pub src_runner: RustfmtRunner,
Expand All @@ -70,6 +76,21 @@ pub struct RustfmtRunner {
binary_path: PathBuf,
}

impl CheckDiffRunners {
/// Creates a diff generated by running the source and feature binaries on the same file path
pub fn create_diff(
&self,
path: &Path,
additional_configs: &Option<Vec<String>>,
) -> Result<String, CheckDiffError> {
let code = std::fs::read_to_string(path)?;
let src_format = self.src_runner.format_code(&code, additional_configs)?;
let feature_format = self.feature_runner.format_code(&code, additional_configs)?;
let diff = diffy::create_patch(src_format.as_str(), feature_format.as_str());
Ok(format!("{diff}"))
Comment on lines +85 to +86
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this was in my suggestion when messaging on Zulip. I think this should be fine, but we could also consider returning a diffy::Patch or maybe even our own wrapper type.

It would be really nice if the diff::Patch gave us some way to tell if the patch was empty, but I don't think it does so we probably need to roll our own way to figure that out.

}
}

impl RustfmtRunner {
fn get_binary_version(&self) -> Result<String, CheckDiffError> {
let Ok(command) = Command::new(&self.binary_path)
Expand All @@ -86,19 +107,18 @@ impl RustfmtRunner {
return Ok(binary_version.to_string());
}

// Run rusfmt with the --check flag to see if a diff is produced.
// Run rusfmt with the --check flag to see if a diff is produced. Runs on the code specified
//
// Parameters:
// binary_path: Path to a rustfmt binary
// output_path: Output file path for the diff
// code: Code to run the binary on
// config: Any additional configuration options to pass to rustfmt
//
#[allow(dead_code)]
fn create_diff(
fn format_code<'a>(
&self,
output_path: &Path,
config: Option<Vec<String>>,
) -> Result<(), CheckDiffError> {
code: &'a str,
config: &Option<Vec<String>>,
) -> Result<String, CheckDiffError> {
let config_arg: String = match config {
Some(configs) => {
let mut result = String::new();
Expand All @@ -117,33 +137,18 @@ impl RustfmtRunner {
config_arg.as_str()
);

// walks the "." directory and finds all files with .rs extensions
for entry in WalkDir::new(".").into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {
let file = OpenOptions::new()
.write(true)
.append(true)
.open(output_path)?;
let Ok(_) = Command::new(&self.binary_path)
.env("LD_LIBRARY_PATH", &self.ld_library_path)
.args([
"--unstable-features",
"--skip-children",
"--check",
"--color=always",
config.as_str(),
])
.stdout(file)
.output()
else {
return Err(CheckDiffError::FailedWritingToFile(
"Failed to write to file",
));
};
}
}
Ok(())
let output = Command::new(&self.binary_path)
.env("LD_LIBRARY_PATH", &self.ld_library_path)
.args([
"--unstable-features",
"--skip-children",
"--emit=stdout",
config.as_str(),
code,
])
.output()?;
Comment on lines +136 to +145
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we'll need to tweak this slightly. We want to pass the code to rustfmt via stdin, and in order to do that we probably need to do something similar to rust-analyzer.

Also, I think we could simplify creating the config arg above. We should probably abstract that into a standalone function.


Ok(std::str::from_utf8(&output.stdout)?.to_string())
}
}

Expand Down Expand Up @@ -335,3 +340,25 @@ pub fn compile_rustfmt(
feature_runner,
});
}

pub fn check_diff(config: Option<Vec<String>>, runners: CheckDiffRunners) -> i32 {
let mut errors = 0;
for entry in WalkDir::new(".").into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {
Comment on lines +342 to +344
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we can call WalkDir::new("."). We need to pass the path for the repository that we want to run the check_diff function on. Remember that we're going to clone various 3rd party repos to run the check-diff on them. We should update the function parameters of check_diff to take the path we want to search in.

Also, I still think it would be great if we abstracted the .rs file search into a function that returned an Iterator of Path or PathBuf objects so that check_diff isn't responsible for that logic. Plus it'll be a lot easier to unit test that we're searching for all .rs files within a directory if we break it out.

match runners.create_diff(path, &config) {
Ok(diff) => {
if !diff.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is what I was getting at earlier. We need a convenient way to check if the diff is empty. I haven't worked with the diffy crate before so I don't know what it outputs when there is no diff. Does it still include the diff header like

--- a
+++ b

or is it just an empty string?

It would be great if you could investigate that / create a unit test for that behavior.

eprint!("diff");
errors += 1;
}
}
Err(e) => {
eprintln!("Error creating diff for {:?}: {:?}", path.display(), e);
errors += 1;
}
}
}
}
return errors;
}
12 changes: 8 additions & 4 deletions check_diff/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use check_diff::compile_rustfmt;
use check_diff::{check_diff, compile_rustfmt, CheckDiffError};
use clap::Parser;
use tempfile::Builder;
use tracing::info;
Expand All @@ -19,17 +19,21 @@ struct CliInputs {
rustfmt_config: Option<Vec<String>>,
}

fn main() {
fn main() -> Result<(), CheckDiffError> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_env("CHECK_DIFF_LOG"))
.init();
let args = CliInputs::parse();
let tmp_dir = Builder::new().tempdir_in("").unwrap();
info!("Created tmp_dir {:?}", tmp_dir);
let _ = compile_rustfmt(
let check_diff_runners = compile_rustfmt(
tmp_dir.path(),
args.remote_repo_url,
args.feature_branch,
args.commit_hash,
);
)?;

let _ = check_diff(args.rustfmt_config, check_diff_runners);

Ok(())
}
Loading