Skip to content

Commit f17c3ff

Browse files
pretty print failing derivation trees
1 parent cd45c6a commit f17c3ff

File tree

1 file changed

+48
-153
lines changed

1 file changed

+48
-153
lines changed

compiler-core/src/error.rs

+48-153
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ use heck::{ToSnakeCase, ToTitleCase, ToUpperCamelCase};
1313
use hexpm::version::ResolutionError;
1414
use itertools::Itertools;
1515
use pubgrub::package::Package;
16-
use pubgrub::range::Range;
1716
use pubgrub::report::{DerivationTree, Derived, External};
1817
use pubgrub::version::Version;
19-
use std::collections::HashSet;
2018
use std::env;
2119
use std::fmt::{Debug, Display};
2220
use std::io::Write;
@@ -360,8 +358,12 @@ impl Error {
360358

361359
pub fn dependency_resolution_failed(error: ResolutionError) -> Error {
362360
Self::DependencyResolutionFailed(match error {
363-
ResolutionError::NoSolution(derivation_tree) =>
364-
derivation_tree_to_pretty_error_message(derivation_tree),
361+
ResolutionError::NoSolution(mut derivation_tree) => {
362+
derivation_tree.collapse_no_versions();
363+
let derivation_tree = simplify_derivation_tree(derivation_tree);
364+
// TODO)) find a way to use the root package name, here it is hardcoded!
365+
DerivationTreePrinter::new("one".into()).print(&derivation_tree)
366+
}
365367

366368
ResolutionError::ErrorRetrievingDependencies {
367369
package,
@@ -407,164 +409,57 @@ impl Error {
407409
}
408410
}
409411

410-
fn derivation_tree_to_pretty_error_message(
411-
mut derivation_tree: DerivationTree<String, hexpm::version::Version>,
412-
) -> String {
413-
derivation_tree.collapse_no_versions();
414-
let derivation_tree = simplify_derivation_tree(derivation_tree);
415-
416-
pprint_error(0, &derivation_tree);
417-
// TODO))
418-
// This order is not deterministic, so sometimes it will default to the
419-
// original version even though it's almost the same!!
420-
match &derivation_tree {
421-
DerivationTree::Derived(Derived { cause1, cause2, .. }) => {
422-
match (cause1.as_ref(), cause2.as_ref()) {
423-
(
424-
DerivationTree::Derived(Derived { cause1, cause2, .. }),
425-
DerivationTree::External(External::FromDependencyOf(
426-
root,
427-
root_range,
428-
dep,
429-
dep_range_from_root,
430-
)),
431-
) => match (cause1.as_ref(), cause2.as_ref()) {
432-
(
433-
DerivationTree::External(External::FromDependencyOf(
434-
maybe_common,
435-
maybe_common_range,
436-
maybe_dep,
437-
dep_range_from_common,
438-
)),
439-
DerivationTree::External(External::FromDependencyOf(
440-
maybe_root,
441-
maybe_root_range,
442-
common,
443-
common_range,
444-
)),
445-
) if maybe_common == common
446-
&& maybe_common_range == common_range
447-
&& maybe_root == root
448-
&& maybe_root_range == root_range
449-
&& maybe_dep == dep =>
450-
{
451-
// TODO))
452-
// The default look of ranges is confusing and we want to switch
453-
// to one more similar to Gleam's constraints.
454-
// However, at the moment there's no way of doing this:
455-
// https://github.com/pubgrub-rs/pubgrub/issues/258
456-
//
457-
wrap_format!(
458-
"Unable to find compatible versions for the version \
459-
constraints in your gleam.toml:
460-
461-
- `{root}` requires `{common}` to be `{common_range}`
462-
- and all versions of `{common}` in that range require `{dep}` to be `{dep_range_from_common}`
463-
- but `{root}` also requires `{dep}` to be `{dep_range_from_root}`
464-
- and there is no version of `{dep}` that satisfies both constraints
465-
"
466-
)
467-
}
468-
_ => derivation_tree_to_default_error_message(derivation_tree),
469-
},
470-
_ => derivation_tree_to_default_error_message(derivation_tree),
471-
}
472-
}
473-
// In case we can't figure out a good way to display the constraint
474-
// failure then we just use a default error message that just lists the
475-
// conflicting packages; leaving to the user to figure out what went
476-
// wrong.
477-
_ => derivation_tree_to_default_error_message(derivation_tree),
478-
}
412+
struct DerivationTreePrinter<'a> {
413+
previous_subject: Option<&'a String>,
414+
previous_object: Option<&'a String>,
415+
root_package_name: String,
479416
}
480417

481-
fn derivation_tree_to_default_error_message(
482-
derivation_tree: DerivationTree<String, hexpm::version::Version>,
483-
) -> String {
484-
let mut conflicting_packages = HashSet::new();
485-
collect_conflicting_packages(&derivation_tree, &mut conflicting_packages);
418+
impl<'a> DerivationTreePrinter<'a> {
419+
pub fn print(
420+
&mut self,
421+
derivation_tree: &'a DerivationTree<String, hexpm::version::Version>,
422+
) -> String {
423+
match &derivation_tree {
424+
DerivationTree::External(External::FromDependencyOf(
425+
package,
426+
_package_version,
427+
required_package,
428+
required_package_version,
429+
)) => {
430+
let start = if self.previous_subject == Some(package) {
431+
"it also".to_string()
432+
} else if package == &self.root_package_name {
433+
"your package".to_string()
434+
} else if self.previous_object == Some(package) {
435+
format!("and `{package}`")
436+
} else {
437+
format!("`{package}`")
438+
};
486439

487-
wrap_format!(
488-
"Unable to find compatible versions for the version constraints in \
489-
your gleam.toml. The conflicting packages are:
440+
self.previous_subject = Some(package);
441+
self.previous_object = Some(required_package);
490442

491-
{}
492-
",
493-
conflicting_packages
494-
.into_iter()
495-
.map(|s| format!("- {}", s))
496-
.join("\n")
497-
)
498-
}
499-
500-
fn pprint_error<'dt, P: Package, V: Version>(
501-
nest: usize,
502-
derivation_tree: &'dt DerivationTree<P, V>,
503-
) {
504-
match derivation_tree {
505-
DerivationTree::External(external) => match external {
506-
External::NotRoot(_, _) => panic!(),
507-
External::NoVersions(_, _) => panic!(),
508-
External::UnavailableDependencies(_, _) => panic!(),
509-
External::FromDependencyOf(p1, range1, p2, range2) => {
510-
let nesting = " ".repeat(nest);
511-
print!(
512-
"{nesting}FromDepOf {{
513-
{nesting} {p1}: {range1},
514-
{nesting} {p2}: {range2},
515-
{nesting}}}"
443+
format!(
444+
"- {start} requires `{required_package}` to be `{required_package_version}`"
445+
)
446+
}
447+
DerivationTree::External(_) => todo!(),
448+
DerivationTree::Derived(Derived { cause1, cause2, .. }) => {
449+
format!(
450+
"{}\n{}",
451+
self.print(cause2.as_ref()),
452+
self.print(cause1.as_ref()),
516453
)
517454
}
518-
},
519-
520-
DerivationTree::Derived(Derived {
521-
terms: _,
522-
shared_id: _,
523-
cause1,
524-
cause2,
525-
}) => {
526-
let nesting = " ".repeat(nest);
527-
print!(
528-
"{nesting}Derived {{
529-
{nesting} cause1: "
530-
);
531-
pprint_error(nest + 2, cause1.as_ref());
532-
print!(
533-
",
534-
{nesting} cause2: "
535-
);
536-
pprint_error(nest + 2, cause2.as_ref());
537-
print!(
538-
",
539-
{nesting}}}",
540-
)
541455
}
542456
}
543-
}
544457

545-
fn collect_conflicting_packages<'dt, P: Package, V: Version>(
546-
derivation_tree: &'dt DerivationTree<P, V>,
547-
conflicting_packages: &mut HashSet<&'dt P>,
548-
) {
549-
match derivation_tree {
550-
DerivationTree::External(external) => match external {
551-
External::NotRoot(package, _) => {
552-
let _ = conflicting_packages.insert(package);
553-
}
554-
External::NoVersions(package, _) => {
555-
let _ = conflicting_packages.insert(package);
556-
}
557-
External::UnavailableDependencies(package, _) => {
558-
let _ = conflicting_packages.insert(package);
559-
}
560-
External::FromDependencyOf(package, _, dep_package, _) => {
561-
let _ = conflicting_packages.insert(package);
562-
let _ = conflicting_packages.insert(dep_package);
563-
}
564-
},
565-
DerivationTree::Derived(derived) => {
566-
collect_conflicting_packages(&derived.cause1, conflicting_packages);
567-
collect_conflicting_packages(&derived.cause2, conflicting_packages);
458+
fn new(root_package_name: String) -> Self {
459+
Self {
460+
previous_subject: None,
461+
previous_object: None,
462+
root_package_name,
568463
}
569464
}
570465
}

0 commit comments

Comments
 (0)