mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 10:26:30 +00:00
fmt
This commit is contained in:
parent
63a8355543
commit
c8531f444a
29 changed files with 686 additions and 386 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1170,6 +1170,9 @@ name = "serde"
|
||||||
version = "1.0.116"
|
version = "1.0.116"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
|
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
|
@ -1258,7 +1261,6 @@ dependencies = [
|
||||||
"sass-rs",
|
"sass-rs",
|
||||||
"semver 0.11.0",
|
"semver 0.11.0",
|
||||||
"serde 1.0.116",
|
"serde 1.0.116",
|
||||||
"serde_derive",
|
|
||||||
"serde_json 1.0.57",
|
"serde_json 1.0.57",
|
||||||
"shared_failure",
|
"shared_failure",
|
||||||
"slog",
|
"slog",
|
||||||
|
|
|
@ -19,15 +19,14 @@ futures = "0.1.18"
|
||||||
hyper = "0.11.15"
|
hyper = "0.11.15"
|
||||||
hyper-tls = "0.1.2"
|
hyper-tls = "0.1.2"
|
||||||
indexmap = { version = "1.0.0", features = ["serde-1"] }
|
indexmap = { version = "1.0.0", features = ["serde-1"] }
|
||||||
lru-cache = "0.1.1"
|
lru-cache = "0.1"
|
||||||
maud = "0.22"
|
maud = "0.22"
|
||||||
once_cell = "1.4"
|
once_cell = "1.4"
|
||||||
relative-path = { version = "0.3.7", features = ["serde"] }
|
relative-path = { version = "0.3.7", features = ["serde"] }
|
||||||
route-recognizer = "0.1.12"
|
route-recognizer = "0.1.12"
|
||||||
rustsec = "0.6.0"
|
rustsec = "0.6.0"
|
||||||
semver = { version = "0.11", features = ["serde"] }
|
semver = { version = "0.11", features = ["serde"] }
|
||||||
serde = "1.0.27"
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_derive = "1.0.27"
|
|
||||||
serde_json = "1.0.9"
|
serde_json = "1.0.9"
|
||||||
shared_failure = "0.1.0"
|
shared_failure = "0.1.0"
|
||||||
slog = "2.1.1"
|
slog = "2.1.1"
|
||||||
|
|
3
build.rs
3
build.rs
|
@ -11,8 +11,7 @@ fn build_style() -> String {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
sass::compile_file("./assets/styles/main.sass", options)
|
sass::compile_file("./assets/styles/main.sass", options).expect("failed to compile style sheet")
|
||||||
.expect("failed to compile style sheet")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,40 +1,57 @@
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Future, Poll, Stream};
|
|
||||||
use futures::stream::futures_unordered;
|
use futures::stream::futures_unordered;
|
||||||
|
use futures::{Future, Poll, Stream};
|
||||||
|
|
||||||
use crate::models::crates::{AnalyzedDependencies, CrateDeps};
|
use crate::models::crates::{AnalyzedDependencies, CrateDeps};
|
||||||
|
|
||||||
use super::super::Engine;
|
|
||||||
use super::super::machines::analyzer::DependencyAnalyzer;
|
use super::super::machines::analyzer::DependencyAnalyzer;
|
||||||
|
use super::super::Engine;
|
||||||
|
|
||||||
pub struct AnalyzeDependenciesFuture {
|
pub struct AnalyzeDependenciesFuture {
|
||||||
inner: Box<dyn Future<Item=AnalyzedDependencies, Error=Error>>
|
inner: Box<dyn Future<Item = AnalyzedDependencies, Error = Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalyzeDependenciesFuture {
|
impl AnalyzeDependenciesFuture {
|
||||||
pub fn new(engine: Engine, deps: CrateDeps) -> Self {
|
pub fn new(engine: Engine, deps: CrateDeps) -> Self {
|
||||||
let future = engine.fetch_advisory_db().and_then(move |advisory_db| {
|
let future =
|
||||||
let analyzer = DependencyAnalyzer::new(&deps, Some(advisory_db));
|
engine.fetch_advisory_db().and_then(move |advisory_db| {
|
||||||
|
let analyzer = DependencyAnalyzer::new(&deps, Some(advisory_db));
|
||||||
|
|
||||||
let main_deps = deps.main.into_iter().filter_map(|(name, dep)| {
|
let main_deps = deps.main.into_iter().filter_map(|(name, dep)| {
|
||||||
if dep.is_external() { Some(name) } else { None }
|
if dep.is_external() {
|
||||||
});
|
Some(name)
|
||||||
let dev_deps = deps.dev.into_iter().filter_map(|(name, dep)| {
|
} else {
|
||||||
if dep.is_external() { Some(name) } else { None }
|
None
|
||||||
});
|
}
|
||||||
let build_deps = deps.build.into_iter().filter_map(|(name, dep)| {
|
});
|
||||||
if dep.is_external() { Some(name) } else { None }
|
let dev_deps = deps.dev.into_iter().filter_map(|(name, dep)| {
|
||||||
});
|
if dep.is_external() {
|
||||||
|
Some(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let build_deps = deps.build.into_iter().filter_map(|(name, dep)| {
|
||||||
|
if dep.is_external() {
|
||||||
|
Some(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let release_futures = engine.fetch_releases(main_deps.chain(dev_deps).chain(build_deps));
|
let release_futures =
|
||||||
|
engine.fetch_releases(main_deps.chain(dev_deps).chain(build_deps));
|
||||||
|
|
||||||
futures_unordered(release_futures)
|
futures_unordered(release_futures)
|
||||||
.fold(analyzer, |mut analyzer, releases| { analyzer.process(releases); Ok(analyzer) as Result<_, Error> })
|
.fold(analyzer, |mut analyzer, releases| {
|
||||||
.map(|analyzer| analyzer.finalize())
|
analyzer.process(releases);
|
||||||
});
|
Ok(analyzer) as Result<_, Error>
|
||||||
|
})
|
||||||
|
.map(|analyzer| analyzer.finalize())
|
||||||
|
});
|
||||||
|
|
||||||
AnalyzeDependenciesFuture {
|
AnalyzeDependenciesFuture {
|
||||||
inner: Box::new(future)
|
inner: Box::new(future),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,40 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Async, Future, Poll, Stream, try_ready};
|
|
||||||
use futures::stream::FuturesOrdered;
|
use futures::stream::FuturesOrdered;
|
||||||
|
use futures::{try_ready, Async, Future, Poll, Stream};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
use crate::models::repo::RepoPath;
|
use crate::models::repo::RepoPath;
|
||||||
|
|
||||||
use super::super::Engine;
|
|
||||||
use super::super::machines::crawler::ManifestCrawler;
|
use super::super::machines::crawler::ManifestCrawler;
|
||||||
pub use super::super::machines::crawler::ManifestCrawlerOutput;
|
pub use super::super::machines::crawler::ManifestCrawlerOutput;
|
||||||
|
use super::super::Engine;
|
||||||
|
|
||||||
pub struct CrawlManifestFuture {
|
pub struct CrawlManifestFuture {
|
||||||
repo_path: RepoPath,
|
repo_path: RepoPath,
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
crawler: ManifestCrawler,
|
crawler: ManifestCrawler,
|
||||||
futures: FuturesOrdered<Box<dyn Future<Item=(RelativePathBuf, String), Error=Error>>>
|
futures: FuturesOrdered<Box<dyn Future<Item = (RelativePathBuf, String), Error = Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrawlManifestFuture {
|
impl CrawlManifestFuture {
|
||||||
pub fn new(engine: &Engine, repo_path: RepoPath, entry_point: RelativePathBuf) -> Self {
|
pub fn new(engine: &Engine, repo_path: RepoPath, entry_point: RelativePathBuf) -> Self {
|
||||||
let future: Box<dyn Future<Item=_, Error=_>> = Box::new(engine.retrieve_manifest_at_path(&repo_path, &entry_point)
|
let future: Box<dyn Future<Item = _, Error = _>> = Box::new(
|
||||||
.map(move |contents| (entry_point, contents)));
|
engine
|
||||||
|
.retrieve_manifest_at_path(&repo_path, &entry_point)
|
||||||
|
.map(move |contents| (entry_point, contents)),
|
||||||
|
);
|
||||||
let engine = engine.clone();
|
let engine = engine.clone();
|
||||||
let crawler = ManifestCrawler::new();
|
let crawler = ManifestCrawler::new();
|
||||||
let mut futures = FuturesOrdered::new();
|
let mut futures = FuturesOrdered::new();
|
||||||
futures.push(future);
|
futures.push(future);
|
||||||
|
|
||||||
CrawlManifestFuture {
|
CrawlManifestFuture {
|
||||||
repo_path, engine, crawler, futures
|
repo_path,
|
||||||
|
engine,
|
||||||
|
crawler,
|
||||||
|
futures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,12 +48,15 @@ impl Future for CrawlManifestFuture {
|
||||||
None => {
|
None => {
|
||||||
let crawler = mem::replace(&mut self.crawler, ManifestCrawler::new());
|
let crawler = mem::replace(&mut self.crawler, ManifestCrawler::new());
|
||||||
Ok(Async::Ready(crawler.finalize()))
|
Ok(Async::Ready(crawler.finalize()))
|
||||||
},
|
}
|
||||||
Some((path, raw_manifest)) => {
|
Some((path, raw_manifest)) => {
|
||||||
let output = self.crawler.step(path, raw_manifest)?;
|
let output = self.crawler.step(path, raw_manifest)?;
|
||||||
for path in output.paths_of_interest.into_iter() {
|
for path in output.paths_of_interest.into_iter() {
|
||||||
let future: Box<dyn Future<Item=_, Error=_>> = Box::new(self.engine.retrieve_manifest_at_path(&self.repo_path, &path)
|
let future: Box<dyn Future<Item = _, Error = _>> = Box::new(
|
||||||
.map(move |contents| (path, contents)));
|
self.engine
|
||||||
|
.retrieve_manifest_at_path(&self.repo_path, &path)
|
||||||
|
.map(move |contents| (path, contents)),
|
||||||
|
);
|
||||||
self.futures.push(future);
|
self.futures.push(future);
|
||||||
}
|
}
|
||||||
self.poll()
|
self.poll()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod crawl;
|
|
||||||
mod analyze;
|
mod analyze;
|
||||||
|
mod crawl;
|
||||||
|
|
||||||
pub use self::crawl::CrawlManifestFuture;
|
|
||||||
pub use self::analyze::AnalyzeDependenciesFuture;
|
pub use self::analyze::AnalyzeDependenciesFuture;
|
||||||
|
pub use self::crawl::CrawlManifestFuture;
|
||||||
|
|
|
@ -3,22 +3,29 @@ use std::sync::Arc;
|
||||||
use rustsec::db::AdvisoryDatabase;
|
use rustsec::db::AdvisoryDatabase;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
use crate::models::crates::{CrateDeps, CrateRelease, CrateName, AnalyzedDependency, AnalyzedDependencies};
|
use crate::models::crates::{
|
||||||
|
AnalyzedDependencies, AnalyzedDependency, CrateDeps, CrateName, CrateRelease,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DependencyAnalyzer {
|
pub struct DependencyAnalyzer {
|
||||||
deps: AnalyzedDependencies,
|
deps: AnalyzedDependencies,
|
||||||
advisory_db: Option<Arc<AdvisoryDatabase>>
|
advisory_db: Option<Arc<AdvisoryDatabase>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DependencyAnalyzer {
|
impl DependencyAnalyzer {
|
||||||
pub fn new(deps: &CrateDeps, advisory_db: Option<Arc<AdvisoryDatabase>>) -> DependencyAnalyzer {
|
pub fn new(deps: &CrateDeps, advisory_db: Option<Arc<AdvisoryDatabase>>) -> DependencyAnalyzer {
|
||||||
DependencyAnalyzer {
|
DependencyAnalyzer {
|
||||||
deps: AnalyzedDependencies::new(deps),
|
deps: AnalyzedDependencies::new(deps),
|
||||||
advisory_db
|
advisory_db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_single(name: &CrateName, dep: &mut AnalyzedDependency, ver: &Version, advisory_db: Option<&AdvisoryDatabase>) {
|
fn process_single(
|
||||||
|
name: &CrateName,
|
||||||
|
dep: &mut AnalyzedDependency,
|
||||||
|
ver: &Version,
|
||||||
|
advisory_db: Option<&AdvisoryDatabase>,
|
||||||
|
) {
|
||||||
if dep.required.matches(&ver) {
|
if dep.required.matches(&ver) {
|
||||||
if let Some(ref mut current_latest_that_matches) = dep.latest_that_matches {
|
if let Some(ref mut current_latest_that_matches) = dep.latest_that_matches {
|
||||||
if *current_latest_that_matches < *ver {
|
if *current_latest_that_matches < *ver {
|
||||||
|
@ -28,7 +35,10 @@ impl DependencyAnalyzer {
|
||||||
dep.latest_that_matches = Some(ver.clone());
|
dep.latest_that_matches = Some(ver.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !advisory_db.map(|db| db.find_vulns_for_crate(name.as_ref(), ver).is_empty()).unwrap_or(true) {
|
if !advisory_db
|
||||||
|
.map(|db| db.find_vulns_for_crate(name.as_ref(), ver).is_empty())
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
dep.insecure = true;
|
dep.insecure = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,17 +53,32 @@ impl DependencyAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process<I: IntoIterator<Item=CrateRelease>>(&mut self, releases: I) {
|
pub fn process<I: IntoIterator<Item = CrateRelease>>(&mut self, releases: I) {
|
||||||
let advisory_db = self.advisory_db.as_ref().map(|r| r.as_ref());
|
let advisory_db = self.advisory_db.as_ref().map(|r| r.as_ref());
|
||||||
for release in releases.into_iter().filter(|r| !r.yanked) {
|
for release in releases.into_iter().filter(|r| !r.yanked) {
|
||||||
if let Some(main_dep) = self.deps.main.get_mut(&release.name) {
|
if let Some(main_dep) = self.deps.main.get_mut(&release.name) {
|
||||||
DependencyAnalyzer::process_single(&release.name, main_dep, &release.version, advisory_db)
|
DependencyAnalyzer::process_single(
|
||||||
|
&release.name,
|
||||||
|
main_dep,
|
||||||
|
&release.version,
|
||||||
|
advisory_db,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if let Some(dev_dep) = self.deps.dev.get_mut(&release.name) {
|
if let Some(dev_dep) = self.deps.dev.get_mut(&release.name) {
|
||||||
DependencyAnalyzer::process_single(&release.name, dev_dep, &release.version, advisory_db)
|
DependencyAnalyzer::process_single(
|
||||||
|
&release.name,
|
||||||
|
dev_dep,
|
||||||
|
&release.version,
|
||||||
|
advisory_db,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if let Some(build_dep) = self.deps.build.get_mut(&release.name) {
|
if let Some(build_dep) = self.deps.build.get_mut(&release.name) {
|
||||||
DependencyAnalyzer::process_single(&release.name, build_dep, &release.version, advisory_db)
|
DependencyAnalyzer::process_single(
|
||||||
|
&release.name,
|
||||||
|
build_dep,
|
||||||
|
&release.version,
|
||||||
|
advisory_db,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,75 +90,156 @@ impl DependencyAnalyzer {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use models::crates::{CrateDep, CrateDeps, CrateRelease};
|
|
||||||
use super::DependencyAnalyzer;
|
use super::DependencyAnalyzer;
|
||||||
|
use models::crates::{CrateDep, CrateDeps, CrateRelease};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tracks_latest_without_matching() {
|
fn tracks_latest_without_matching() {
|
||||||
let mut deps = CrateDeps::default();
|
let mut deps = CrateDeps::default();
|
||||||
deps.main.insert("hyper".parse().unwrap(), CrateDep::External("^0.11.0".parse().unwrap()));
|
deps.main.insert(
|
||||||
|
"hyper".parse().unwrap(),
|
||||||
|
CrateDep::External("^0.11.0".parse().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
||||||
analyzer.process(vec![
|
analyzer.process(vec![
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.0".parse().unwrap(), deps: Default::default(), yanked: false },
|
CrateRelease {
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.1".parse().unwrap(), deps: Default::default(), yanked: false }
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.0".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
|
CrateRelease {
|
||||||
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.1".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let analyzed = analyzer.finalize();
|
let analyzed = analyzer.finalize();
|
||||||
|
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest_that_matches, None);
|
assert_eq!(
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest, Some("0.10.1".parse().unwrap()));
|
analyzed.main.get("hyper").unwrap().latest_that_matches,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
analyzed.main.get("hyper").unwrap().latest,
|
||||||
|
Some("0.10.1".parse().unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tracks_latest_that_matches() {
|
fn tracks_latest_that_matches() {
|
||||||
let mut deps = CrateDeps::default();
|
let mut deps = CrateDeps::default();
|
||||||
deps.main.insert("hyper".parse().unwrap(), CrateDep::External("^0.10.0".parse().unwrap()));
|
deps.main.insert(
|
||||||
|
"hyper".parse().unwrap(),
|
||||||
|
CrateDep::External("^0.10.0".parse().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
||||||
analyzer.process(vec![
|
analyzer.process(vec![
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.0".parse().unwrap(), deps: Default::default(), yanked: false },
|
CrateRelease {
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.1".parse().unwrap(), deps: Default::default(), yanked: false },
|
name: "hyper".parse().unwrap(),
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.11.0".parse().unwrap(), deps: Default::default(), yanked: false }
|
version: "0.10.0".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
|
CrateRelease {
|
||||||
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.1".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
|
CrateRelease {
|
||||||
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.11.0".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let analyzed = analyzer.finalize();
|
let analyzed = analyzer.finalize();
|
||||||
|
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest_that_matches, Some("0.10.1".parse().unwrap()));
|
assert_eq!(
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest, Some("0.11.0".parse().unwrap()));
|
analyzed.main.get("hyper").unwrap().latest_that_matches,
|
||||||
|
Some("0.10.1".parse().unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
analyzed.main.get("hyper").unwrap().latest,
|
||||||
|
Some("0.11.0".parse().unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skips_yanked_releases() {
|
fn skips_yanked_releases() {
|
||||||
let mut deps = CrateDeps::default();
|
let mut deps = CrateDeps::default();
|
||||||
deps.main.insert("hyper".parse().unwrap(), CrateDep::External("^0.10.0".parse().unwrap()));
|
deps.main.insert(
|
||||||
|
"hyper".parse().unwrap(),
|
||||||
|
CrateDep::External("^0.10.0".parse().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
||||||
analyzer.process(vec![
|
analyzer.process(vec![
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.0".parse().unwrap(), deps: Default::default(), yanked: false },
|
CrateRelease {
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.1".parse().unwrap(), deps: Default::default(), yanked: true },
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.0".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
|
CrateRelease {
|
||||||
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.1".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: true,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let analyzed = analyzer.finalize();
|
let analyzed = analyzer.finalize();
|
||||||
|
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest_that_matches, Some("0.10.0".parse().unwrap()));
|
assert_eq!(
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest, Some("0.10.0".parse().unwrap()));
|
analyzed.main.get("hyper").unwrap().latest_that_matches,
|
||||||
|
Some("0.10.0".parse().unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
analyzed.main.get("hyper").unwrap().latest,
|
||||||
|
Some("0.10.0".parse().unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skips_prereleases() {
|
fn skips_prereleases() {
|
||||||
let mut deps = CrateDeps::default();
|
let mut deps = CrateDeps::default();
|
||||||
deps.main.insert("hyper".parse().unwrap(), CrateDep::External("^0.10.0".parse().unwrap()));
|
deps.main.insert(
|
||||||
|
"hyper".parse().unwrap(),
|
||||||
|
CrateDep::External("^0.10.0".parse().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
let mut analyzer = DependencyAnalyzer::new(&deps, None);
|
||||||
analyzer.process(vec![
|
analyzer.process(vec![
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.0".parse().unwrap(), deps: Default::default(), yanked: false },
|
CrateRelease {
|
||||||
CrateRelease { name: "hyper".parse().unwrap(), version: "0.10.1-alpha".parse().unwrap(), deps: Default::default(), yanked: false },
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.0".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
|
CrateRelease {
|
||||||
|
name: "hyper".parse().unwrap(),
|
||||||
|
version: "0.10.1-alpha".parse().unwrap(),
|
||||||
|
deps: Default::default(),
|
||||||
|
yanked: false,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let analyzed = analyzer.finalize();
|
let analyzed = analyzer.finalize();
|
||||||
|
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest_that_matches, Some("0.10.0".parse().unwrap()));
|
assert_eq!(
|
||||||
assert_eq!(analyzed.main.get("hyper").unwrap().latest, Some("0.10.0".parse().unwrap()));
|
analyzed.main.get("hyper").unwrap().latest_that_matches,
|
||||||
|
Some("0.10.0".parse().unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
analyzed.main.get("hyper").unwrap().latest,
|
||||||
|
Some("0.10.0".parse().unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,57 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
|
use crate::models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName};
|
||||||
use crate::parsers::manifest::parse_manifest_toml;
|
use crate::parsers::manifest::parse_manifest_toml;
|
||||||
use crate::models::crates::{CrateDep, CrateDeps, CrateName, CrateManifest};
|
|
||||||
|
|
||||||
pub struct ManifestCrawlerOutput {
|
pub struct ManifestCrawlerOutput {
|
||||||
pub crates: IndexMap<CrateName, CrateDeps>
|
pub crates: IndexMap<CrateName, CrateDeps>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManifestCrawlerStepOutput {
|
pub struct ManifestCrawlerStepOutput {
|
||||||
pub paths_of_interest: Vec<RelativePathBuf>
|
pub paths_of_interest: Vec<RelativePathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManifestCrawler {
|
pub struct ManifestCrawler {
|
||||||
manifests: HashMap<RelativePathBuf, CrateManifest>,
|
manifests: HashMap<RelativePathBuf, CrateManifest>,
|
||||||
leaf_crates: IndexMap<CrateName, CrateDeps>
|
leaf_crates: IndexMap<CrateName, CrateDeps>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManifestCrawler {
|
impl ManifestCrawler {
|
||||||
pub fn new() -> ManifestCrawler {
|
pub fn new() -> ManifestCrawler {
|
||||||
ManifestCrawler {
|
ManifestCrawler {
|
||||||
manifests: HashMap::new(),
|
manifests: HashMap::new(),
|
||||||
leaf_crates: IndexMap::new()
|
leaf_crates: IndexMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, path: RelativePathBuf, raw_manifest: String) -> Result<ManifestCrawlerStepOutput, Error> {
|
pub fn step(
|
||||||
|
&mut self,
|
||||||
|
path: RelativePathBuf,
|
||||||
|
raw_manifest: String,
|
||||||
|
) -> Result<ManifestCrawlerStepOutput, Error> {
|
||||||
let manifest = parse_manifest_toml(&raw_manifest)?;
|
let manifest = parse_manifest_toml(&raw_manifest)?;
|
||||||
self.manifests.insert(path.clone(), manifest.clone());
|
self.manifests.insert(path.clone(), manifest.clone());
|
||||||
|
|
||||||
let mut output = ManifestCrawlerStepOutput {
|
let mut output = ManifestCrawlerStepOutput {
|
||||||
paths_of_interest: vec![]
|
paths_of_interest: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
match manifest {
|
match manifest {
|
||||||
CrateManifest::Package(name, deps) => {
|
CrateManifest::Package(name, deps) => {
|
||||||
self.process_package(&path, name, deps, &mut output);
|
self.process_package(&path, name, deps, &mut output);
|
||||||
},
|
}
|
||||||
CrateManifest::Workspace { members } => {
|
CrateManifest::Workspace { members } => {
|
||||||
self.process_workspace(&path, &members, &mut output);
|
self.process_workspace(&path, &members, &mut output);
|
||||||
},
|
}
|
||||||
CrateManifest::Mixed { name, deps, members } => {
|
CrateManifest::Mixed {
|
||||||
|
name,
|
||||||
|
deps,
|
||||||
|
members,
|
||||||
|
} => {
|
||||||
self.process_package(&path, name, deps, &mut output);
|
self.process_package(&path, name, deps, &mut output);
|
||||||
self.process_workspace(&path, &members, &mut output);
|
self.process_workspace(&path, &members, &mut output);
|
||||||
}
|
}
|
||||||
|
@ -52,15 +60,31 @@ impl ManifestCrawler {
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_interest(&mut self, base_path: &RelativePathBuf, path: &RelativePathBuf, output: &mut ManifestCrawlerStepOutput) {
|
fn register_interest(
|
||||||
|
&mut self,
|
||||||
|
base_path: &RelativePathBuf,
|
||||||
|
path: &RelativePathBuf,
|
||||||
|
output: &mut ManifestCrawlerStepOutput,
|
||||||
|
) {
|
||||||
let full_path = base_path.join_normalized(path);
|
let full_path = base_path.join_normalized(path);
|
||||||
if !self.manifests.contains_key(&full_path) {
|
if !self.manifests.contains_key(&full_path) {
|
||||||
output.paths_of_interest.push(full_path);
|
output.paths_of_interest.push(full_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_package(&mut self, base_path: &RelativePathBuf, name: CrateName, deps: CrateDeps, output: &mut ManifestCrawlerStepOutput) {
|
fn process_package(
|
||||||
for (_, dep) in deps.main.iter().chain(deps.dev.iter()).chain(deps.build.iter()) {
|
&mut self,
|
||||||
|
base_path: &RelativePathBuf,
|
||||||
|
name: CrateName,
|
||||||
|
deps: CrateDeps,
|
||||||
|
output: &mut ManifestCrawlerStepOutput,
|
||||||
|
) {
|
||||||
|
for (_, dep) in deps
|
||||||
|
.main
|
||||||
|
.iter()
|
||||||
|
.chain(deps.dev.iter())
|
||||||
|
.chain(deps.build.iter())
|
||||||
|
{
|
||||||
if let &CrateDep::Internal(ref path) = dep {
|
if let &CrateDep::Internal(ref path) = dep {
|
||||||
self.register_interest(base_path, path, output);
|
self.register_interest(base_path, path, output);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +93,12 @@ impl ManifestCrawler {
|
||||||
self.leaf_crates.insert(name, deps);
|
self.leaf_crates.insert(name, deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_workspace(&mut self, base_path: &RelativePathBuf, members: &[RelativePathBuf], output: &mut ManifestCrawlerStepOutput) {
|
fn process_workspace(
|
||||||
|
&mut self,
|
||||||
|
base_path: &RelativePathBuf,
|
||||||
|
members: &[RelativePathBuf],
|
||||||
|
output: &mut ManifestCrawlerStepOutput,
|
||||||
|
) {
|
||||||
for path in members {
|
for path in members {
|
||||||
if !path.ends_with("*") {
|
if !path.ends_with("*") {
|
||||||
self.register_interest(base_path, path, output);
|
self.register_interest(base_path, path, output);
|
||||||
|
@ -79,7 +108,7 @@ impl ManifestCrawler {
|
||||||
|
|
||||||
pub fn finalize(self) -> ManifestCrawlerOutput {
|
pub fn finalize(self) -> ManifestCrawlerOutput {
|
||||||
ManifestCrawlerOutput {
|
ManifestCrawlerOutput {
|
||||||
crates: self.leaf_crates
|
crates: self.leaf_crates,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,8 +118,8 @@ mod tests {
|
||||||
use relative_path::RelativePath;
|
use relative_path::RelativePath;
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
|
|
||||||
use models::crates::CrateDep;
|
|
||||||
use super::ManifestCrawler;
|
use super::ManifestCrawler;
|
||||||
|
use models::crates::CrateDep;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_package_manifest() {
|
fn simple_package_manifest() {
|
||||||
|
@ -99,7 +128,9 @@ mod tests {
|
||||||
name = "simpleton"
|
name = "simpleton"
|
||||||
"#;
|
"#;
|
||||||
let mut crawler = ManifestCrawler::new();
|
let mut crawler = ManifestCrawler::new();
|
||||||
let step_output = crawler.step("Cargo.toml".into(), manifest.to_string()).unwrap();
|
let step_output = crawler
|
||||||
|
.step("Cargo.toml".into(), manifest.to_string())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(step_output.paths_of_interest.len(), 0);
|
assert_eq!(step_output.paths_of_interest.len(), 0);
|
||||||
let output = crawler.finalize();
|
let output = crawler.finalize();
|
||||||
assert_eq!(output.crates.len(), 1);
|
assert_eq!(output.crates.len(), 1);
|
||||||
|
@ -127,16 +158,24 @@ codegen = "0.0.1"
|
||||||
let output = crawler.finalize();
|
let output = crawler.finalize();
|
||||||
assert_eq!(output.crates.len(), 1);
|
assert_eq!(output.crates.len(), 1);
|
||||||
assert_eq!(output.crates["more-complex"].main.len(), 2);
|
assert_eq!(output.crates["more-complex"].main.len(), 2);
|
||||||
assert_eq!(output.crates["more-complex"].main.get("foo").unwrap(),
|
assert_eq!(
|
||||||
&CrateDep::External(VersionReq::parse("0.30.0").unwrap()));
|
output.crates["more-complex"].main.get("foo").unwrap(),
|
||||||
assert_eq!(output.crates["more-complex"].main.get("bar").unwrap(),
|
&CrateDep::External(VersionReq::parse("0.30.0").unwrap())
|
||||||
&CrateDep::External(VersionReq::parse("1.2.0").unwrap()));
|
);
|
||||||
|
assert_eq!(
|
||||||
|
output.crates["more-complex"].main.get("bar").unwrap(),
|
||||||
|
&CrateDep::External(VersionReq::parse("1.2.0").unwrap())
|
||||||
|
);
|
||||||
assert_eq!(output.crates["more-complex"].dev.len(), 1);
|
assert_eq!(output.crates["more-complex"].dev.len(), 1);
|
||||||
assert_eq!(output.crates["more-complex"].dev.get("quickcheck").unwrap(),
|
assert_eq!(
|
||||||
&CrateDep::External(VersionReq::parse("0.5").unwrap()));
|
output.crates["more-complex"].dev.get("quickcheck").unwrap(),
|
||||||
|
&CrateDep::External(VersionReq::parse("0.5").unwrap())
|
||||||
|
);
|
||||||
assert_eq!(output.crates["more-complex"].build.len(), 1);
|
assert_eq!(output.crates["more-complex"].build.len(), 1);
|
||||||
assert_eq!(output.crates["more-complex"].build.get("codegen").unwrap(),
|
assert_eq!(
|
||||||
&CrateDep::External(VersionReq::parse("0.0.1").unwrap()));
|
output.crates["more-complex"].build.get("codegen").unwrap(),
|
||||||
|
&CrateDep::External(VersionReq::parse("0.0.1").unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -226,10 +265,17 @@ features = ["use_std"]
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut crawler = ManifestCrawler::new();
|
let mut crawler = ManifestCrawler::new();
|
||||||
let step_output = crawler.step("".into(), futures_manifest.to_string()).unwrap();
|
let step_output = crawler
|
||||||
|
.step("".into(), futures_manifest.to_string())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(step_output.paths_of_interest.len(), 1);
|
assert_eq!(step_output.paths_of_interest.len(), 1);
|
||||||
assert_eq!(step_output.paths_of_interest[0].as_str(), "futures-cpupool");
|
assert_eq!(step_output.paths_of_interest[0].as_str(), "futures-cpupool");
|
||||||
let step_output = crawler.step("futures-cpupool".into(), futures_cpupool_manifest.to_string()).unwrap();
|
let step_output = crawler
|
||||||
|
.step(
|
||||||
|
"futures-cpupool".into(),
|
||||||
|
futures_cpupool_manifest.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(step_output.paths_of_interest.len(), 0);
|
assert_eq!(step_output.paths_of_interest.len(), 0);
|
||||||
let output = crawler.finalize();
|
let output = crawler.finalize();
|
||||||
assert_eq!(output.crates.len(), 2);
|
assert_eq!(output.crates.len(), 2);
|
||||||
|
@ -237,10 +283,20 @@ features = ["use_std"]
|
||||||
assert_eq!(output.crates["futures"].dev.len(), 0);
|
assert_eq!(output.crates["futures"].dev.len(), 0);
|
||||||
assert_eq!(output.crates["futures"].build.len(), 0);
|
assert_eq!(output.crates["futures"].build.len(), 0);
|
||||||
assert_eq!(output.crates["futures-cpupool"].main.len(), 2);
|
assert_eq!(output.crates["futures-cpupool"].main.len(), 2);
|
||||||
assert_eq!(output.crates["futures-cpupool"].main.get("num_cpus").unwrap(),
|
assert_eq!(
|
||||||
&CrateDep::External(VersionReq::parse("1.0").unwrap()));
|
output.crates["futures-cpupool"]
|
||||||
assert_eq!(output.crates["futures-cpupool"].main.get("futures").unwrap(),
|
.main
|
||||||
&CrateDep::Internal(RelativePath::new("..").to_relative_path_buf()));
|
.get("num_cpus")
|
||||||
|
.unwrap(),
|
||||||
|
&CrateDep::External(VersionReq::parse("1.0").unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
output.crates["futures-cpupool"]
|
||||||
|
.main
|
||||||
|
.get("futures")
|
||||||
|
.unwrap(),
|
||||||
|
&CrateDep::Internal(RelativePath::new("..").to_relative_path_buf())
|
||||||
|
);
|
||||||
assert_eq!(output.crates["futures-cpupool"].dev.len(), 0);
|
assert_eq!(output.crates["futures-cpupool"].dev.len(), 0);
|
||||||
assert_eq!(output.crates["futures-cpupool"].build.len(), 0);
|
assert_eq!(output.crates["futures-cpupool"].build.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pub mod crawler;
|
|
||||||
pub mod analyzer;
|
pub mod analyzer;
|
||||||
|
pub mod crawler;
|
||||||
|
|
|
@ -8,10 +8,12 @@ const BITBUCKET_USER_CONTENT_BASE_URI: &'static str = "https://bitbucket.org";
|
||||||
|
|
||||||
pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> {
|
pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> {
|
||||||
let path_str: &str = path.as_ref();
|
let path_str: &str = path.as_ref();
|
||||||
Ok(format!("{}/{}/{}/raw/HEAD/{}",
|
Ok(format!(
|
||||||
|
"{}/{}/{}/raw/HEAD/{}",
|
||||||
BITBUCKET_USER_CONTENT_BASE_URI,
|
BITBUCKET_USER_CONTENT_BASE_URI,
|
||||||
repo_path.qual.as_ref(),
|
repo_path.qual.as_ref(),
|
||||||
repo_path.name.as_ref(),
|
repo_path.name.as_ref(),
|
||||||
path_str
|
path_str
|
||||||
).parse::<Uri>()?)
|
)
|
||||||
|
.parse::<Uri>()?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Future, Stream, IntoFuture, future};
|
use futures::{future, Future, IntoFuture, Stream};
|
||||||
use hyper::{Error as HyperError, Method, Request, Response, Uri};
|
use hyper::{Error as HyperError, Method, Request, Response, Uri};
|
||||||
use tokio_service::Service;
|
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
use serde_json;
|
use serde::Deserialize;
|
||||||
|
use tokio_service::Service;
|
||||||
|
|
||||||
use crate::models::crates::{CrateName, CrateRelease, CrateDeps, CrateDep, CratePath};
|
use crate::models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease};
|
||||||
|
|
||||||
const CRATES_INDEX_BASE_URI: &str = "https://raw.githubusercontent.com/rust-lang/crates.io-index";
|
const CRATES_INDEX_BASE_URI: &str = "https://raw.githubusercontent.com/rust-lang/crates.io-index";
|
||||||
const CRATES_API_BASE_URI: &str = "https://crates.io/api/v1";
|
const CRATES_API_BASE_URI: &str = "https://crates.io/api/v1";
|
||||||
|
@ -17,7 +17,7 @@ struct RegistryPackageDep {
|
||||||
name: String,
|
name: String,
|
||||||
req: VersionReq,
|
req: VersionReq,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
kind: Option<String>
|
kind: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -26,51 +26,63 @@ struct RegistryPackage {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
deps: Vec<RegistryPackageDep>,
|
deps: Vec<RegistryPackageDep>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
yanked: bool
|
yanked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_pkgs(name: &CrateName, packages: Vec<RegistryPackage>) -> Result<QueryCrateResponse, Error> {
|
fn convert_pkgs(
|
||||||
let releases = packages.into_iter().map(|package| {
|
name: &CrateName,
|
||||||
let mut deps = CrateDeps::default();
|
packages: Vec<RegistryPackage>,
|
||||||
for dep in package.deps {
|
) -> Result<QueryCrateResponse, Error> {
|
||||||
match dep.kind.map(|k| k.clone()).unwrap_or_else(|| "normal".into()).as_ref() {
|
let releases = packages
|
||||||
"normal" =>
|
.into_iter()
|
||||||
deps.main.insert(dep.name.parse()?, CrateDep::External(dep.req)),
|
.map(|package| {
|
||||||
"dev" =>
|
let mut deps = CrateDeps::default();
|
||||||
deps.dev.insert(dep.name.parse()?, CrateDep::External(dep.req)),
|
for dep in package.deps {
|
||||||
_ => None
|
match dep
|
||||||
};
|
.kind
|
||||||
}
|
.map(|k| k.clone())
|
||||||
Ok(CrateRelease {
|
.unwrap_or_else(|| "normal".into())
|
||||||
name: name.clone(),
|
.as_ref()
|
||||||
version: package.vers,
|
{
|
||||||
deps: deps,
|
"normal" => deps
|
||||||
yanked: package.yanked
|
.main
|
||||||
|
.insert(dep.name.parse()?, CrateDep::External(dep.req)),
|
||||||
|
"dev" => deps
|
||||||
|
.dev
|
||||||
|
.insert(dep.name.parse()?, CrateDep::External(dep.req)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(CrateRelease {
|
||||||
|
name: name.clone(),
|
||||||
|
version: package.vers,
|
||||||
|
deps: deps,
|
||||||
|
yanked: package.yanked,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}).collect::<Result<_, Error>>()?;
|
.collect::<Result<_, Error>>()?;
|
||||||
|
|
||||||
Ok(QueryCrateResponse {
|
Ok(QueryCrateResponse { releases: releases })
|
||||||
releases: releases
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QueryCrateResponse {
|
pub struct QueryCrateResponse {
|
||||||
pub releases: Vec<CrateRelease>
|
pub releases: Vec<CrateRelease>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct QueryCrate<S>(pub S);
|
pub struct QueryCrate<S>(pub S);
|
||||||
|
|
||||||
impl<S> Service for QueryCrate<S>
|
impl<S> Service for QueryCrate<S>
|
||||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
where
|
||||||
S::Future: 'static
|
S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = CrateName;
|
type Request = CrateName;
|
||||||
type Response = QueryCrateResponse;
|
type Response = QueryCrateResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Box<dyn Future<Item=Self::Response, Error=Self::Error>>;
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn call(&self, crate_name: CrateName) -> Self::Future {
|
fn call(&self, crate_name: CrateName) -> Self::Future {
|
||||||
let lower_name = crate_name.as_ref().to_lowercase();
|
let lower_name = crate_name.as_ref().to_lowercase();
|
||||||
|
|
||||||
let path = match lower_name.len() {
|
let path = match lower_name.len() {
|
||||||
|
@ -80,8 +92,8 @@ impl<S> Service for QueryCrate<S>
|
||||||
_ => format!("{}/{}/{}", &lower_name[0..2], &lower_name[2..4], lower_name),
|
_ => format!("{}/{}/{}", &lower_name[0..2], &lower_name[2..4], lower_name),
|
||||||
};
|
};
|
||||||
|
|
||||||
let uri = try_future_box!(format!("{}/master/{}", CRATES_INDEX_BASE_URI, path)
|
let uri =
|
||||||
.parse::<Uri>());
|
try_future_box!(format!("{}/master/{}", CRATES_INDEX_BASE_URI, path).parse::<Uri>());
|
||||||
|
|
||||||
let request = Request::new(Method::Get, uri.clone());
|
let request = Request::new(Method::Get, uri.clone());
|
||||||
|
|
||||||
|
@ -94,7 +106,8 @@ impl<S> Service for QueryCrate<S>
|
||||||
let body_future = response.body().concat2().from_err();
|
let body_future = response.body().concat2().from_err();
|
||||||
let decode_future = body_future.and_then(move |body| {
|
let decode_future = body_future.and_then(move |body| {
|
||||||
let string_body = str::from_utf8(body.as_ref())?;
|
let string_body = str::from_utf8(body.as_ref())?;
|
||||||
let packages = string_body.lines()
|
let packages = string_body
|
||||||
|
.lines()
|
||||||
.map(|s| s.trim())
|
.map(|s| s.trim())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.map(|s| serde_json::from_str::<RegistryPackage>(s))
|
.map(|s| serde_json::from_str::<RegistryPackage>(s))
|
||||||
|
@ -112,38 +125,48 @@ impl<S> Service for QueryCrate<S>
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SummaryResponseDetail {
|
struct SummaryResponseDetail {
|
||||||
name: String,
|
name: String,
|
||||||
max_version: Version
|
max_version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SummaryResponse {
|
struct SummaryResponse {
|
||||||
most_downloaded: Vec<SummaryResponseDetail>
|
most_downloaded: Vec<SummaryResponseDetail>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_summary(response: SummaryResponse) -> Result<Vec<CratePath>, Error> {
|
fn convert_summary(response: SummaryResponse) -> Result<Vec<CratePath>, Error> {
|
||||||
response.most_downloaded.into_iter().map(|detail| {
|
response
|
||||||
let name = detail.name.parse()?;
|
.most_downloaded
|
||||||
Ok(CratePath { name, version: detail.max_version })
|
.into_iter()
|
||||||
}).collect()
|
.map(|detail| {
|
||||||
|
let name = detail.name.parse()?;
|
||||||
|
Ok(CratePath {
|
||||||
|
name,
|
||||||
|
version: detail.max_version,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GetPopularCrates<S>(pub S);
|
pub struct GetPopularCrates<S>(pub S);
|
||||||
|
|
||||||
impl<S> Service for GetPopularCrates<S>
|
impl<S> Service for GetPopularCrates<S>
|
||||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
where
|
||||||
S::Future: 'static
|
S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = ();
|
type Request = ();
|
||||||
type Response = Vec<CratePath>;
|
type Response = Vec<CratePath>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Box<dyn Future<Item=Self::Response, Error=Self::Error>>;
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn call(&self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let service = self.0.clone();
|
let service = self.0.clone();
|
||||||
|
|
||||||
let uri_future = format!("{}/summary", CRATES_API_BASE_URI)
|
let uri_future = format!("{}/summary", CRATES_API_BASE_URI)
|
||||||
.parse::<Uri>().into_future().from_err();
|
.parse::<Uri>()
|
||||||
|
.into_future()
|
||||||
|
.from_err();
|
||||||
|
|
||||||
Box::new(uri_future.and_then(move |uri| {
|
Box::new(uri_future.and_then(move |uri| {
|
||||||
let request = Request::new(Method::Get, uri.clone());
|
let request = Request::new(Method::Get, uri.clone());
|
||||||
|
@ -151,7 +174,11 @@ impl<S> Service for GetPopularCrates<S>
|
||||||
service.call(request).from_err().and_then(move |response| {
|
service.call(request).from_err().and_then(move |response| {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
future::Either::A(future::err(format_err!("Status code {} for URI {}", status, uri)))
|
future::Either::A(future::err(format_err!(
|
||||||
|
"Status code {} for URI {}",
|
||||||
|
status,
|
||||||
|
uri
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
let body_future = response.body().concat2().from_err();
|
let body_future = response.body().concat2().from_err();
|
||||||
let decode_future = body_future.and_then(|body| {
|
let decode_future = body_future.and_then(|body| {
|
||||||
|
|
|
@ -1,58 +1,64 @@
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Future, Stream};
|
use futures::{Future, Stream};
|
||||||
use hyper::{Error as HyperError, Method, Request, Response, Uri};
|
|
||||||
use hyper::header::UserAgent;
|
use hyper::header::UserAgent;
|
||||||
|
use hyper::{Error as HyperError, Method, Request, Response, Uri};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
use serde::Deserialize;
|
||||||
use tokio_service::Service;
|
use tokio_service::Service;
|
||||||
use serde_json;
|
|
||||||
|
|
||||||
use crate::models::repo::{Repository, RepoPath};
|
use crate::models::repo::{RepoPath, Repository};
|
||||||
|
|
||||||
const GITHUB_API_BASE_URI: &'static str = "https://api.github.com";
|
const GITHUB_API_BASE_URI: &'static str = "https://api.github.com";
|
||||||
const GITHUB_USER_CONTENT_BASE_URI: &'static str = "https://raw.githubusercontent.com";
|
const GITHUB_USER_CONTENT_BASE_URI: &'static str = "https://raw.githubusercontent.com";
|
||||||
|
|
||||||
pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> {
|
pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> {
|
||||||
let path_str: &str = path.as_ref();
|
let path_str: &str = path.as_ref();
|
||||||
Ok(format!("{}/{}/{}/HEAD/{}",
|
Ok(format!(
|
||||||
|
"{}/{}/{}/HEAD/{}",
|
||||||
GITHUB_USER_CONTENT_BASE_URI,
|
GITHUB_USER_CONTENT_BASE_URI,
|
||||||
repo_path.qual.as_ref(),
|
repo_path.qual.as_ref(),
|
||||||
repo_path.name.as_ref(),
|
repo_path.name.as_ref(),
|
||||||
path_str
|
path_str
|
||||||
).parse::<Uri>()?)
|
)
|
||||||
|
.parse::<Uri>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct GithubSearchResponse {
|
struct GithubSearchResponse {
|
||||||
items: Vec<GithubRepo>
|
items: Vec<GithubRepo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct GithubRepo {
|
struct GithubRepo {
|
||||||
name: String,
|
name: String,
|
||||||
owner: GithubOwner,
|
owner: GithubOwner,
|
||||||
description: String
|
description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct GithubOwner {
|
struct GithubOwner {
|
||||||
login: String
|
login: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GetPopularRepos<S>(pub S);
|
pub struct GetPopularRepos<S>(pub S);
|
||||||
|
|
||||||
impl<S> Service for GetPopularRepos<S>
|
impl<S> Service for GetPopularRepos<S>
|
||||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
where
|
||||||
S::Future: 'static
|
S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = ();
|
type Request = ();
|
||||||
type Response = Vec<Repository>;
|
type Response = Vec<Repository>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Box<dyn Future<Item=Self::Response, Error=Self::Error>>;
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn call(&self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let uri = try_future_box!(format!("{}/search/repositories?q=language:rust&sort=stars", GITHUB_API_BASE_URI)
|
let uri = try_future_box!(format!(
|
||||||
.parse::<Uri>());
|
"{}/search/repositories?q=language:rust&sort=stars",
|
||||||
|
GITHUB_API_BASE_URI
|
||||||
|
)
|
||||||
|
.parse::<Uri>());
|
||||||
|
|
||||||
let mut request = Request::new(Method::Get, uri);
|
let mut request = Request::new(Method::Get, uri);
|
||||||
request.headers_mut().set(UserAgent::new("deps.rs"));
|
request.headers_mut().set(UserAgent::new("deps.rs"));
|
||||||
|
@ -60,18 +66,31 @@ impl<S> Service for GetPopularRepos<S>
|
||||||
Box::new(self.0.call(request).from_err().and_then(|response| {
|
Box::new(self.0.call(request).from_err().and_then(|response| {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
try_future!(Err(format_err!("Status code {} for popular repo search", status)));
|
try_future!(Err(format_err!(
|
||||||
|
"Status code {} for popular repo search",
|
||||||
|
status
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_future = response.body().concat2().from_err();
|
let body_future = response.body().concat2().from_err();
|
||||||
let decode_future = body_future
|
let decode_future = body_future
|
||||||
.and_then(|body| serde_json::from_slice(body.as_ref()).map_err(|err| err.into()));
|
.and_then(|body| serde_json::from_slice(body.as_ref()).map_err(|err| err.into()));
|
||||||
decode_future.and_then(|search_response: GithubSearchResponse| {
|
decode_future
|
||||||
search_response.items.into_iter().map(|item| {
|
.and_then(|search_response: GithubSearchResponse| {
|
||||||
let path = RepoPath::from_parts("github", &item.owner.login, &item.name)?;
|
search_response
|
||||||
Ok(Repository { path, description: item.description })
|
.items
|
||||||
}).collect::<Result<Vec<_>, _>>()
|
.into_iter()
|
||||||
}).into()
|
.map(|item| {
|
||||||
|
let path =
|
||||||
|
RepoPath::from_parts("github", &item.owner.login, &item.name)?;
|
||||||
|
Ok(Repository {
|
||||||
|
path,
|
||||||
|
description: item.description,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
})
|
||||||
|
.into()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use failure::Error;
|
||||||
use hyper::Uri;
|
use hyper::Uri;
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use failure::Error;
|
|
||||||
|
|
||||||
use crate::models::repo::RepoPath;
|
use crate::models::repo::RepoPath;
|
||||||
|
|
||||||
|
@ -15,10 +15,12 @@ pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<
|
||||||
} else {
|
} else {
|
||||||
path_str
|
path_str
|
||||||
};
|
};
|
||||||
Ok(format!("{}/{}/{}/raw/HEAD/{}",
|
Ok(format!(
|
||||||
|
"{}/{}/{}/raw/HEAD/{}",
|
||||||
GITLAB_USER_CONTENT_BASE_URI,
|
GITLAB_USER_CONTENT_BASE_URI,
|
||||||
repo_path.qual.as_ref(),
|
repo_path.qual.as_ref(),
|
||||||
repo_path.name.as_ref(),
|
repo_path.name.as_ref(),
|
||||||
slash_path
|
slash_path
|
||||||
).parse::<Uri>()?)
|
)
|
||||||
|
.parse::<Uri>()?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use hyper::{Error as HyperError, Method, Request, Response};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use tokio_service::Service;
|
use tokio_service::Service;
|
||||||
|
|
||||||
use crate::models::repo::{RepoSite, RepoPath};
|
use crate::models::repo::{RepoPath, RepoSite};
|
||||||
|
|
||||||
pub mod bitbucket;
|
pub mod bitbucket;
|
||||||
pub mod crates;
|
pub mod crates;
|
||||||
|
@ -16,26 +16,21 @@ pub mod rustsec;
|
||||||
pub struct RetrieveFileAtPath<S>(pub S);
|
pub struct RetrieveFileAtPath<S>(pub S);
|
||||||
|
|
||||||
impl<S> Service for RetrieveFileAtPath<S>
|
impl<S> Service for RetrieveFileAtPath<S>
|
||||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
where
|
||||||
S::Future: 'static
|
S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = (RepoPath, RelativePathBuf);
|
type Request = (RepoPath, RelativePathBuf);
|
||||||
type Response = String;
|
type Response = String;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Box<dyn Future<Item=Self::Response, Error=Self::Error>>;
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn call(&self, req: Self::Request) -> Self::Future {
|
fn call(&self, req: Self::Request) -> Self::Future {
|
||||||
let (repo_path, path) = req;
|
let (repo_path, path) = req;
|
||||||
let uri = match &repo_path.site {
|
let uri = match &repo_path.site {
|
||||||
&RepoSite::Github => {
|
&RepoSite::Github => try_future_box!(github::get_manifest_uri(&repo_path, &path)),
|
||||||
try_future_box!(github::get_manifest_uri(&repo_path, &path))
|
&RepoSite::Gitlab => try_future_box!(gitlab::get_manifest_uri(&repo_path, &path)),
|
||||||
},
|
&RepoSite::Bitbucket => try_future_box!(bitbucket::get_manifest_uri(&repo_path, &path)),
|
||||||
&RepoSite::Gitlab => {
|
|
||||||
try_future_box!(gitlab::get_manifest_uri(&repo_path, &path))
|
|
||||||
},
|
|
||||||
&RepoSite::Bitbucket => {
|
|
||||||
try_future_box!(bitbucket::get_manifest_uri(&repo_path, &path))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = Request::new(Method::Get, uri.clone());
|
let request = Request::new(Method::Get, uri.clone());
|
||||||
|
|
|
@ -2,23 +2,24 @@ use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Future, IntoFuture, Stream, future};
|
use futures::{future, Future, IntoFuture, Stream};
|
||||||
use hyper::{Error as HyperError, Method, Request, Response};
|
use hyper::{Error as HyperError, Method, Request, Response};
|
||||||
use rustsec::ADVISORY_DB_URL;
|
|
||||||
use rustsec::db::AdvisoryDatabase;
|
use rustsec::db::AdvisoryDatabase;
|
||||||
|
use rustsec::ADVISORY_DB_URL;
|
||||||
use tokio_service::Service;
|
use tokio_service::Service;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FetchAdvisoryDatabase<S>(pub S);
|
pub struct FetchAdvisoryDatabase<S>(pub S);
|
||||||
|
|
||||||
impl<S> Service for FetchAdvisoryDatabase<S>
|
impl<S> Service for FetchAdvisoryDatabase<S>
|
||||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
where
|
||||||
S::Future: 'static
|
S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = ();
|
type Request = ();
|
||||||
type Response = Arc<AdvisoryDatabase>;
|
type Response = Arc<AdvisoryDatabase>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Box<dyn Future<Item=Self::Response, Error=Self::Error>>;
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn call(&self, _req: ()) -> Self::Future {
|
fn call(&self, _req: ()) -> Self::Future {
|
||||||
let service = self.0.clone();
|
let service = self.0.clone();
|
||||||
|
@ -31,11 +32,17 @@ impl<S> Service for FetchAdvisoryDatabase<S>
|
||||||
service.call(request).from_err().and_then(|response| {
|
service.call(request).from_err().and_then(|response| {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
future::Either::A(future::err(format_err!("Status code {} when fetching advisory db", status)))
|
future::Either::A(future::err(format_err!(
|
||||||
|
"Status code {} when fetching advisory db",
|
||||||
|
status
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
let body_future = response.body().concat2().from_err();
|
let body_future = response.body().concat2().from_err();
|
||||||
let decode_future = body_future
|
let decode_future = body_future.and_then(|body| {
|
||||||
.and_then(|body| Ok(Arc::new(AdvisoryDatabase::from_toml(str::from_utf8(&body)?)?)));
|
Ok(Arc::new(AdvisoryDatabase::from_toml(str::from_utf8(
|
||||||
|
&body,
|
||||||
|
)?)?))
|
||||||
|
});
|
||||||
future::Either::B(decode_future)
|
future::Either::B(decode_future)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate try_future;
|
extern crate try_future;
|
||||||
|
|
|
@ -9,14 +9,14 @@ use semver::{Version, VersionReq};
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct CratePath {
|
pub struct CratePath {
|
||||||
pub name: CrateName,
|
pub name: CrateName,
|
||||||
pub version: Version
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CratePath {
|
impl CratePath {
|
||||||
pub fn from_parts(name: &str, version: &str) -> Result<CratePath, Error> {
|
pub fn from_parts(name: &str, version: &str) -> Result<CratePath, Error> {
|
||||||
Ok(CratePath {
|
Ok(CratePath {
|
||||||
name: name.parse()?,
|
name: name.parse()?,
|
||||||
version: version.parse()?
|
version: version.parse()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,9 @@ impl FromStr for CrateName {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<CrateName, Error> {
|
fn from_str(input: &str) -> Result<CrateName, Error> {
|
||||||
let is_valid = input.chars().all(|c| {
|
let is_valid = input
|
||||||
c.is_ascii_alphanumeric() || c == '_' || c == '-'
|
.chars()
|
||||||
});
|
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-');
|
||||||
|
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
Err(format_err!("failed to validate crate name: {}", input))
|
Err(format_err!("failed to validate crate name: {}", input))
|
||||||
|
@ -63,13 +63,13 @@ pub struct CrateRelease {
|
||||||
pub name: CrateName,
|
pub name: CrateName,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub deps: CrateDeps,
|
pub deps: CrateDeps,
|
||||||
pub yanked: bool
|
pub yanked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum CrateDep {
|
pub enum CrateDep {
|
||||||
External(VersionReq),
|
External(VersionReq),
|
||||||
Internal(RelativePathBuf)
|
Internal(RelativePathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrateDep {
|
impl CrateDep {
|
||||||
|
@ -86,7 +86,7 @@ impl CrateDep {
|
||||||
pub struct CrateDeps {
|
pub struct CrateDeps {
|
||||||
pub main: IndexMap<CrateName, CrateDep>,
|
pub main: IndexMap<CrateName, CrateDep>,
|
||||||
pub dev: IndexMap<CrateName, CrateDep>,
|
pub dev: IndexMap<CrateName, CrateDep>,
|
||||||
pub build: IndexMap<CrateName, CrateDep>
|
pub build: IndexMap<CrateName, CrateDep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -94,7 +94,7 @@ pub struct AnalyzedDependency {
|
||||||
pub required: VersionReq,
|
pub required: VersionReq,
|
||||||
pub latest_that_matches: Option<Version>,
|
pub latest_that_matches: Option<Version>,
|
||||||
pub latest: Option<Version>,
|
pub latest: Option<Version>,
|
||||||
pub insecure: bool
|
pub insecure: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalyzedDependency {
|
impl AnalyzedDependency {
|
||||||
|
@ -103,7 +103,7 @@ impl AnalyzedDependency {
|
||||||
required,
|
required,
|
||||||
latest_that_matches: None,
|
latest_that_matches: None,
|
||||||
latest: None,
|
latest: None,
|
||||||
insecure: false
|
insecure: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,32 +116,44 @@ impl AnalyzedDependency {
|
||||||
pub struct AnalyzedDependencies {
|
pub struct AnalyzedDependencies {
|
||||||
pub main: IndexMap<CrateName, AnalyzedDependency>,
|
pub main: IndexMap<CrateName, AnalyzedDependency>,
|
||||||
pub dev: IndexMap<CrateName, AnalyzedDependency>,
|
pub dev: IndexMap<CrateName, AnalyzedDependency>,
|
||||||
pub build: IndexMap<CrateName, AnalyzedDependency>
|
pub build: IndexMap<CrateName, AnalyzedDependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalyzedDependencies {
|
impl AnalyzedDependencies {
|
||||||
pub fn new(deps: &CrateDeps) -> AnalyzedDependencies {
|
pub fn new(deps: &CrateDeps) -> AnalyzedDependencies {
|
||||||
let main = deps.main.iter().filter_map(|(name, dep)| {
|
let main = deps
|
||||||
if let &CrateDep::External(ref req) = dep {
|
.main
|
||||||
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
.iter()
|
||||||
} else {
|
.filter_map(|(name, dep)| {
|
||||||
None
|
if let &CrateDep::External(ref req) = dep {
|
||||||
}
|
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
||||||
}).collect();
|
} else {
|
||||||
let dev = deps.dev.iter().filter_map(|(name, dep)| {
|
None
|
||||||
if let &CrateDep::External(ref req) = dep {
|
}
|
||||||
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
})
|
||||||
} else {
|
.collect();
|
||||||
None
|
let dev = deps
|
||||||
}
|
.dev
|
||||||
}).collect();
|
.iter()
|
||||||
let build = deps.build.iter().filter_map(|(name, dep)| {
|
.filter_map(|(name, dep)| {
|
||||||
if let &CrateDep::External(ref req) = dep {
|
if let &CrateDep::External(ref req) = dep {
|
||||||
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect();
|
})
|
||||||
|
.collect();
|
||||||
|
let build = deps
|
||||||
|
.build
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(name, dep)| {
|
||||||
|
if let &CrateDep::External(ref req) = dep {
|
||||||
|
Some((name.clone(), AnalyzedDependency::new(req.clone())))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
AnalyzedDependencies { main, dev, build }
|
AnalyzedDependencies { main, dev, build }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,38 +162,35 @@ impl AnalyzedDependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_outdated(&self) -> usize {
|
pub fn count_outdated(&self) -> usize {
|
||||||
let main_outdated = self.main.iter()
|
let main_outdated = self
|
||||||
|
.main
|
||||||
|
.iter()
|
||||||
.filter(|&(_, dep)| dep.is_outdated())
|
.filter(|&(_, dep)| dep.is_outdated())
|
||||||
.count();
|
.count();
|
||||||
let dev_outdated = self.dev.iter()
|
let dev_outdated = self
|
||||||
|
.dev
|
||||||
|
.iter()
|
||||||
.filter(|&(_, dep)| dep.is_outdated())
|
.filter(|&(_, dep)| dep.is_outdated())
|
||||||
.count();
|
.count();
|
||||||
let build_outdated = self.build.iter()
|
let build_outdated = self
|
||||||
|
.build
|
||||||
|
.iter()
|
||||||
.filter(|&(_, dep)| dep.is_outdated())
|
.filter(|&(_, dep)| dep.is_outdated())
|
||||||
.count();
|
.count();
|
||||||
main_outdated + dev_outdated + build_outdated
|
main_outdated + dev_outdated + build_outdated
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_insecure(&self) -> usize {
|
pub fn count_insecure(&self) -> usize {
|
||||||
let main_insecure = self.main.iter()
|
let main_insecure = self.main.iter().filter(|&(_, dep)| dep.insecure).count();
|
||||||
.filter(|&(_, dep)| dep.insecure)
|
let dev_insecure = self.dev.iter().filter(|&(_, dep)| dep.insecure).count();
|
||||||
.count();
|
let build_insecure = self.build.iter().filter(|&(_, dep)| dep.insecure).count();
|
||||||
let dev_insecure = self.dev.iter()
|
|
||||||
.filter(|&(_, dep)| dep.insecure)
|
|
||||||
.count();
|
|
||||||
let build_insecure = self.build.iter()
|
|
||||||
.filter(|&(_, dep)| dep.insecure)
|
|
||||||
.count();
|
|
||||||
main_insecure + dev_insecure + build_insecure
|
main_insecure + dev_insecure + build_insecure
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn any_outdated(&self) -> bool {
|
pub fn any_outdated(&self) -> bool {
|
||||||
let main_any_outdated = self.main.iter()
|
let main_any_outdated = self.main.iter().any(|(_, dep)| dep.is_outdated());
|
||||||
.any(|(_, dep)| dep.is_outdated());
|
let dev_any_outdated = self.dev.iter().any(|(_, dep)| dep.is_outdated());
|
||||||
let dev_any_outdated = self.dev.iter()
|
let build_any_outdated = self.build.iter().any(|(_, dep)| dep.is_outdated());
|
||||||
.any(|(_, dep)| dep.is_outdated());
|
|
||||||
let build_any_outdated = self.build.iter()
|
|
||||||
.any(|(_, dep)| dep.is_outdated());
|
|
||||||
main_any_outdated || dev_any_outdated || build_any_outdated
|
main_any_outdated || dev_any_outdated || build_any_outdated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +198,12 @@ impl AnalyzedDependencies {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CrateManifest {
|
pub enum CrateManifest {
|
||||||
Package(CrateName, CrateDeps),
|
Package(CrateName, CrateDeps),
|
||||||
Workspace { members: Vec<RelativePathBuf> },
|
Workspace {
|
||||||
Mixed { name: CrateName, deps: CrateDeps, members: Vec<RelativePathBuf> }
|
members: Vec<RelativePathBuf>,
|
||||||
|
},
|
||||||
|
Mixed {
|
||||||
|
name: CrateName,
|
||||||
|
deps: CrateDeps,
|
||||||
|
members: Vec<RelativePathBuf>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@ pub mod repo;
|
||||||
|
|
||||||
pub enum SubjectPath {
|
pub enum SubjectPath {
|
||||||
Repo(self::repo::RepoPath),
|
Repo(self::repo::RepoPath),
|
||||||
Crate(self::crates::CratePath)
|
Crate(self::crates::CratePath),
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ use failure::Error;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Repository {
|
pub struct Repository {
|
||||||
pub path: RepoPath,
|
pub path: RepoPath,
|
||||||
pub description: String
|
pub description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct RepoPath {
|
pub struct RepoPath {
|
||||||
pub site: RepoSite,
|
pub site: RepoSite,
|
||||||
pub qual: RepoQualifier,
|
pub qual: RepoQualifier,
|
||||||
pub name: RepoName
|
pub name: RepoName,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepoPath {
|
impl RepoPath {
|
||||||
|
@ -20,7 +20,7 @@ impl RepoPath {
|
||||||
Ok(RepoPath {
|
Ok(RepoPath {
|
||||||
site: site.parse()?,
|
site: site.parse()?,
|
||||||
qual: qual.parse()?,
|
qual: qual.parse()?,
|
||||||
name: name.parse()?
|
name: name.parse()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ impl FromStr for RepoSite {
|
||||||
"github" => Ok(RepoSite::Github),
|
"github" => Ok(RepoSite::Github),
|
||||||
"gitlab" => Ok(RepoSite::Gitlab),
|
"gitlab" => Ok(RepoSite::Gitlab),
|
||||||
"bitbucket" => Ok(RepoSite::Bitbucket),
|
"bitbucket" => Ok(RepoSite::Bitbucket),
|
||||||
_ => Err(format_err!("unknown repo site identifier"))
|
_ => Err(format_err!("unknown repo site identifier")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ impl FromStr for RepoQualifier {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<RepoQualifier, Error> {
|
fn from_str(input: &str) -> Result<RepoQualifier, Error> {
|
||||||
let is_valid = input.chars().all(|c| {
|
let is_valid = input
|
||||||
c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_'
|
.chars()
|
||||||
});
|
.all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_');
|
||||||
|
|
||||||
ensure!(is_valid, "invalid repo qualifier");
|
ensure!(is_valid, "invalid repo qualifier");
|
||||||
Ok(RepoQualifier(input.to_string()))
|
Ok(RepoQualifier(input.to_string()))
|
||||||
|
@ -94,9 +94,9 @@ impl FromStr for RepoName {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<RepoName, Error> {
|
fn from_str(input: &str) -> Result<RepoName, Error> {
|
||||||
let is_valid = input.chars().all(|c| {
|
let is_valid = input
|
||||||
c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_'
|
.chars()
|
||||||
});
|
.all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_');
|
||||||
|
|
||||||
ensure!(is_valid, "invalid repo name");
|
ensure!(is_valid, "invalid repo name");
|
||||||
Ok(RepoName(input.to_string()))
|
Ok(RepoName(input.to_string()))
|
||||||
|
|
|
@ -2,33 +2,33 @@ use failure::Error;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use toml;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::crates::{CrateName, CrateDep, CrateDeps, CrateManifest};
|
use crate::models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct CargoTomlComplexDependency {
|
struct CargoTomlComplexDependency {
|
||||||
git: Option<String>,
|
git: Option<String>,
|
||||||
path: Option<RelativePathBuf>,
|
path: Option<RelativePathBuf>,
|
||||||
version: Option<String>
|
version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum CargoTomlDependency {
|
enum CargoTomlDependency {
|
||||||
Simple(String),
|
Simple(String),
|
||||||
Complex(CargoTomlComplexDependency)
|
Complex(CargoTomlComplexDependency),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct CargoTomlPackage {
|
struct CargoTomlPackage {
|
||||||
name: String
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct CargoTomlWorkspace {
|
struct CargoTomlWorkspace {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
members: Vec<RelativePathBuf>
|
members: Vec<RelativePathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -44,32 +44,42 @@ struct CargoToml {
|
||||||
dev_dependencies: IndexMap<String, CargoTomlDependency>,
|
dev_dependencies: IndexMap<String, CargoTomlDependency>,
|
||||||
#[serde(rename = "build-dependencies")]
|
#[serde(rename = "build-dependencies")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
build_dependencies: IndexMap<String, CargoTomlDependency>
|
build_dependencies: IndexMap<String, CargoTomlDependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_dependency(cargo_dep: (String, CargoTomlDependency)) -> Option<Result<(CrateName, CrateDep), Error>> {
|
fn convert_dependency(
|
||||||
|
cargo_dep: (String, CargoTomlDependency),
|
||||||
|
) -> Option<Result<(CrateName, CrateDep), Error>> {
|
||||||
match cargo_dep {
|
match cargo_dep {
|
||||||
(name, CargoTomlDependency::Simple(string)) => {
|
(name, CargoTomlDependency::Simple(string)) => Some(
|
||||||
Some(name.parse::<CrateName>().map_err(|err| err.into()).and_then(|parsed_name| {
|
name.parse::<CrateName>()
|
||||||
string.parse::<VersionReq>().map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
.map(|version| (parsed_name, CrateDep::External(version)))
|
.and_then(|parsed_name| {
|
||||||
}))
|
string
|
||||||
}
|
.parse::<VersionReq>()
|
||||||
|
.map_err(|err| err.into())
|
||||||
|
.map(|version| (parsed_name, CrateDep::External(version)))
|
||||||
|
}),
|
||||||
|
),
|
||||||
(name, CargoTomlDependency::Complex(cplx)) => {
|
(name, CargoTomlDependency::Complex(cplx)) => {
|
||||||
if cplx.git.is_some() {
|
if cplx.git.is_some() {
|
||||||
None
|
None
|
||||||
} else if cplx.path.is_some() {
|
} else if cplx.path.is_some() {
|
||||||
cplx.path.map(|path| {
|
cplx.path.map(|path| {
|
||||||
name.parse::<CrateName>().map_err(|err| err.into()).map(|parsed_name| {
|
name.parse::<CrateName>()
|
||||||
(parsed_name, CrateDep::Internal(path))
|
.map_err(|err| err.into())
|
||||||
})
|
.map(|parsed_name| (parsed_name, CrateDep::Internal(path)))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
cplx.version.map(|string| {
|
cplx.version.map(|string| {
|
||||||
name.parse::<CrateName>().map_err(|err| err.into()).and_then(|parsed_name| {
|
name.parse::<CrateName>()
|
||||||
string.parse::<VersionReq>().map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
.map(|version| (parsed_name, CrateDep::External(version)))
|
.and_then(|parsed_name| {
|
||||||
})
|
string
|
||||||
|
.parse::<VersionReq>()
|
||||||
|
.map_err(|err| err.into())
|
||||||
|
.map(|version| (parsed_name, CrateDep::External(version)))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,20 +92,29 @@ pub fn parse_manifest_toml(input: &str) -> Result<CrateManifest, Error> {
|
||||||
let mut package_part = None;
|
let mut package_part = None;
|
||||||
let mut workspace_part = None;
|
let mut workspace_part = None;
|
||||||
|
|
||||||
if let Some(package) = cargo_toml.package {
|
if let Some(package) = cargo_toml.package {
|
||||||
let crate_name = package.name.parse::<CrateName>()?;
|
let crate_name = package.name.parse::<CrateName>()?;
|
||||||
|
|
||||||
let dependencies = cargo_toml.dependencies
|
let dependencies = cargo_toml
|
||||||
.into_iter().filter_map(convert_dependency).collect::<Result<IndexMap<_, _>, _>>()?;
|
.dependencies
|
||||||
let dev_dependencies = cargo_toml.dev_dependencies
|
.into_iter()
|
||||||
.into_iter().filter_map(convert_dependency).collect::<Result<IndexMap<_, _>, _>>()?;
|
.filter_map(convert_dependency)
|
||||||
let build_dependencies = cargo_toml.build_dependencies
|
.collect::<Result<IndexMap<_, _>, _>>()?;
|
||||||
.into_iter().filter_map(convert_dependency).collect::<Result<IndexMap<_, _>, _>>()?;
|
let dev_dependencies = cargo_toml
|
||||||
|
.dev_dependencies
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(convert_dependency)
|
||||||
|
.collect::<Result<IndexMap<_, _>, _>>()?;
|
||||||
|
let build_dependencies = cargo_toml
|
||||||
|
.build_dependencies
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(convert_dependency)
|
||||||
|
.collect::<Result<IndexMap<_, _>, _>>()?;
|
||||||
|
|
||||||
let deps = CrateDeps {
|
let deps = CrateDeps {
|
||||||
main: dependencies,
|
main: dependencies,
|
||||||
dev: dev_dependencies,
|
dev: dev_dependencies,
|
||||||
build: build_dependencies
|
build: build_dependencies,
|
||||||
};
|
};
|
||||||
|
|
||||||
package_part = Some((crate_name, deps));
|
package_part = Some((crate_name, deps));
|
||||||
|
@ -106,21 +125,23 @@ pub fn parse_manifest_toml(input: &str) -> Result<CrateManifest, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match (package_part, workspace_part) {
|
match (package_part, workspace_part) {
|
||||||
(Some((name, deps)), None) =>
|
(Some((name, deps)), None) => Ok(CrateManifest::Package(name, deps)),
|
||||||
Ok(CrateManifest::Package(name, deps)),
|
(None, Some(members)) => Ok(CrateManifest::Workspace { members }),
|
||||||
(None, Some(members)) =>
|
(Some((name, deps)), Some(members)) => Ok(CrateManifest::Mixed {
|
||||||
Ok(CrateManifest::Workspace { members }),
|
name,
|
||||||
(Some((name, deps)), Some(members)) =>
|
deps,
|
||||||
Ok(CrateManifest::Mixed { name, deps, members }),
|
members,
|
||||||
(None, None) =>
|
}),
|
||||||
Err(format_err!("neither workspace nor package found in manifest"))
|
(None, None) => Err(format_err!(
|
||||||
|
"neither workspace nor package found in manifest"
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use models::crates::CrateManifest;
|
|
||||||
use super::parse_manifest_toml;
|
use super::parse_manifest_toml;
|
||||||
|
use models::crates::CrateManifest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_workspace_without_members_declaration() {
|
fn parse_workspace_without_members_declaration() {
|
||||||
|
@ -136,14 +157,18 @@ symbolic-common = { version = "2.0.6", path = "common" }
|
||||||
let manifest = parse_manifest_toml(toml).unwrap();
|
let manifest = parse_manifest_toml(toml).unwrap();
|
||||||
|
|
||||||
match manifest {
|
match manifest {
|
||||||
CrateManifest::Mixed { name, deps, members } => {
|
CrateManifest::Mixed {
|
||||||
|
name,
|
||||||
|
deps,
|
||||||
|
members,
|
||||||
|
} => {
|
||||||
assert_eq!(name.as_ref(), "symbolic");
|
assert_eq!(name.as_ref(), "symbolic");
|
||||||
assert_eq!(deps.main.len(), 1);
|
assert_eq!(deps.main.len(), 1);
|
||||||
assert_eq!(deps.dev.len(), 0);
|
assert_eq!(deps.dev.len(), 0);
|
||||||
assert_eq!(deps.build.len(), 0);
|
assert_eq!(deps.build.len(), 0);
|
||||||
assert_eq!(members.len(), 0);
|
assert_eq!(members.len(), 0);
|
||||||
},
|
}
|
||||||
_ => panic!("expected mixed manifest")
|
_ => panic!("expected mixed manifest"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
pub static STATIC_STYLE_CSS: &'static str =
|
pub static STATIC_STYLE_CSS: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
|
||||||
include_str!(concat!(env!("OUT_DIR"), "/style.css"));
|
pub static STATIC_FAVICON_PNG: &'static [u8; 1338] = include_bytes!("../../assets/favicon.png");
|
||||||
pub static STATIC_FAVICON_PNG: &'static [u8; 1338] =
|
|
||||||
include_bytes!("../../assets/favicon.png");
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use badge::{Badge, BadgeOptions};
|
use badge::{Badge, BadgeOptions};
|
||||||
use hyper::Response;
|
|
||||||
use hyper::header::ContentType;
|
use hyper::header::ContentType;
|
||||||
|
use hyper::Response;
|
||||||
|
|
||||||
use crate::engine::AnalyzeDependenciesOutcome;
|
use crate::engine::AnalyzeDependenciesOutcome;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ pub fn badge(analysis_outcome: Option<&AnalyzeDependenciesOutcome>) -> Badge {
|
||||||
BadgeOptions {
|
BadgeOptions {
|
||||||
subject: "dependencies".into(),
|
subject: "dependencies".into(),
|
||||||
status: "insecure".into(),
|
status: "insecure".into(),
|
||||||
color: "#e05d44".into()
|
color: "#e05d44".into(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (outdated, total) = outcome.outdated_ratio();
|
let (outdated, total) = outcome.outdated_ratio();
|
||||||
|
@ -20,30 +20,28 @@ pub fn badge(analysis_outcome: Option<&AnalyzeDependenciesOutcome>) -> Badge {
|
||||||
BadgeOptions {
|
BadgeOptions {
|
||||||
subject: "dependencies".into(),
|
subject: "dependencies".into(),
|
||||||
status: format!("{} of {} outdated", outdated, total),
|
status: format!("{} of {} outdated", outdated, total),
|
||||||
color: "#dfb317".into()
|
color: "#dfb317".into(),
|
||||||
}
|
}
|
||||||
} else if total > 0 {
|
} else if total > 0 {
|
||||||
BadgeOptions {
|
BadgeOptions {
|
||||||
subject: "dependencies".into(),
|
subject: "dependencies".into(),
|
||||||
status: "up to date".into(),
|
status: "up to date".into(),
|
||||||
color: "#4c1".into()
|
color: "#4c1".into(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BadgeOptions {
|
BadgeOptions {
|
||||||
subject: "dependencies".into(),
|
subject: "dependencies".into(),
|
||||||
status: "none".into(),
|
status: "none".into(),
|
||||||
color: "#4c1".into()
|
color: "#4c1".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => {
|
|
||||||
BadgeOptions {
|
|
||||||
subject: "dependencies".into(),
|
|
||||||
status: "unknown".into(),
|
|
||||||
color: "#9f9f9f".into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
None => BadgeOptions {
|
||||||
|
subject: "dependencies".into(),
|
||||||
|
status: "unknown".into(),
|
||||||
|
color: "#9f9f9f".into(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Badge::new(opts)
|
Badge::new(opts)
|
||||||
|
|
|
@ -2,18 +2,21 @@ use hyper::Response;
|
||||||
use maud::html;
|
use maud::html;
|
||||||
|
|
||||||
pub fn render(title: &str, descr: &str) -> Response {
|
pub fn render(title: &str, descr: &str) -> Response {
|
||||||
super::render_html(title, html! {
|
super::render_html(
|
||||||
section class="hero is-light" {
|
title,
|
||||||
div class="hero-head" { (super::render_navbar()) }
|
html! {
|
||||||
}
|
section class="hero is-light" {
|
||||||
section class="section" {
|
div class="hero-head" { (super::render_navbar()) }
|
||||||
div class="container" {
|
}
|
||||||
div class="notification is-danger" {
|
section class="section" {
|
||||||
p class="title is-3" { (title) }
|
div class="container" {
|
||||||
p { (descr) }
|
div class="notification is-danger" {
|
||||||
|
p class="title is-3" { (title) }
|
||||||
|
p { (descr) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
(super::render_footer(None))
|
||||||
(super::render_footer(None))
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use hyper::Response;
|
use hyper::Response;
|
||||||
use maud::{Markup, html};
|
use maud::{html, Markup};
|
||||||
|
|
||||||
use crate::models::repo::Repository;
|
|
||||||
use crate::models::crates::CratePath;
|
use crate::models::crates::CratePath;
|
||||||
|
use crate::models::repo::Repository;
|
||||||
|
|
||||||
fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Markup {
|
fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Markup {
|
||||||
html! {
|
html! {
|
||||||
|
@ -64,23 +64,26 @@ fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Response {
|
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Response {
|
||||||
super::render_html("Keep your dependencies up-to-date", html! {
|
super::render_html(
|
||||||
section class="hero is-light" {
|
"Keep your dependencies up-to-date",
|
||||||
div class="hero-head" { (super::render_navbar()) }
|
html! {
|
||||||
div class="hero-body" {
|
section class="hero is-light" {
|
||||||
div class="container" {
|
div class="hero-head" { (super::render_navbar()) }
|
||||||
p class="title is-1" { "Keep your dependencies up-to-date" }
|
div class="hero-body" {
|
||||||
p {
|
div class="container" {
|
||||||
"Deps.rs uses semantic versioning to detect outdated or insecure dependencies in your project's"
|
p class="title is-1" { "Keep your dependencies up-to-date" }
|
||||||
code { "Cargo.toml" }
|
p {
|
||||||
"."
|
"Deps.rs uses semantic versioning to detect outdated or insecure dependencies in your project's"
|
||||||
|
code { "Cargo.toml" }
|
||||||
|
"."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
section class="section" {
|
||||||
section class="section" {
|
div class="container" { (popular_table(popular_repos, popular_crates)) }
|
||||||
div class="container" { (popular_table(popular_repos, popular_crates)) }
|
}
|
||||||
}
|
(super::render_footer(None))
|
||||||
(super::render_footer(None))
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use hyper::Response;
|
|
||||||
use hyper::header::ContentType;
|
use hyper::header::ContentType;
|
||||||
use maud::{Markup, Render, html};
|
use hyper::Response;
|
||||||
|
use maud::{html, Markup, Render};
|
||||||
|
|
||||||
pub mod index;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod index;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
use super::super::SELF_BASE_URL;
|
use super::super::SELF_BASE_URL;
|
||||||
|
@ -47,7 +47,8 @@ fn render_navbar() -> Markup {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_footer(duration: Option<Duration>) -> Markup {
|
fn render_footer(duration: Option<Duration>) -> Markup {
|
||||||
let duration_millis = duration.map(|d| d.as_secs() * 1000 + (d.subsec_nanos() / 1000 / 1000) as u64);
|
let duration_millis =
|
||||||
|
duration.map(|d| d.as_secs() * 1000 + (d.subsec_nanos() / 1000 / 1000) as u64);
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
footer class="footer" {
|
footer class="footer" {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use hyper::Response;
|
use hyper::Response;
|
||||||
use maud::{Markup, html};
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use maud::{html, Markup};
|
||||||
|
|
||||||
use crate::engine::AnalyzeDependenciesOutcome;
|
use crate::engine::AnalyzeDependenciesOutcome;
|
||||||
use crate::models::crates::{CrateName, AnalyzedDependency, AnalyzedDependencies};
|
use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName};
|
||||||
use crate::models::SubjectPath;
|
|
||||||
use crate::models::repo::RepoSite;
|
use crate::models::repo::RepoSite;
|
||||||
|
use crate::models::SubjectPath;
|
||||||
|
|
||||||
use super::super::badge;
|
use super::super::badge;
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ fn get_site_icon(site: &RepoSite) -> &'static str {
|
||||||
match *site {
|
match *site {
|
||||||
RepoSite::Github => "fa-github",
|
RepoSite::Github => "fa-github",
|
||||||
RepoSite::Gitlab => "fa-gitlab",
|
RepoSite::Gitlab => "fa-gitlab",
|
||||||
RepoSite::Bitbucket => "fa-bitbucket"
|
RepoSite::Bitbucket => "fa-bitbucket",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ fn render_title(subject_path: &SubjectPath) -> Markup {
|
||||||
(format!(" {} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref()))
|
(format!(" {} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SubjectPath::Crate(ref crate_path) => {
|
SubjectPath::Crate(ref crate_path) => {
|
||||||
html! {
|
html! {
|
||||||
a href=(format!("https://crates.io/crates/{}/{}", crate_path.name.as_ref(), crate_path.version)) {
|
a href=(format!("https://crates.io/crates/{}/{}", crate_path.name.as_ref(), crate_path.version)) {
|
||||||
|
@ -144,18 +144,26 @@ fn render_failure(subject_path: SubjectPath) -> Markup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_success(analysis_outcome: AnalyzeDependenciesOutcome, subject_path: SubjectPath) -> Markup {
|
fn render_success(
|
||||||
|
analysis_outcome: AnalyzeDependenciesOutcome,
|
||||||
|
subject_path: SubjectPath,
|
||||||
|
) -> Markup {
|
||||||
let self_path = match subject_path {
|
let self_path = match subject_path {
|
||||||
SubjectPath::Repo(ref repo_path) =>
|
SubjectPath::Repo(ref repo_path) => format!(
|
||||||
format!("repo/{}/{}/{}", repo_path.site.as_ref(), repo_path.qual.as_ref(), repo_path.name.as_ref()),
|
"repo/{}/{}/{}",
|
||||||
SubjectPath::Crate(ref crate_path) =>
|
repo_path.site.as_ref(),
|
||||||
|
repo_path.qual.as_ref(),
|
||||||
|
repo_path.name.as_ref()
|
||||||
|
),
|
||||||
|
SubjectPath::Crate(ref crate_path) => {
|
||||||
format!("crate/{}/{}", crate_path.name.as_ref(), crate_path.version)
|
format!("crate/{}/{}", crate_path.name.as_ref(), crate_path.version)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let status_base_url = format!("{}/{}", &super::SELF_BASE_URL as &str, self_path);
|
let status_base_url = format!("{}/{}", &super::SELF_BASE_URL as &str, self_path);
|
||||||
|
|
||||||
let status_data_uri = badge::badge(Some(&analysis_outcome)).to_svg_data_uri();
|
let status_data_uri = badge::badge(Some(&analysis_outcome)).to_svg_data_uri();
|
||||||
|
|
||||||
let hero_class = if analysis_outcome.any_insecure() {
|
let hero_class = if analysis_outcome.any_insecure() {
|
||||||
"is-danger"
|
"is-danger"
|
||||||
} else if analysis_outcome.any_outdated() {
|
} else if analysis_outcome.any_outdated() {
|
||||||
"is-warning"
|
"is-warning"
|
||||||
|
@ -194,12 +202,17 @@ fn render_success(analysis_outcome: AnalyzeDependenciesOutcome, subject_path: Su
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(analysis_outcome: Option<AnalyzeDependenciesOutcome>, subject_path: SubjectPath) -> Response {
|
pub fn render(
|
||||||
|
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
||||||
|
subject_path: SubjectPath,
|
||||||
|
) -> Response {
|
||||||
let title = match subject_path {
|
let title = match subject_path {
|
||||||
SubjectPath::Repo(ref repo_path) =>
|
SubjectPath::Repo(ref repo_path) => {
|
||||||
format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref()),
|
format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref())
|
||||||
SubjectPath::Crate(ref crate_path) =>
|
}
|
||||||
|
SubjectPath::Crate(ref crate_path) => {
|
||||||
format!("{} {}", crate_path.name.as_ref(), crate_path.version)
|
format!("{} {}", crate_path.name.as_ref(), crate_path.version)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(outcome) = analysis_outcome {
|
if let Some(outcome) = analysis_outcome {
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pub mod html;
|
|
||||||
pub mod badge;
|
pub mod badge;
|
||||||
|
pub mod html;
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::{Future, Poll};
|
|
||||||
use futures::future::{FromErr, Shared, SharedItem};
|
use futures::future::{FromErr, Shared, SharedItem};
|
||||||
|
use futures::{Future, Poll};
|
||||||
use lru_cache::LruCache;
|
use lru_cache::LruCache;
|
||||||
use shared_failure::SharedFailure;
|
use shared_failure::SharedFailure;
|
||||||
use tokio_service::Service;
|
use tokio_service::Service;
|
||||||
|
|
||||||
pub struct Cache<S>
|
pub struct Cache<S>
|
||||||
where S: Service<Error=Error>,
|
where
|
||||||
S::Request: Hash + Eq
|
S: Service<Error = Error>,
|
||||||
|
S::Request: Hash + Eq,
|
||||||
{
|
{
|
||||||
inner: S,
|
inner: S,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
cache: Mutex<LruCache<S::Request, (Instant, Shared<FromErr<S::Future, SharedFailure>>)>>
|
cache: Mutex<LruCache<S::Request, (Instant, Shared<FromErr<S::Future, SharedFailure>>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Debug for Cache<S>
|
impl<S> Debug for Cache<S>
|
||||||
where S: Service<Error=Error> + Debug,
|
where
|
||||||
S::Request: Hash + Eq
|
S: Service<Error = Error> + Debug,
|
||||||
|
S::Request: Hash + Eq,
|
||||||
{
|
{
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
|
||||||
fmt.debug_struct("Cache")
|
fmt.debug_struct("Cache")
|
||||||
|
@ -33,21 +35,23 @@ impl<S> Debug for Cache<S>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Cache<S>
|
impl<S> Cache<S>
|
||||||
where S: Service<Error=Error>,
|
where
|
||||||
S::Request: Hash + Eq
|
S: Service<Error = Error>,
|
||||||
|
S::Request: Hash + Eq,
|
||||||
{
|
{
|
||||||
pub fn new(service: S, duration: Duration, capacity: usize) -> Cache<S> {
|
pub fn new(service: S, duration: Duration, capacity: usize) -> Cache<S> {
|
||||||
Cache {
|
Cache {
|
||||||
inner: service,
|
inner: service,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
cache: Mutex::new(LruCache::new(capacity))
|
cache: Mutex::new(LruCache::new(capacity)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Service for Cache<S>
|
impl<S> Service for Cache<S>
|
||||||
where S: Service<Error=Error>,
|
where
|
||||||
S::Request: Clone + Hash + Eq
|
S: Service<Error = Error>,
|
||||||
|
S::Request: Clone + Hash + Eq,
|
||||||
{
|
{
|
||||||
type Request = S::Request;
|
type Request = S::Request;
|
||||||
type Response = CachedItem<S::Response>;
|
type Response = CachedItem<S::Response>;
|
||||||
|
@ -70,23 +74,25 @@ impl<S> Service for Cache<S>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cached<F: Future<Error=Error>>(Shared<FromErr<F, SharedFailure>>);
|
pub struct Cached<F: Future<Error = Error>>(Shared<FromErr<F, SharedFailure>>);
|
||||||
|
|
||||||
impl<F> Debug for Cached<F>
|
impl<F> Debug for Cached<F>
|
||||||
where F: Future<Error=Error> + Debug,
|
where
|
||||||
F::Item: Debug
|
F: Future<Error = Error> + Debug,
|
||||||
|
F::Item: Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
|
||||||
self.0.fmt(fmt)
|
self.0.fmt(fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future<Error=Error>> Future for Cached<F> {
|
impl<F: Future<Error = Error>> Future for Cached<F> {
|
||||||
type Item = CachedItem<F::Item>;
|
type Item = CachedItem<F::Item>;
|
||||||
type Error = SharedFailure;
|
type Error = SharedFailure;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
self.0.poll()
|
self.0
|
||||||
|
.poll()
|
||||||
.map_err(|err| (*err).clone())
|
.map_err(|err| (*err).clone())
|
||||||
.map(|_async| _async.map(CachedItem))
|
.map(|_async| _async.map(CachedItem))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue