Skip to content

Commit

Permalink
chore: reunite .crate handlers into a common module
Browse files Browse the repository at this point in the history
  • Loading branch information
paolobarbolini committed Apr 6, 2024
1 parent 41c1fd5 commit 56473fe
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 85 deletions.
13 changes: 4 additions & 9 deletions src/git.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
use std::{
cmp::Ordering,
collections::BTreeSet,
fs::File,
io::Read,
path::{Path, PathBuf},
process::Command,
str,
};

use anyhow::{ensure, Context as _, Result};
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;
use url::Url;

use crate::package::Package;

#[derive(Debug)]
pub struct GitRepository {
repo_dir: PathBuf,
Expand Down Expand Up @@ -122,7 +120,7 @@ impl<'a> GitRepositoryCheckout<'a> {
default_toolchain: &str,
name: &str,
version: &Version,
) -> Result<Archive<impl Read>> {
) -> Result<Package> {
let package_path = self
.repository
.repo_dir
Expand All @@ -147,10 +145,7 @@ impl<'a> GitRepositoryCheckout<'a> {
);
}

File::open(package_path)
.map(GzDecoder::new)
.map(Archive::new)
.map_err(Into::into)
Ok(Package::new(package_path))
}
}

Expand Down
95 changes: 32 additions & 63 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use std::{
collections::BTreeMap,
env, fs,
io::{self as std_io, BufReader, Read},
io::{self as std_io, Read},
str,
};

use anyhow::{ensure, Context, Result};
use cargo_lock::{package::SourceKind, Checksum, Lockfile};
use cargo_toml::Manifest;
use serde::Deserialize;
use sha2::{Digest as _, Sha256, Sha512};
use sha2::{Digest as _, Sha256};
use url::Url;

use crate::package::{PackageComparison, PackageContents};

use self::git::GitRepository;
use self::io::AsciiWhitespaceSkippingReader;
use self::registry::RegistryCrate;

mod git;
mod io;
mod package;
mod registry;
mod rustup;

Expand Down Expand Up @@ -105,6 +106,7 @@ fn main() -> Result<()> {
continue;
}
};
let registry_crate_package = registry_crate.package();

