#![doc = include_str!("../README.md")]
#![deny(unsafe_code)]
#![warn(
clippy::all,
clippy::await_holding_lock,
clippy::char_lit_as_u8,
clippy::checked_conversions,
clippy::dbg_macro,
clippy::debug_assert_with_mut_call,
clippy::doc_markdown,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::exit,
clippy::expl_impl_clone_on_copy,
clippy::explicit_deref_methods,
clippy::explicit_into_iter_loop,
clippy::fallible_impl_from,
clippy::filter_map_next,
clippy::flat_map_option,
clippy::float_cmp_const,
clippy::fn_params_excessive_bools,
clippy::from_iter_instead_of_collect,
clippy::if_let_mutex,
clippy::implicit_clone,
clippy::imprecise_flops,
clippy::inefficient_to_string,
clippy::invalid_upcast_comparisons,
clippy::large_digit_groups,
clippy::large_stack_arrays,
clippy::large_types_passed_by_value,
clippy::let_unit_value,
clippy::linkedlist,
clippy::lossy_float_literal,
clippy::macro_use_imports,
clippy::manual_ok_or,
clippy::map_err_ignore,
clippy::map_flatten,
clippy::map_unwrap_or,
clippy::match_on_vec_items,
clippy::match_same_arms,
clippy::match_wild_err_arm,
clippy::match_wildcard_for_single_variants,
clippy::mem_forget,
clippy::mismatched_target_os,
clippy::missing_enforced_import_renames,
clippy::mut_mut,
clippy::mutex_integer,
clippy::needless_borrow,
clippy::needless_continue,
clippy::needless_for_each,
clippy::option_option,
clippy::path_buf_push_overwrite,
clippy::ptr_as_ptr,
clippy::rc_mutex,
clippy::ref_option_ref,
clippy::rest_pat_in_fully_bound_structs,
clippy::same_functions_in_if_condition,
clippy::semicolon_if_nothing_returned,
clippy::single_match_else,
clippy::string_add_assign,
clippy::string_add,
clippy::string_lit_as_bytes,
clippy::string_to_string,
clippy::todo,
clippy::trait_duplication_in_bounds,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::unused_self,
clippy::useless_transmute,
clippy::verbose_file_reads,
clippy::zero_sized_map_values,
future_incompatible,
nonstandard_style,
rust_2018_idioms
)]
#![allow(clippy::single_match_else)]
pub use semver::Version;
use std::{cmp, collections::HashMap, fmt};
pub mod advisories;
pub mod bans;
mod cfg;
pub mod diag;
pub mod licenses;
pub mod sources;
pub use cfg::{Spanned, UnvalidatedConfig};
use krates::cm;
pub use krates::{DepKind, Kid, Utf8PathBuf};
pub use rustsec::package::source::SourceId;
#[derive(serde::Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
#[serde(rename_all = "snake_case")]
pub enum LintLevel {
Allow,
Warn,
Deny,
}
impl Default for LintLevel {
fn default() -> Self {
LintLevel::Warn
}
}
const fn lint_allow() -> LintLevel {
LintLevel::Allow
}
const fn lint_warn() -> LintLevel {
LintLevel::Warn
}
const fn lint_deny() -> LintLevel {
LintLevel::Deny
}
#[derive(Debug)]
pub struct Krate {
pub name: String,
pub id: Kid,
pub version: Version,
pub source: Option<SourceId>,
pub authors: Vec<String>,
pub repository: Option<String>,
pub description: Option<String>,
pub manifest_path: Utf8PathBuf,
pub license: Option<String>,
pub license_file: Option<Utf8PathBuf>,
pub deps: Vec<cm::Dependency>,
pub features: HashMap<String, Vec<String>>,
pub targets: Vec<cm::Target>,
pub publish: Option<Vec<String>>,
}
#[cfg(test)]
impl Default for Krate {
fn default() -> Self {
Self {
name: "".to_owned(),
version: Version::new(0, 1, 0),
authors: Vec::new(),
id: Kid {
repr: "".to_owned(),
},
source: None,
description: None,
deps: Vec::new(),
license: None,
license_file: None,
targets: Vec::new(),
features: HashMap::new(),
manifest_path: Utf8PathBuf::new(),
repository: None,
publish: None,
}
}
}
impl PartialOrd for Krate {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Krate {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl PartialEq for Krate {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Krate {}
impl krates::KrateDetails for Krate {
fn name(&self) -> &str {
&self.name
}
fn version(&self) -> &semver::Version {
&self.version
}
}
impl From<cm::Package> for Krate {
fn from(pkg: cm::Package) -> Self {
Self {
name: pkg.name,
id: pkg.id,
version: pkg.version,
authors: pkg.authors,
repository: pkg.repository,
source: {
pkg.source.and_then(|src| {
let url = format!("{}", src);
SourceId::from_url(&url).map_or_else(
|e| {
log::warn!("unable to parse source url '{}': {}", url, e);
None
},
Some,
)
})
},
targets: pkg.targets,
license: pkg.license.map(|lf| {
if lf.contains('/') {
lf.replace('/', " OR ")
} else {
lf
}
}),
license_file: pkg.license_file,
description: pkg.description,
manifest_path: pkg.manifest_path,
deps: {
let mut deps = pkg.dependencies;
deps.sort_by(|a, b| a.name.cmp(&b.name));
deps
},
features: pkg.features,
publish: pkg.publish,
}
}
}
impl Krate {
pub(crate) fn is_private(&self, private_registries: &[&str]) -> bool {
self.publish.as_ref().map_or(false, |v| {
if v.is_empty() {
true
} else {
v.iter()
.all(|reg| private_registries.contains(®.as_str()))
}
})
}
pub(crate) fn normalized_source_url(&self) -> Option<url::Url> {
self.source.as_ref().map(|source| {
let mut url = source.url().clone();
url.set_query(None);
url.set_fragment(None);
crate::sources::normalize_url(&mut url);
url
})
}
}
impl fmt::Display for Krate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.name, self.version)
}
}
pub type Krates = krates::Krates<Krate>;
#[inline]
pub fn binary_search<T, Q>(s: &[T], query: &Q) -> Result<usize, usize>
where
T: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
s.binary_search_by(|i| i.borrow().cmp(query))
}
#[inline]
pub fn contains<T, Q>(s: &[T], query: &Q) -> bool
where
T: std::borrow::Borrow<Q>,
Q: Eq + ?Sized,
{
s.iter().any(|i| i.borrow() == query)
}
#[inline]
pub fn hash(data: &[u8]) -> u32 {
use std::hash::Hasher;
let mut xx = twox_hash::XxHash32::default();
xx.write(data);
xx.finish() as u32
}
pub struct CheckCtx<'ctx, T> {
pub cfg: T,
pub krates: &'ctx Krates,
pub krate_spans: &'ctx diag::KrateSpans,
pub serialize_extra: bool,
}
#[inline]
pub fn match_req(version: &Version, req: Option<&semver::VersionReq>) -> bool {
req.map_or(true, |req| req.matches(version))
}