#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
#![warn(rustdoc::redundant_explicit_links)]
#![cfg_attr(
feature = "rayon",
doc = r##"
With `rayon`, it's trivial to fetch sources in parallel:
```rust
# use fetch_source::Error;
use rayon::prelude::*;
use std::path::PathBuf;
# fn main() -> Result<(), Error> {
let cargo_toml = r#"
[package.metadata.fetch-source]
"syn::latest" = { git = "https://github.com/dtolnay/syn.git" }
"syn::1.0.0" = { tar = "https://github.com/dtolnay/syn/archive/refs/tags/1.0.0.tar.gz" }
"#;
let out_dir = PathBuf::from(std::env::temp_dir());
fetch_source::try_parse_toml(cargo_toml)?.into_par_iter()
.map(|(_, source)| source.fetch(&out_dir))
.filter_map(Result::err)
.for_each(|err| eprintln!("{err}"));
# Ok(())
# }
```
"##
)]
mod cache;
mod error;
mod git;
mod source;
#[cfg(feature = "tar")]
mod tar;
pub use cache::{Cache, CacheDir, CacheItems, CacheRoot, RelCacheDir};
pub use error::{Error, ErrorKind, FetchError};
pub use git::Git;
pub use source::{
Artefact, Digest, FetchResult, Source, SourceName, SourceParseError, SourcesTable,
try_parse_toml,
};
#[cfg(feature = "tar")]
pub use tar::Tar;
pub fn load_sources<P: AsRef<std::path::Path>>(path: P) -> Result<SourcesTable, Error> {
Ok(try_parse_toml(&std::fs::read_to_string(
path.as_ref().to_path_buf().join("Cargo.toml"),
)?)?)
}
pub fn fetch_all<P: AsRef<std::path::Path>>(
sources: SourcesTable,
out_dir: P,
) -> Vec<(SourceName, FetchResult<Artefact>)> {
sources
.into_iter()
.map(
|(name, source)| match source.fetch(out_dir.as_ref().join(&name)) {
Ok(artefact) => (name, Ok(artefact)),
Err(err) => (name, Err(err)),
},
)
.collect()
}
#[cfg(feature = "rayon")]
mod par {
use super::*;
use rayon::prelude::*;
pub fn fetch_all_par<P: AsRef<std::path::Path> + Sync>(
sources: SourcesTable,
out_dir: P,
) -> Vec<(SourceName, FetchResult<Artefact>)> {
sources
.into_par_iter()
.map(
|(name, source)| match source.fetch(out_dir.as_ref().join(&name)) {
Ok(artefact) => (name, Ok(artefact)),
Err(err) => (name, Err(err)),
},
)
.collect::<Vec<_>>()
}
pub fn cache_all_par(
cache: &mut Cache,
sources: SourcesTable,
) -> Vec<(SourceName, FetchError)> {
let items = cache.items();
let cache_root = cache.cache_dir();
let results = sources
.into_iter()
.filter(|(_, source)| !items.contains(source))
.collect::<Vec<_>>()
.into_par_iter()
.map(|(name, source)| {
let artefact_dir = cache_root.append(items.relative_path(&source));
(name, source.fetch(&*artefact_dir))
})
.collect::<Vec<_>>();
let items = cache.items_mut();
results.into_iter().fold(Vec::new(), {
|mut errors, (name, result)| {
match result {
Ok(artefact) => items.insert(artefact),
Err(err) => errors.push((name, err)),
}
errors
}
})
}
}
#[cfg(feature = "rayon")]
pub use par::{cache_all_par, fetch_all_par};
#[cfg(test)]
#[macro_export]
macro_rules! build_from_json {
($t:ty) => {{
serde_json::from_value::<$t>(serde_json::json! { { } }).map_err($crate::SourceParseError::from)
}};
($t:ty, $($json:tt)+) => {{
serde_json::from_value::<$t>(serde_json::json! { { $($json)+ } }).map_err($crate::SourceParseError::from)
}};
($($json:tt)*) => {{
serde_json::from_value(serde_json::json! { { $($json)* } }).map_err($crate::SourceParseError::from)
}};
}