use std::fmt;
use std::ops::{Deref, DerefMut};
use crate::package::Package;
use crate::range::Range;
use crate::term::Term;
use crate::type_aliases::Map;
use crate::version::Version;
pub trait Reporter<P: Package, V: Version> {
type Output;
fn report(derivation_tree: &DerivationTree<P, V>) -> Self::Output;
}
#[derive(Debug, Clone)]
pub enum DerivationTree<P: Package, V: Version> {
External(External<P, V>),
Derived(Derived<P, V>),
}
#[derive(Debug, Clone)]
pub enum External<P: Package, V: Version> {
NotRoot(P, V),
NoVersions(P, Range<V>),
UnavailableDependencies(P, Range<V>),
FromDependencyOf(P, Range<V>, P, Range<V>),
}
#[derive(Debug, Clone)]
pub struct Derived<P: Package, V: Version> {
pub terms: Map<P, Term<V>>,
pub shared_id: Option<usize>,
pub cause1: Box<DerivationTree<P, V>>,
pub cause2: Box<DerivationTree<P, V>>,
}
impl<P: Package, V: Version> DerivationTree<P, V> {
pub fn collapse_no_versions(&mut self) {
match self {
DerivationTree::External(_) => {}
DerivationTree::Derived(derived) => {
match (derived.cause1.deref_mut(), derived.cause2.deref_mut()) {
(DerivationTree::External(External::NoVersions(p, r)), ref mut cause2) => {
cause2.collapse_no_versions();
*self = cause2
.clone()
.merge_no_versions(p.to_owned(), r.to_owned())
.unwrap_or_else(|| self.to_owned());
}
(ref mut cause1, DerivationTree::External(External::NoVersions(p, r))) => {
cause1.collapse_no_versions();
*self = cause1
.clone()
.merge_no_versions(p.to_owned(), r.to_owned())
.unwrap_or_else(|| self.to_owned());
}
_ => {
derived.cause1.collapse_no_versions();
derived.cause2.collapse_no_versions();
}
}
}
}
}
fn merge_no_versions(self, package: P, range: Range<V>) -> Option<Self> {
match self {
DerivationTree::Derived(_) => Some(self),
DerivationTree::External(External::NotRoot(_, _)) => {
panic!("How did we end up with a NoVersions merged with a NotRoot?")
}
DerivationTree::External(External::NoVersions(_, r)) => Some(DerivationTree::External(
External::NoVersions(package, range.union(&r)),
)),
DerivationTree::External(External::UnavailableDependencies(_, r)) => {
Some(DerivationTree::External(External::UnavailableDependencies(
package,
range.union(&r),
)))
}
DerivationTree::External(External::FromDependencyOf(p1, r1, p2, r2)) => {
if p1 == package {
Some(DerivationTree::External(External::FromDependencyOf(
p1,
r1.union(&range),
p2,
r2,
)))
} else {
Some(DerivationTree::External(External::FromDependencyOf(
p1,
r1,
p2,
r2.union(&range),
)))
}
}
}
}
}
impl<P: Package, V: Version> fmt::Display for External<P, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotRoot(package, version) => {
write!(f, "we are solving dependencies of {} {}", package, version)
}
Self::NoVersions(package, range) => {
if range == &Range::any() {
write!(f, "there is no available version for {}", package)
} else {
write!(f, "there is no version of {} in {}", package, range)
}
}
Self::UnavailableDependencies(package, range) => {
if range == &Range::any() {
write!(f, "dependencies of {} are unavailable", package)
} else {
write!(
f,
"dependencies of {} at version {} are unavailable",
package, range
)
}
}
Self::FromDependencyOf(p, range_p, dep, range_dep) => {
if range_p == &Range::any() && range_dep == &Range::any() {
write!(f, "{} depends on {}", p, dep)
} else if range_p == &Range::any() {
write!(f, "{} depends on {} {}", p, dep, range_dep)
} else if range_dep == &Range::any() {
write!(f, "{} {} depends on {}", p, range_p, dep)
} else {
write!(f, "{} {} depends on {} {}", p, range_p, dep, range_dep)
}
}
}
}
}
pub struct DefaultStringReporter {
ref_count: usize,
shared_with_ref: Map<usize, usize>,
lines: Vec<String>,
}
impl DefaultStringReporter {
fn new() -> Self {
Self {
ref_count: 0,
shared_with_ref: Map::default(),
lines: Vec::new(),
}
}
fn build_recursive<P: Package, V: Version>(&mut self, derived: &Derived<P, V>) {
self.build_recursive_helper(derived);
if let Some(id) = derived.shared_id {
if self.shared_with_ref.get(&id) == None {
self.add_line_ref();
self.shared_with_ref.insert(id, self.ref_count);
}
};
}
fn build_recursive_helper<P: Package, V: Version>(&mut self, current: &Derived<P, V>) {
match (current.cause1.deref(), current.cause2.deref()) {
(DerivationTree::External(external1), DerivationTree::External(external2)) => {
self.lines.push(Self::explain_both_external(
external1,
external2,
¤t.terms,
));
}
(DerivationTree::Derived(derived), DerivationTree::External(external)) => {
self.report_one_each(derived, external, ¤t.terms);
}
(DerivationTree::External(external), DerivationTree::Derived(derived)) => {
self.report_one_each(derived, external, ¤t.terms);
}
(DerivationTree::Derived(derived1), DerivationTree::Derived(derived2)) => {
match (
self.line_ref_of(derived1.shared_id),
self.line_ref_of(derived2.shared_id),
) {
(Some(ref1), Some(ref2)) => self.lines.push(Self::explain_both_ref(
ref1,
derived1,
ref2,
derived2,
¤t.terms,
)),
(Some(ref1), None) => {
self.build_recursive(derived2);
self.lines
.push(Self::and_explain_ref(ref1, derived1, ¤t.terms));
}
(None, Some(ref2)) => {
self.build_recursive(derived1);
self.lines
.push(Self::and_explain_ref(ref2, derived2, ¤t.terms));
}
(None, None) => {
self.build_recursive(derived1);
if derived1.shared_id != None {
self.lines.push("".into());
self.build_recursive(current);
} else {
self.add_line_ref();
let ref1 = self.ref_count;
self.lines.push("".into());
self.build_recursive(derived2);
self.lines
.push(Self::and_explain_ref(ref1, derived1, ¤t.terms));
}
}
}
}
}
}
fn report_one_each<P: Package, V: Version>(
&mut self,
derived: &Derived<P, V>,
external: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) {
match self.line_ref_of(derived.shared_id) {
Some(ref_id) => self.lines.push(Self::explain_ref_and_external(
ref_id,
derived,
external,
current_terms,
)),
None => self.report_recurse_one_each(derived, external, current_terms),
}
}
fn report_recurse_one_each<P: Package, V: Version>(
&mut self,
derived: &Derived<P, V>,
external: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) {
match (derived.cause1.deref(), derived.cause2.deref()) {
(DerivationTree::Derived(prior_derived), DerivationTree::External(prior_external)) => {
self.build_recursive(prior_derived);
self.lines.push(Self::and_explain_prior_and_external(
prior_external,
external,
current_terms,
));
}
(DerivationTree::External(prior_external), DerivationTree::Derived(prior_derived)) => {
self.build_recursive(prior_derived);
self.lines.push(Self::and_explain_prior_and_external(
prior_external,
external,
current_terms,
));
}
_ => {
self.build_recursive(derived);
self.lines
.push(Self::and_explain_external(external, current_terms));
}
}
}
fn explain_both_external<P: Package, V: Version>(
external1: &External<P, V>,
external2: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"Because {} and {}, {}.",
external1,
external2,
Self::string_terms(current_terms)
)
}
fn explain_both_ref<P: Package, V: Version>(
ref_id1: usize,
derived1: &Derived<P, V>,
ref_id2: usize,
derived2: &Derived<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"Because {} ({}) and {} ({}), {}.",
Self::string_terms(&derived1.terms),
ref_id1,
Self::string_terms(&derived2.terms),
ref_id2,
Self::string_terms(current_terms)
)
}
fn explain_ref_and_external<P: Package, V: Version>(
ref_id: usize,
derived: &Derived<P, V>,
external: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"Because {} ({}) and {}, {}.",
Self::string_terms(&derived.terms),
ref_id,
external,
Self::string_terms(current_terms)
)
}
fn and_explain_external<P: Package, V: Version>(
external: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"And because {}, {}.",
external,
Self::string_terms(current_terms)
)
}
fn and_explain_ref<P: Package, V: Version>(
ref_id: usize,
derived: &Derived<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"And because {} ({}), {}.",
Self::string_terms(&derived.terms),
ref_id,
Self::string_terms(current_terms)
)
}
fn and_explain_prior_and_external<P: Package, V: Version>(
prior_external: &External<P, V>,
external: &External<P, V>,
current_terms: &Map<P, Term<V>>,
) -> String {
format!(
"And because {} and {}, {}.",
prior_external,
external,
Self::string_terms(current_terms)
)
}
pub fn string_terms<P: Package, V: Version>(terms: &Map<P, Term<V>>) -> String {
let terms_vec: Vec<_> = terms.iter().collect();
match terms_vec.as_slice() {
[] => "version solving failed".into(),
[(package, Term::Positive(range))] => format!("{} {} is forbidden", package, range),
[(package, Term::Negative(range))] => format!("{} {} is mandatory", package, range),
[(p1, Term::Positive(r1)), (p2, Term::Negative(r2))] => {
External::FromDependencyOf(p1, r1.clone(), p2, r2.clone()).to_string()
}
[(p1, Term::Negative(r1)), (p2, Term::Positive(r2))] => {
External::FromDependencyOf(p2, r2.clone(), p1, r1.clone()).to_string()
}
slice => {
let str_terms: Vec<_> = slice.iter().map(|(p, t)| format!("{} {}", p, t)).collect();
str_terms.join(", ") + " are incompatible"
}
}
}
fn add_line_ref(&mut self) {
let new_count = self.ref_count + 1;
self.ref_count = new_count;
if let Some(line) = self.lines.last_mut() {
*line = format!("{} ({})", line, new_count);
}
}
fn line_ref_of(&self, shared_id: Option<usize>) -> Option<usize> {
shared_id.and_then(|id| self.shared_with_ref.get(&id).cloned())
}
}
impl<P: Package, V: Version> Reporter<P, V> for DefaultStringReporter {
type Output = String;
fn report(derivation_tree: &DerivationTree<P, V>) -> Self::Output {
match derivation_tree {
DerivationTree::External(external) => external.to_string(),
DerivationTree::Derived(derived) => {
let mut reporter = Self::new();
reporter.build_recursive(derived);
reporter.lines.join("\n")
}
}
}
}