use crate::retriever::{self, docs_rs::Retriever};
use cargo_lock::Package;
use futures::FutureExt;
use licensebat_core::{
collector::{RetrievedDependencyStream, RetrievedDependencyStreamResult},
Collector, Comment, FileCollector, RetrievedDependency,
};
use std::{str::FromStr, sync::Arc};
use tracing::instrument;
#[derive(Debug)]
pub struct Rust<R: Retriever> {
retriever: R,
}
impl<R: Retriever> Rust<R> {
#[must_use]
pub fn new(retriever: R) -> Self {
Self { retriever }
}
}
impl Rust<retriever::DocsRs> {
#[must_use]
pub fn with_docs_rs_retriever(
client: reqwest::Client,
store: Arc<Option<askalono::Store>>,
) -> Self {
Self::new(retriever::DocsRs::new(client, store))
}
}
impl<R: Retriever> Collector for Rust<R> {
fn get_name(&self) -> String {
String::from("rust")
}
}
impl<R: Retriever> FileCollector for Rust<R> {
fn get_dependency_filename(&self) -> String {
String::from("Cargo.lock")
}
#[instrument(skip(self))]
fn get_dependencies(&self, dependency_file_content: &str) -> RetrievedDependencyStreamResult {
let lockfile = cargo_lock::Lockfile::from_str(dependency_file_content)?;
let futures = lockfile
.packages
.into_iter()
.map(|p| get_dependency(p, &self.retriever).boxed())
.collect();
Ok(RetrievedDependencyStream::new(futures))
}
}
async fn get_dependency<R: Retriever>(package: Package, retriever: &R) -> RetrievedDependency {
if let Some(source) = package.source {
if source.is_registry() {
#[allow(clippy::if_same_then_else)]
if source.is_default_registry() {
let dependency = licensebat_core::Dependency {
name: package.name.to_string(),
version: package.version.to_string(),
is_dev: None,
is_optional: None,
};
return retriever.get_dependency(dependency).await;
} else if source.is_remote_registry() {
} else {
}
}
if source.is_git() {
}
}
RetrievedDependency {
name: package.name.to_string(),
version: package.version.to_string(),
url: None,
dependency_type: crate::RUST.to_owned(),
validated: false,
is_valid: false,
is_ignored: false,
error: Some("Crate type not Supported".to_owned()),
licenses: None,
comment: Some(Comment::removable("Git, Local and Remote registries are not supported yet. We're working on it. We're marking this as invalid by default so you can check the validity of the license. Consider adding this dependency to the ignored list in the .licrc configuration file if you trust the source.")),
suggested_licenses: None,
is_dev: None,
is_optional: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::{
future::{ready, BoxFuture},
StreamExt,
};
use licensebat_core::Dependency;
#[derive(Debug)]
struct MockRetriever;
impl Retriever for MockRetriever {
type Response = BoxFuture<'static, RetrievedDependency>;
fn get_dependency(&self, dependency: Dependency) -> Self::Response {
ready(RetrievedDependency {
name: dependency.name.to_string(),
version: dependency.version.to_string(),
..RetrievedDependency::default()
})
.boxed()
}
}
fn build_collector() -> Rust<MockRetriever> {
Rust::new(MockRetriever)
}
#[tokio::test]
async fn it_works_for_crates_registry() {
let rust = build_collector();
let lock_content = r#"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
"#;
let mut deps = rust
.get_dependencies(&lock_content)
.unwrap()
.collect::<Vec<_>>()
.await;
assert_eq!(deps.len(), 1);
let dep = deps[0].as_mut().fuse().await;
assert_eq!(rust.get_name(), "rust");
assert_eq!(rust.get_dependency_filename(), "Cargo.lock");
assert_eq!(dep.name, "mime");
assert_eq!(dep.version, "0.3.16");
}
#[tokio::test]
async fn it_works_for_crates_registry_with_special_version() {
let rust = build_collector();
let lock_content = r#"
[[package]]
name = "mime"
version = "3.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
"#;
let mut deps = rust
.get_dependencies(&lock_content)
.unwrap()
.collect::<Vec<_>>()
.await;
assert_eq!(deps.len(), 1);
let dep = deps[0].as_mut().fuse().await;
assert_eq!(rust.get_name(), "rust");
assert_eq!(rust.get_dependency_filename(), "Cargo.lock");
assert_eq!(dep.name, "mime");
assert_eq!(dep.version, "3.0.0-beta.4");
}
#[tokio::test]
async fn git_is_not_implemented() {
let rust = build_collector();
let lock_content = r#"
[[package]]
name = "mime"
version = "3.0.0"
source = "git+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
"#;
let mut deps = rust
.get_dependencies(&lock_content)
.unwrap()
.collect::<Vec<_>>()
.await;
assert_eq!(deps.len(), 1);
let dep = deps[0].as_mut().fuse().await;
assert_eq!(rust.get_name(), "rust");
assert_eq!(rust.get_dependency_filename(), "Cargo.lock");
assert_eq!(dep.name, "mime");
assert_eq!(dep.version, "3.0.0");
}
}