Skip to content

Commit

Permalink
refactor code structure
Browse files Browse the repository at this point in the history
The goal is to keep related code together, instead of spreading it out into
top-level modules exclusively.
  • Loading branch information
Byron committed Jul 29, 2023
1 parent 260c103 commit eb46133
Show file tree
Hide file tree
Showing 9 changed files with 745 additions and 737 deletions.
2 changes: 1 addition & 1 deletion src/changes.rs → src/bare_index/changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ fn oid_and_branch_from_commit_message(msg: &str) -> Option<(gix::ObjectId, &str)

#[cfg(test)]
pub(crate) mod test {
use crate::changes::{oid_and_branch_from_commit_message, };
use super::{oid_and_branch_from_commit_message};

#[test]
fn changes_parse_split_message() {
Expand Down
82 changes: 82 additions & 0 deletions src/bare_index/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::Error;
use std::path::{Path, PathBuf};

/// Calls the specified function for each cargo config located according to
/// cargo's standard hierarchical structure
///
/// Note that this only supports the use of `.cargo/config.toml`, which is not
/// supported below cargo 1.39.0
///
/// See https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
fn read_cargo_config<T>(
root: Option<&Path>,
cargo_home: Option<&Path>,
callback: impl Fn(&toml::Value) -> Option<T>,
) -> Result<Option<T>, Error> {
use std::borrow::Cow;

if let Some(mut path) = root
.map(PathBuf::from)
.or_else(|| std::env::current_dir().ok())
{
loop {
path.push(".cargo/config.toml");
if let Some(toml) = try_read_toml(&path)? {
if let Some(value) = callback(&toml) {
return Ok(Some(value));
}
}
path.pop();
path.pop();

// Walk up to the next potential config root
if !path.pop() {
break;
}
}
}

if let Some(home) = cargo_home
.map(Cow::Borrowed)
.or_else(|| home::cargo_home().ok().map(Cow::Owned))
{
let path = home.join("config.toml");
if let Some(toml) = try_read_toml(&path)? {
if let Some(value) = callback(&toml) {
return Ok(Some(value));
}
}
}

Ok(None)
}

fn try_read_toml(path: &Path) -> Result<Option<toml::Value>, Error> {
if !path.exists() {
return Ok(None);
}

let toml = toml::from_str(&std::fs::read_to_string(path)?).map_err(Error::Toml)?;
Ok(Some(toml))
}

/// Gets the url of a replacement registry for crates.io if one has been configured
///
/// See https://doc.rust-lang.org/cargo/reference/source-replacement.html
#[inline]
pub(crate) fn get_crates_io_replacement(
root: Option<&Path>,
cargo_home: Option<&Path>,
) -> Result<Option<String>, Error> {
read_cargo_config(root, cargo_home, |config| {
config.get("source").and_then(|sources| {
sources
.get("crates-io")
.and_then(|v| v.get("replace-with"))
.and_then(|v| v.as_str())
.and_then(|v| sources.get(v))
.and_then(|v| v.get("registry"))
.and_then(|v| v.as_str().map(String::from))
})
})
}
57 changes: 8 additions & 49 deletions src/bare_index.rs → src/bare_index/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![allow(clippy::result_large_err)]
use crate::changes::ChangesIter;
use crate::dedupe::DedupeContext;
use crate::dirs::get_index_details;
use crate::dirs::{crate_name_to_relative_path, get_index_details};
use crate::error::GixError;
use crate::{path_max_byte_len, Crate, Error, IndexConfig};
use gix::config::tree::Key;
Expand All @@ -11,6 +10,10 @@ use std::path::{Path, PathBuf};
/// The default URL of the crates.io index for use with git, see [`Index::with_path`]
pub const INDEX_GIT_URL: &str = "https://github.com/rust-lang/crates.io-index";

mod changes;
use changes::ChangesIter;
mod config;

/// Wrapper around managing the crates.io-index git repository
///
/// Uses a "bare" git index that fetches files directly from the repo instead of local checkout.
Expand Down Expand Up @@ -39,7 +42,7 @@ impl Index {
/// Note this function takes the `CARGO_HOME` environment variable into account
#[inline]
pub fn new_cargo_default() -> Result<Self, Error> {
let url = crate::config::get_crates_io_replacement(None, None)?;
let url = config::get_crates_io_replacement(None, None)?;
Self::from_url(url.as_deref().unwrap_or(crate::INDEX_GIT_URL))
}

Expand Down Expand Up @@ -203,7 +206,7 @@ impl Index {
/// to read majority of crates, prefer the [`Index::crates()`] iterator.
#[must_use]
pub fn crate_(&self, name: &str) -> Option<Crate> {
let rel_path = crate::crate_name_to_relative_path(name, None)?;
let rel_path = crate_name_to_relative_path(name, None)?;

// Attempt to load the .cache/ entry first, this is purely an acceleration
// mechanism and can fail for a few reasons that are non-fatal
Expand Down Expand Up @@ -509,48 +512,4 @@ impl<'a> Iterator for Crates<'a> {

#[cfg(test)]
#[cfg(feature = "https")]
mod test {
use gix::bstr::ByteSlice;
use super::*;

#[test]
#[cfg_attr(debug_assertions, ignore = "too slow in debug mode")]
fn parse_all_blobs() {
std::thread::scope(|scope| {
let (tx, rx) = std::sync::mpsc::channel();
let blobs = scope.spawn(move || {
let index = shared_index();
for c in index.crates_blobs().unwrap() {
tx.send(c).unwrap();
}
});
let parse = scope.spawn(move || {
let mut found_gcc_crate = false;
let mut ctx = DedupeContext::new();
for c in rx {
match c.parse(&mut ctx) {
Ok(c) => {
if c.name() == "gcc" {
found_gcc_crate = true;
}
}
Err(e) => panic!("can't parse :( {:?}: {e}", c.0.as_bstr()),
}
}
assert!(found_gcc_crate);
});
parse.join().unwrap();
blobs.join().unwrap();
});
}

fn shared_index() -> Index {
let index_path = "tests/fixtures/git-registry";
if is_ci::cached() {
Index::new_cargo_default()
.expect("CI has just cloned this index and its ours and valid")
} else {
Index::with_path(index_path, INDEX_GIT_URL).expect("clone works and there is no racing")
}
}
}
mod test;
43 changes: 43 additions & 0 deletions src/bare_index/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use gix::bstr::ByteSlice;
use super::*;

#[test]
#[cfg_attr(debug_assertions, ignore = "too slow in debug mode")]
fn parse_all_blobs() {
std::thread::scope(|scope| {
let (tx, rx) = std::sync::mpsc::channel();
let blobs = scope.spawn(move || {
let index = shared_index();
for c in index.crates_blobs().unwrap() {
tx.send(c).unwrap();
}
});
let parse = scope.spawn(move || {
let mut found_gcc_crate = false;
let mut ctx = DedupeContext::new();
for c in rx {
match c.parse(&mut ctx) {
Ok(c) => {
if c.name() == "gcc" {
found_gcc_crate = true;
}
}
Err(e) => panic!("can't parse :( {:?}: {e}", c.0.as_bstr()),
}
}
assert!(found_gcc_crate);
});
parse.join().unwrap();
blobs.join().unwrap();
});
}

fn shared_index() -> Index {
let index_path = "tests/fixtures/git-registry";
if is_ci::cached() {
Index::new_cargo_default()
.expect("CI has just cloned this index and its ours and valid")
} else {
Index::with_path(index_path, INDEX_GIT_URL).expect("clone works and there is no racing")
}
}
118 changes: 40 additions & 78 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,44 @@
use crate::Error;
use std::path::{Path, PathBuf};

/// Calls the specified function for each cargo config located according to
/// cargo's standard hierarchical structure
///
/// Note that this only supports the use of `.cargo/config.toml`, which is not
/// supported below cargo 1.39.0
///
/// See https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
pub(crate) fn read_cargo_config<T>(
root: Option<&Path>,
cargo_home: Option<&Path>,
callback: impl Fn(&toml::Value) -> Option<T>,
) -> Result<Option<T>, Error> {
use std::borrow::Cow;

if let Some(mut path) = root
.map(PathBuf::from)
.or_else(|| std::env::current_dir().ok())
{
loop {
path.push(".cargo/config.toml");
if let Some(toml) = try_read_toml(&path)? {
if let Some(value) = callback(&toml) {
return Ok(Some(value));
}
}
path.pop();
path.pop();

// Walk up to the next potential config root
if !path.pop() {
break;
}
}
}

if let Some(home) = cargo_home
.map(Cow::Borrowed)
.or_else(|| home::cargo_home().ok().map(Cow::Owned))
{
let path = home.join("config.toml");
if let Some(toml) = try_read_toml(&path)? {
if let Some(value) = callback(&toml) {
return Ok(Some(value));
}
}
}

Ok(None)
use serde_derive::Deserialize;
use crate::dirs::crate_prefix;

/// Global configuration of an index, reflecting the [contents of config.json](https://doc.rust-lang.org/cargo/reference/registries.html#index-format).
#[derive(Clone, Debug, Deserialize)]
pub struct IndexConfig {
/// Pattern for creating download URLs. Use [`IndexConfig::download_url`] instead.
pub dl: String,
/// Base URL for publishing, etc.
pub api: Option<String>,
}

fn try_read_toml(path: &Path) -> Result<Option<toml::Value>, Error> {
if !path.exists() {
return Ok(None);
impl IndexConfig {
/// Get the URL from where the specified package can be downloaded.
/// This method assumes the particular version is present in the registry,
/// and does not verify that it is.
#[must_use]
pub fn download_url(&self, name: &str, version: &str) -> Option<String> {
if !self.dl.contains("{crate}")
&& !self.dl.contains("{version}")
&& !self.dl.contains("{prefix}")
&& !self.dl.contains("{lowerprefix}")
{
let mut new = String::with_capacity(self.dl.len() + name.len() + version.len() + 10);
new.push_str(&self.dl);
new.push('/');
new.push_str(name);
new.push('/');
new.push_str(version);
new.push_str("/download");
Some(new)
} else {
let mut prefix = String::with_capacity(5);
crate_prefix(&mut prefix, name, '/')?;
Some(
self.dl
.replace("{crate}", name)
.replace("{version}", version)
.replace("{prefix}", &prefix)
.replace("{lowerprefix}", &prefix.to_ascii_lowercase()),
)
}
}

let toml = toml::from_str(&std::fs::read_to_string(path)?).map_err(Error::Toml)?;
Ok(Some(toml))
}

/// Gets the url of a replacement registry for crates.io if one has been configured
///
/// See https://doc.rust-lang.org/cargo/reference/source-replacement.html
#[inline]
pub(crate) fn get_crates_io_replacement(
root: Option<&Path>,
cargo_home: Option<&Path>,
) -> Result<Option<String>, Error> {
read_cargo_config(root, cargo_home, |config| {
config.get("source").and_then(|sources| {
sources
.get("crates-io")
.and_then(|v| v.get("replace-with"))
.and_then(|v| v.as_str())
.and_then(|v| sources.get(v))
.and_then(|v| v.get("registry"))
.and_then(|v| v.as_str().map(String::from))
})
})
}
Loading

0 comments on commit eb46133

Please sign in to comment.