//
// Verify the package checksum
Expand All @@ -113,7 +115,7 @@ fn main() -> Result<()> {
match package.checksum {
Some(Checksum::Sha256(expected_sha256_hash)) => {
let mut sha256 = Sha256::new();
std_io::copy(&mut registry_crate.raw_crate_file()?, &mut sha256)?;
std_io::copy(&mut registry_crate_package.raw_reader()?, &mut sha256)?;
let sha256 = sha256.finalize();

ensure!(
Expand All @@ -134,7 +136,7 @@ fn main() -> Result<()> {
let mut cargo_vcs_info = None;
let mut cargo_toml = None;

let mut tar = registry_crate.crate_contents()?;
let mut tar = registry_crate_package.archive_reader()?;
for entry in tar.entries()? {
let mut entry = entry?;
let path = entry
Expand Down Expand Up @@ -253,12 +255,12 @@ fn main() -> Result<()> {
// Create local package
//

let mut our_tar_gz = match git_repository_checkout.crate_package(
let repository_package = match git_repository_checkout.crate_package(
&default_toolchain,
package.name.as_str(),
&package.version,
) {
Ok(our_tar_gz) => our_tar_gz,
Ok(repository_package) => repository_package,
Err(err) => {
println!(
"Couldn't package {} v{} err={:?}",
Expand All @@ -272,76 +274,43 @@ fn main() -> Result<()> {
// Hash file contents
//

let mut crates_io_tar = registry_crate.crate_contents()?;
let mut crates_io_hashes = BTreeMap::new();
for file in crates_io_tar.entries()? {
let file = file?;
let path = file.path()?.into_owned();
if path.ends_with(".cargo_vcs_info.json") {
continue;
}
// TODO: remove this
if path.ends_with("Cargo.toml") || path.ends_with("Cargo.toml.orig") {
continue;
}

let mut reader = AsciiWhitespaceSkippingReader::new(BufReader::new(file));

let mut sha512 = Sha512::new();
std_io::copy(&mut reader, &mut sha512)?;
crates_io_hashes.insert(path, sha512.finalize());
}

let mut our_hashes = BTreeMap::new();
for file in our_tar_gz.entries()? {
let file = file?;
let path = file.path()?.into_owned();
if path.ends_with(".cargo_vcs_info.json") {
continue;
}
// TODO: remove this
if path.ends_with("Cargo.toml") || path.ends_with("Cargo.toml.orig") {
continue;
}

let mut reader = AsciiWhitespaceSkippingReader::new(BufReader::new(file));

let mut sha512 = Sha512::new();
std_io::copy(&mut reader, &mut sha512)?;
our_hashes.insert(path, sha512.finalize());
}
let repository_package_contents = repository_package
.contents()
.context("calculate repository package contents")?;
let registry_package_contents = registry_crate_package
.contents()
.context("calculate registry crate package contents")?;

//
// Compare hashes
//

for (our_filename, our_sha512_hash) in &our_hashes {
match crates_io_hashes.get(our_filename) {
Some(crates_io_sha512) if our_sha512_hash == crates_io_sha512 => {}
Some(_) => {
let comparison =
PackageContents::compare(&repository_package_contents, &registry_package_contents);
for outcome in comparison {
match outcome {
PackageComparison::Equal(_) => continue,
PackageComparison::Different(path) => {
println!(
"Package {} has mismatching file hashes for {}",
package.name,
our_filename.display()
path.display()
);
}
None => {
PackageComparison::OnlyLeft(path) => {
println!(
"Package {} has file {} in our release but not in crates.io tarball",
package.name,
our_filename.display()
path.display()
);
}
PackageComparison::OnlyRight(path) => {
println!(
"Package {} has file {} in crates.io release but not ours",
package.name,
path.display()
);
}
}
}

for crates_io_filename in crates_io_hashes.keys() {
if !our_hashes.contains_key(crates_io_filename) {
println!(
"Package {} has file {} in crates.io release but not ours",
package.name,
crates_io_filename.display()
);
}
}
}
Expand Down
107 changes: 107 additions & 0 deletions src/package.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use std::{
collections::BTreeMap,
fs::File,
io::{self, BufReader, Read, Seek},
path::{Path, PathBuf},
};

use flate2::read::GzDecoder;
use sha2::{Digest as _, Sha512};
use tar::Archive;

use crate::io::AsciiWhitespaceSkippingReader;

#[derive(Debug)]
pub struct Package(PathBuf);

#[derive(Debug)]
pub struct PackageContents(BTreeMap<PathBuf, [u8; 64]>);

#[derive(Debug)]
pub enum PackageComparison {
Equal(#[allow(dead_code)] PathBuf),
Different(PathBuf),
OnlyLeft(PathBuf),
OnlyRight(PathBuf),
}

impl Package {
pub fn new(path: PathBuf) -> Self {
Self(path)
}

pub fn raw_reader(&self) -> io::Result<impl Read + Seek> {
File::open(&self.0)
}

pub fn decompressed_reader(&self) -> io::Result<impl Read> {
self.raw_reader().map(GzDecoder::new)
}

pub fn archive_reader(&self) -> io::Result<Archive<impl Read>> {
self.decompressed_reader().map(Archive::new)
}

pub fn contents(&self) -> io::Result<PackageContents> {
let mut hashes = BTreeMap::new();

let mut archive = self.archive_reader()?;
for file in archive.entries()? {
let file = file?;
let path = file.path()?.into_owned();

let mut reader = AsciiWhitespaceSkippingReader::new(BufReader::new(file));

let mut sha512 = Sha512::new();
io::copy(&mut reader, &mut sha512)?;
hashes.insert(path, sha512.finalize().into());
}

Ok(PackageContents(hashes))
}
}

impl PackageContents {
pub fn compare<'a>(
left: &'a PackageContents,
right: &'a PackageContents,
) -> impl Iterator<Item = PackageComparison> + 'a {
let a = left
.0
.iter()
.filter(|(path, _)| !is_path_ignored(path))
.map(|(path, left_hash)| match right.0.get(path) {
Some(right_hash) if left_hash == right_hash => {
PackageComparison::Equal(path.to_owned())
}
Some(_) => PackageComparison::Different(path.to_owned()),
None => PackageComparison::OnlyLeft(path.to_owned()),
});
let b = right
.0
.iter()
.filter(|(path, _)| !is_path_ignored(path))
.filter_map(|(path, _)| {
if left.0.contains_key(path) {
None
} else {
Some(PackageComparison::OnlyRight(path.to_owned()))
}
});

a.chain(b)
}
}

fn is_path_ignored(path: &Path) -> bool {
if path.ends_with(".cargo_vcs_info.json") {
return true;
}

// TODO: remove this
if path.ends_with("Cargo.toml") || path.ends_with("Cargo.toml.orig") {
return true;
}

false
}
18 changes: 5 additions & 13 deletions src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::{
fs::{self, File},
io::{self, Read, Write as _},
io::{self, Write as _},
path::{Path, PathBuf},
};

use anyhow::Result;
use flate2::read::GzDecoder;
use semver::Version;
use tar::Archive;

use crate::package::Package;

pub struct RegistryCrate {
crate_file: PathBuf,
Expand Down Expand Up @@ -50,15 +50,7 @@ impl RegistryCrate {
})
}

pub fn raw_crate_file(&self) -> io::Result<impl Read> {
File::open(&self.crate_file)
}

pub fn decompressed_crate_file(&self) -> io::Result<impl Read> {
self.raw_crate_file().map(GzDecoder::new)
}

pub fn crate_contents(&self) -> io::Result<Archive<impl Read>> {
self.decompressed_crate_file().map(Archive::new)
pub fn package(&self) -> Package {
Package::new(self.crate_file.clone())
}
}

0 comments on commit 56473fe

Please sign in to comment.