use crate::dependencies::formatter::{
format_version, get_package_metadata_msrv, parse_manifest_workaround,
};
use crate::dependencies::DependencyGraph;
use crate::reporter::Output;
use cargo_metadata::Package;
use petgraph::visit::Bfs;
use std::collections::BTreeMap;
use std::fmt::Formatter;
use std::marker::PhantomData;
pub(crate) struct ByMSRVFormatter<T: Output> {
graph: DependencyGraph,
output: PhantomData<T>,
}
impl<T: Output> ByMSRVFormatter<T> {
pub fn new(graph: DependencyGraph) -> Self {
Self {
graph,
output: PhantomData,
}
}
}
impl<T: Output> ByMSRVFormatter<T> {
fn dependencies_by_msrv<Fi, Fg, B>(&self, init: Fi, f: Fg) -> B
where
Fi: FnOnce() -> B,
Fg: Fn(&mut B, Values),
{
use crate::semver;
let mut out = init();
let dependency_graph = &self.graph;
let root = &dependency_graph.root_crate;
let root = dependency_graph.index[root];
let graph = &dependency_graph.packages;
let mut bfs = Bfs::new(&graph, root.into());
let mut version_map: BTreeMap<Option<semver::Version>, Vec<&Package>> = BTreeMap::new();
while let Some(nx) = bfs.next(&graph) {
let package = &graph[nx];
let msrv = package
.rust_version
.clone()
.map(|req| {
let comparator = &req.comparators[0];
crate::semver::Version::new(
comparator.major,
comparator.minor.unwrap_or_default(),
comparator.patch.unwrap_or_default(),
)
})
.or_else(|| get_package_metadata_msrv(package))
.or_else(|| parse_manifest_workaround(package.manifest_path.as_path()));
version_map.entry(msrv).or_default().push(package);
}
for (version, packages) in version_map {
let values = Values {
msrv: format_version(version.as_ref()),
dependencies: packages.iter().map(|p| p.name.clone()).collect(),
};
f(&mut out, values);
}
out
}
}
impl std::fmt::Display for ByMSRVFormatter<crate::reporter::ui::HumanPrinter<'_>> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Cell, ContentArrangement, Table};
let out = self.dependencies_by_msrv(
|| {
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(vec!["MSRV", "Dependency"]);
table
},
|acc, next| {
acc.add_row(vec![
Cell::new(&next.msrv),
Cell::new(&next.dependencies.join(", ")),
]);
},
);
out.fmt(f)
}
}
impl std::fmt::Display for ByMSRVFormatter<crate::reporter::json::JsonPrinter<'_>> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use json::object;
let objects = self.dependencies_by_msrv(Vec::new, |acc, next| {
acc.push(object! {
"msrv": next.msrv,
"dependencies": next.dependencies
});
});
let json = object! {
reason: "list",
variant: crate::config::list::ORDERED_BY_MSRV,
success: true,
list: objects,
};
write!(f, "{}", json)
}
}
struct Values {
msrv: String,
dependencies: Vec<String>,
}