Skip to content

Commit

Permalink
Store Clippy repository in a Rc<Repository>
Browse files Browse the repository at this point in the history
This is to facilitate an experimental auto-correct feature.
  • Loading branch information
smoelius committed Dec 24, 2024
1 parent b5b657d commit 760f894
Showing 1 changed file with 41 additions and 19 deletions.
60 changes: 41 additions & 19 deletions dylint/src/package_options/revs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use dylint_internal::{
git2::{Commit, ObjectType, Oid, Repository},
};
use if_chain::if_chain;
use std::{cell::RefCell, rc::Rc};
use tempfile::{tempdir, TempDir};

const RUST_CLIPPY_URL: &str = "https://github.com/rust-lang/rust-clippy";
Expand All @@ -17,30 +18,28 @@ pub struct Rev {
}

pub struct Revs {
tempdir: TempDir,
repository: Repository,
repository: Rc<Repository>,
}

pub struct RevIter<'revs> {
revs: &'revs Revs,
repository: Rc<Repository>,
commit: Commit<'revs>,
curr_rev: Option<Rev>,
}

impl Revs {
pub fn new(quiet: bool) -> Result<Self> {
let tempdir = tempdir().with_context(|| "`tempdir` failed")?;

let repository = clone(RUST_CLIPPY_URL, "master", tempdir.path(), quiet)?;
let repository = clippy_repository(quiet)?;

Ok(Self {
tempdir,
repository,
})
Ok(Self { repository })
}

#[allow(clippy::iter_not_returning_iterator)]
pub fn iter(&self) -> Result<RevIter> {
let path = self
.repository
.workdir()
.ok_or_else(|| anyhow!("Could not get working directory"))?;
let object = {
let head = self.repository.head()?;
let oid = head.target().ok_or_else(|| anyhow!("Could not get HEAD"))?;
Expand All @@ -49,11 +48,11 @@ impl Revs {
let commit = object
.as_commit()
.ok_or_else(|| anyhow!("Object is not a commit"))?;
let version = clippy_utils_package_version(self.tempdir.path())?;
let channel = toolchain_channel(self.tempdir.path())?;
let version = clippy_utils_package_version(path)?;
let channel = toolchain_channel(path)?;
let oid = commit.id();
Ok(RevIter {
revs: self,
repository: self.repository.clone(),
commit: commit.clone(),
curr_rev: Some(Rev {
version,
Expand All @@ -79,6 +78,10 @@ impl Iterator for RevIter<'_> {
let curr_rev = if let Some(rev) = self.curr_rev.take() {
rev
} else {
let path = self
.repository
.workdir()
.ok_or_else(|| anyhow!("Could not get working directory"))?;
// smoelius: Note that this is not `git log`'s default behavior. Rather, this
// behavior corresponds to:
// git log --first-parent
Expand All @@ -88,20 +91,18 @@ impl Iterator for RevIter<'_> {
} else {
return Ok(None);
};
self.revs
.repository
self.repository
.checkout_tree(commit.as_object(), None)
.with_context(|| {
format!("`checkout_tree` failed for `{:?}`", commit.as_object())
})?;
self.revs
.repository
self.repository
.set_head_detached(commit.id())
.with_context(|| {
format!("`set_head_detached` failed for `{}`", commit.id())
})?;
let version = clippy_utils_package_version(self.revs.tempdir.path())?;
let channel = toolchain_channel(self.revs.tempdir.path())?;
let version = clippy_utils_package_version(path)?;
let channel = toolchain_channel(path)?;
let oid = commit.id();
Rev {
version,
Expand All @@ -124,6 +125,27 @@ impl Iterator for RevIter<'_> {
}
}

// smoelius: `thread_local!` because `git2::Repository` cannot be shared between threads safely.
thread_local! {
static TMPDIR_AND_REPOSITORY: RefCell<Option<(TempDir, Rc<Repository>)>> = RefCell::new(None);
}

pub fn clippy_repository(quiet: bool) -> Result<Rc<Repository>> {
TMPDIR_AND_REPOSITORY.with_borrow_mut(|cell| {
if let Some((_, repository)) = cell {
return Ok(repository.clone());
}

let tempdir = tempdir().with_context(|| "`tempdir` failed")?;

let repository = clone(RUST_CLIPPY_URL, "master", tempdir.path(), quiet).map(Rc::new)?;

cell.replace((tempdir, repository.clone()));

Ok(repository)
})
}

#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod test {
Expand Down

0 comments on commit 760f894

Please sign in to comment.