extern crate serde;
extern crate serde_json;
use regex::Regex;
use serde::Deserialize;
use std::collections::HashMap;
fn default_bool() -> bool {
false
}
fn default_requires() -> HashMap<String, String> {
HashMap::new()
}
fn default_dependencies() -> Dependencies {
HashMap::new()
}
pub type Dependencies = HashMap<String, Dependency>;
#[derive(Deserialize, Debug)]
pub struct Dependency {
version: String,
integrity: String,
#[serde(default = "default_bool")]
bundled: bool,
#[serde(default = "default_bool")]
dev: bool,
#[serde(default = "default_bool")]
optional: bool,
#[serde(default = "default_requires")]
requires: HashMap<String, String>,
#[serde(default = "default_dependencies")]
pub dependencies: Dependencies,
}
#[derive(Deserialize, Debug)]
pub struct PackageLock {
pub name: String,
#[serde(rename = "lockfileVersion")]
pub lockfile_version: i32,
pub requires: bool,
pub dependencies: Dependencies,
}
pub fn validate(lock: &PackageLock) -> bool {
lock.lockfile_version == 1 && lock.requires == true
}
pub type Patterns = Vec<Option<Regex>>;
#[derive(Clone, Copy)]
pub enum SortType {
Dont,
Count,
Name,
}
pub struct FlatOptions {
pub patterns: Patterns,
pub production: bool,
}
pub struct Options {
pub production: bool,
pub patterns: Patterns,
pub min: Option<usize>,
pub sort: Option<SortType>,
}
fn gather_dependencies(dependencies: &Dependencies, options: &Options) -> Vec<String> {
let mut names = vec![];
let patterns = &options.patterns;
let patterns_length = patterns.len();
for (name, info) in dependencies {
if options.production && info.dev {
continue;
}
if patterns_length > 0 {
for pattern in patterns.iter() {
if let Some(p) = pattern {
if p.is_match(name) {
names.push(name.to_string())
}
}
}
} else {
names.push(name.to_string());
}
names.append(&mut gather_dependencies(
&info.dependencies,
&Options {
production: options.production,
patterns: patterns.clone(),
min: options.min,
sort: options.sort,
},
));
}
return names;
}
pub fn check_for_flatness(lock: PackageLock, options: Options) -> bool {
let names = gather_dependencies(&lock.dependencies, &options);
let mut seen = vec![];
for name in names {
if seen.contains(&name) {
return false;
}
seen.push(name);
}
true
}
pub fn get_depcount(lock: PackageLock, options: Options) -> String {
let names = gather_dependencies(&lock.dependencies, &options);
let mut seen = HashMap::new();
for name in names {
let occurrences = seen.entry(name).or_insert(0);
*occurrences += 1;
}
let mut seen_enough = vec![];
let mut longest = 0;
if let Some(min) = options.min {
for (name, occurences) in seen.iter() {
if occurences >= &min {
let length = name.len();
if longest < length {
longest = length
}
seen_enough.push((name, occurences))
}
}
};
match options.sort {
Some(SortType::Name) => seen_enough.sort_by(|a, b| a.0.cmp(&b.0)),
Some(SortType::Count) => seen_enough.sort_by(|a, b| a.1.cmp(&b.1)),
Some(SortType::Dont) => {}
None => {}
};
let mut lines = vec![];
for (name, count) in seen_enough.iter() {
lines.push(format!(
"{}{}{}",
name,
" ".repeat(longest - name.len() + 8),
count
))
}
lines.join("\n")
}