mirror of https://github.com/astral-sh/uv
Improve formatting of package ranges in error messages (#864)
Closes #810 Closes https://github.com/astral-sh/puffin/issues/812 Requires https://github.com/zanieb/pubgrub/pull/19 and https://github.com/zanieb/pubgrub/pull/18 - Always pair package ranges with names e.g. `... of a matching a<1.0` instead of `... of a matching <1.0` - Split range segments onto multiple lines when not a singleton as suggested in [#850](https://github.com/astral-sh/puffin/pull/850#discussion_r1446419610) - Improve formatting when ranges are split across multiple lines e.g. by avoiding extra spaces and improving wording Note review will require expanding the hidden files as there are significant changes to the report formatter and snapshots. Bear with me here as these are definitely not perfect still. The following changes build on top of this independently for further improvements: - #868 - #867 - #866 - #871
This commit is contained in:
parent
4d8bfd7f61
commit
93d3093a2a
|
|
@ -2267,7 +2267,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pubgrub"
|
name = "pubgrub"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "git+https://github.com/zanieb/pubgrub?rev=866c0f2a87fee1e8abe804d40a2ee934de0973d7#866c0f2a87fee1e8abe804d40a2ee934de0973d7"
|
source = "git+https://github.com/zanieb/pubgrub?rev=0e02ea9fc8d021fb6a6b9e77b09ade4332068f42#0e02ea9fc8d021fb6a6b9e77b09ade4332068f42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.1.0",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ owo-colors = { version = "3.5.0" }
|
||||||
petgraph = { version = "0.6.4" }
|
petgraph = { version = "0.6.4" }
|
||||||
platform-info = { version = "2.0.2" }
|
platform-info = { version = "2.0.2" }
|
||||||
plist = { version = "1.6.0" }
|
plist = { version = "1.6.0" }
|
||||||
pubgrub = { git = "https://github.com/zanieb/pubgrub", rev = "866c0f2a87fee1e8abe804d40a2ee934de0973d7" }
|
pubgrub = { git = "https://github.com/zanieb/pubgrub", rev = "0e02ea9fc8d021fb6a6b9e77b09ade4332068f42" }
|
||||||
pyo3 = { version = "0.20.2" }
|
pyo3 = { version = "0.20.2" }
|
||||||
pyo3-log = { version = "0.9.0"}
|
pyo3-log = { version = "0.9.0"}
|
||||||
pyproject-toml = { version = "0.8.1" }
|
pyproject-toml = { version = "0.8.1" }
|
||||||
|
|
|
||||||
|
|
@ -670,9 +670,11 @@ fn compile_python_37() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of Python>=3.8 and black==23.10.1 depends
|
╰─▶ Because there are no versions of Python that satisfy Python>=3.8
|
||||||
on Python>=3.8, black==23.10.1 is forbidden.
|
and black==23.10.1 depends on Python>=3.8, we can conclude that
|
||||||
And because root depends on black==23.10.1, version solving failed.
|
black==23.10.1 is forbidden.
|
||||||
|
And because root depends on black==23.10.1 we can conclude that the
|
||||||
|
requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1407,8 +1409,9 @@ fn conflicting_direct_url_dependency() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there is no version of werkzeug==3.0.0 and root depends on
|
╰─▶ Because there is no version of werkzeug==3.0.0 and root depends
|
||||||
werkzeug==3.0.0, version solving failed.
|
on werkzeug==3.0.0, we can conclude that the requirements are
|
||||||
|
unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1558,8 +1561,10 @@ fn conflicting_transitive_url_dependency() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because flask==3.0.0 depends on werkzeug>=3.0.0 and there are no
|
╰─▶ Because flask==3.0.0 depends on werkzeug>=3.0.0 and there are no
|
||||||
versions of werkzeug>=3.0.0, flask==3.0.0 is forbidden.
|
versions of werkzeug that satisfy werkzeug>=3.0.0, we can conclude that
|
||||||
And because root depends on flask==3.0.0, version solving failed.
|
flask==3.0.0 is forbidden.
|
||||||
|
And because root depends on flask==3.0.0 we can conclude that the
|
||||||
|
requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1901,8 +1906,9 @@ dependencies = ["django==300.1.4"]
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there is no version of django==300.1.4 and my-project depends on
|
╰─▶ Because there is no version of django==300.1.4 and my-project
|
||||||
django==300.1.4, version solving failed.
|
depends on django==300.1.4, we can conclude that the requirements are
|
||||||
|
unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2227,8 +2233,9 @@ fn compile_yanked_version_indirect() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of attrs>20.3.0, <21.2.0 and root depends
|
╰─▶ Because there are no versions of attrs that satisfy attrs>20.3.0,<21.2.0
|
||||||
on attrs>20.3.0, <21.2.0, version solving failed.
|
and root depends on attrs>20.3.0,<21.2.0, we can conclude that the
|
||||||
|
requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,10 +77,11 @@ fn no_solution() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of flask>3.0.0 and flask==3.0.0 depends on
|
╰─▶ Because there are no versions of flask that satisfy flask>3.0.0
|
||||||
werkzeug>=3.0.0, flask>=3.0.0 depends on werkzeug>=3.0.0.
|
and flask==3.0.0 depends on werkzeug>=3.0.0, we can conclude that
|
||||||
|
flask>=3.0.0 depends on werkzeug>=3.0.0.
|
||||||
And because root depends on flask>=3.0.0 and root depends on
|
And because root depends on flask>=3.0.0 and root depends on
|
||||||
werkzeug<1.0.0, version solving failed.
|
werkzeug<1.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,13 @@ fn excluded_only_version() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a<1.0.0 | >1.0.0 and root depends on a<1.0.0 | >1.0.0, version solving failed.
|
╰─▶ Because there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
and root depends on one of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -150,9 +156,23 @@ fn excluded_only_compatible_version() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a<1.0.0 | >1.0.0, <2.0.0 | >2.0.0, <3.0.0 | >3.0.0 and a==1.0.0 depends on b==1.0.0, a<2.0.0 depends on b==1.0.0.
|
╰─▶ Because there are no versions of a that satisfy any of:
|
||||||
And because a==3.0.0 depends on b==3.0.0, a<2.0.0 | >2.0.0 depends on b<=1.0.0 | >=3.0.0.
|
a<1.0.0
|
||||||
And because root depends on b>=2.0.0, <3.0.0 and root depends on a<2.0.0 | >2.0.0, version solving failed.
|
a>1.0.0,<2.0.0
|
||||||
|
a>2.0.0,<3.0.0
|
||||||
|
a>3.0.0
|
||||||
|
and a==1.0.0 depends on b==1.0.0, we can conclude that a<2.0.0 depends on b==1.0.0.
|
||||||
|
And because a==3.0.0 depends on b==3.0.0 we can conclude that any of:
|
||||||
|
a<2.0.0
|
||||||
|
a>2.0.0
|
||||||
|
depends on one of:
|
||||||
|
b<=1.0.0
|
||||||
|
b>=3.0.0
|
||||||
|
|
||||||
|
And because root depends on b>=2.0.0,<3.0.0 and root depends on one of:
|
||||||
|
a<2.0.0
|
||||||
|
a>2.0.0
|
||||||
|
we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -258,13 +278,27 @@ fn dependency_excludes_range_of_compatible_versions() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a<1.0.0 | >1.0.0, <2.0.0 | >3.0.0 and a==1.0.0 depends on b==1.0.0, a<2.0.0 depends on b==1.0.0. (1)
|
╰─▶ Because there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0,<2.0.0
|
||||||
|
a>3.0.0
|
||||||
|
and a==1.0.0 depends on b==1.0.0, we can conclude that a<2.0.0 depends on b==1.0.0. (1)
|
||||||
|
|
||||||
Because there are no versions of c<1.0.0 | >1.0.0, <2.0.0 | >2.0.0 and c==1.0.0 depends on a<2.0.0, c<2.0.0 depends on a<2.0.0.
|
Because there are no versions of c that satisfy any of:
|
||||||
And because c==2.0.0 depends on a>=3.0.0, c depends on a<2.0.0 | >=3.0.0.
|
c<1.0.0
|
||||||
And because a<2.0.0 depends on b==1.0.0 (1), a!=3.0.0, c*, b!=1.0.0 are incompatible.
|
c>1.0.0,<2.0.0
|
||||||
And because a==3.0.0 depends on b==3.0.0, c depends on b<=1.0.0 | >=3.0.0.
|
c>2.0.0
|
||||||
And because root depends on c and root depends on b>=2.0.0, <3.0.0, version solving failed.
|
and c==1.0.0 depends on a<2.0.0, we can conclude that c<2.0.0 depends on a<2.0.0.
|
||||||
|
And because c==2.0.0 depends on a>=3.0.0 we can conclude that c depends on one of:
|
||||||
|
a<2.0.0
|
||||||
|
a>=3.0.0
|
||||||
|
|
||||||
|
And because we know from (1) that a<2.0.0 depends on b==1.0.0, we can conclude that a!=3.0.0, c*, b!=1.0.0 are incompatible.
|
||||||
|
And because a==3.0.0 depends on b==3.0.0 we can conclude that c depends on one of:
|
||||||
|
b<=1.0.0
|
||||||
|
b>=3.0.0
|
||||||
|
|
||||||
|
And because root depends on c and root depends on b>=2.0.0,<3.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -386,13 +420,39 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() -> Result<(
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because a==1.0.0 depends on b==1.0.0 and there are no versions of a<1.0.0 | >1.0.0, <2.0.0 | >3.0.0, a<2.0.0 depends on b==1.0.0.
|
╰─▶ Because a==1.0.0 depends on b==1.0.0 and there are no versions of a that satisfy any of:
|
||||||
And because a==3.0.0 depends on b==3.0.0, a<2.0.0 | >=3.0.0 depends on b<=1.0.0 | >=3.0.0. (1)
|
a<1.0.0
|
||||||
|
a>1.0.0,<2.0.0
|
||||||
|
a>3.0.0
|
||||||
|
we can conclude that a<2.0.0 depends on b==1.0.0.
|
||||||
|
And because a==3.0.0 depends on b==3.0.0 we can conclude that any of:
|
||||||
|
a<2.0.0
|
||||||
|
a>=3.0.0
|
||||||
|
depends on one of:
|
||||||
|
b<=1.0.0
|
||||||
|
b>=3.0.0
|
||||||
|
(1)
|
||||||
|
|
||||||
Because there are no versions of c<1.0.0 | >1.0.0, <2.0.0 | >2.0.0 and c==1.0.0 depends on a<2.0.0, c<2.0.0 depends on a<2.0.0.
|
Because there are no versions of c that satisfy any of:
|
||||||
And because c==2.0.0 depends on a>=3.0.0, c depends on a<2.0.0 | >=3.0.0.
|
c<1.0.0
|
||||||
And because a<2.0.0 | >=3.0.0 depends on b<=1.0.0 | >=3.0.0 (1), c depends on b<=1.0.0 | >=3.0.0.
|
c>1.0.0,<2.0.0
|
||||||
And because root depends on b>=2.0.0, <3.0.0 and root depends on c, version solving failed.
|
c>2.0.0
|
||||||
|
and c==1.0.0 depends on a<2.0.0, we can conclude that c<2.0.0 depends on a<2.0.0.
|
||||||
|
And because c==2.0.0 depends on a>=3.0.0 we can conclude that c depends on one of:
|
||||||
|
a<2.0.0
|
||||||
|
a>=3.0.0
|
||||||
|
|
||||||
|
And because we know from (1) that any of:
|
||||||
|
a<2.0.0
|
||||||
|
a>=3.0.0
|
||||||
|
depends on one of:
|
||||||
|
b<=1.0.0
|
||||||
|
b>=3.0.0
|
||||||
|
we can conclude that c depends on one of:
|
||||||
|
b<=1.0.0
|
||||||
|
b>=3.0.0
|
||||||
|
|
||||||
|
And because root depends on b>=2.0.0,<3.0.0 and root depends on c, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -521,7 +581,7 @@ fn requires_package_only_prereleases_in_range() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a>0.1.0 and root depends on a>0.1.0, version solving failed.
|
╰─▶ Because there are no versions of a that satisfy a>0.1.0 and root depends on a>0.1.0, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: Pre-releases are available for a in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: Pre-releases are available for a in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
|
|
@ -1116,8 +1176,11 @@ fn requires_transitive_package_only_prereleases_in_range() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of b>0.1 and a==0.1.0 depends on b>0.1, a==0.1.0 is forbidden.
|
╰─▶ Because there are no versions of b that satisfy b>0.1 and a==0.1.0 depends on b>0.1, we can conclude that a==0.1.0 is forbidden.
|
||||||
And because there are no versions of a<0.1.0 | >0.1.0 and root depends on a, version solving failed.
|
And because there are no versions of a that satisfy any of:
|
||||||
|
a<0.1.0
|
||||||
|
a>0.1.0
|
||||||
|
and root depends on a, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: Pre-releases are available for b in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: Pre-releases are available for b in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
|
|
@ -1271,10 +1334,13 @@ fn requires_transitive_prerelease_and_stable_dependency() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there is no version of c==2.0.0b1 and a==1.0.0 depends on c==2.0.0b1, a==1.0.0 is forbidden.
|
╰─▶ Because there is no version of c==2.0.0b1 and a==1.0.0 depends on c==2.0.0b1, we can conclude that a==1.0.0 is forbidden.
|
||||||
And because there are no versions of a<1.0.0 | >1.0.0 and root depends on a, version solving failed.
|
And because there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
and root depends on a, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: c was requested with a pre-release marker (e.g., ==2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: c was requested with a pre-release marker (e.g., c==2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1468,12 +1534,18 @@ fn requires_transitive_prerelease_and_stable_dependency_many_versions() -> Resul
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of b<1.0.0 | >1.0.0 and b==1.0.0 depends on c, b depends on c.
|
╰─▶ Because there are no versions of b that satisfy any of:
|
||||||
And because there are no versions of c>=2.0.0b1, b depends on c<2.0.0b1.
|
b<1.0.0
|
||||||
And because a==1.0.0 depends on c>=2.0.0b1 and there are no versions of a<1.0.0 | >1.0.0, b*, a* are incompatible.
|
b>1.0.0
|
||||||
And because root depends on b and root depends on a, version solving failed.
|
and b==1.0.0 depends on c, we can conclude that b depends on c.
|
||||||
|
And because there are no versions of c that satisfy c>=2.0.0b1 we can conclude that b depends on c<2.0.0b1.
|
||||||
|
And because a==1.0.0 depends on c>=2.0.0b1 and there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
we can conclude that b*, a* are incompatible.
|
||||||
|
And because root depends on b and root depends on a, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: c was requested with a pre-release marker (e.g., >=2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: c was requested with a pre-release marker (e.g., c>=2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1567,10 +1639,25 @@ fn requires_transitive_prerelease_and_stable_dependency_many_versions_holes() ->
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of c>1.0.0, <2.0.0a5 | >2.0.0a7, <2.0.0b1 | >2.0.0b1, <2.0.0b5 and a==1.0.0 depends on c>1.0.0, <2.0.0a5 | >2.0.0a7, <2.0.0b1 | >2.0.0b1, <2.0.0b5, a==1.0.0 is forbidden.
|
╰─▶ Because there are no versions of c that satisfy any of:
|
||||||
And because there are no versions of a<1.0.0 | >1.0.0 and root depends on a, version solving failed.
|
c>1.0.0,<2.0.0a5
|
||||||
|
c>2.0.0a7,<2.0.0b1
|
||||||
|
c>2.0.0b1,<2.0.0b5
|
||||||
|
and a==1.0.0 depends on one of:
|
||||||
|
c>1.0.0,<2.0.0a5
|
||||||
|
c>2.0.0a7,<2.0.0b1
|
||||||
|
c>2.0.0b1,<2.0.0b5
|
||||||
|
we can conclude that a==1.0.0 is forbidden.
|
||||||
|
And because there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
and root depends on a, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: c was requested with a pre-release marker (e.g., >1.0.0, <2.0.0a5 | >2.0.0a7, <2.0.0b1 | >2.0.0b1, <2.0.0b5), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: c was requested with a pre-release marker (e.g., any of:
|
||||||
|
c>1.0.0,<2.0.0a5
|
||||||
|
c>2.0.0a7,<2.0.0b1
|
||||||
|
c>2.0.0b1,<2.0.0b5
|
||||||
|
), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1681,7 +1768,7 @@ fn requires_exact_version_does_not_exist() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there is no version of a==2.0.0 and root depends on a==2.0.0, version solving failed.
|
╰─▶ Because there is no version of a==2.0.0 and root depends on a==2.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1737,7 +1824,7 @@ fn requires_greater_version_does_not_exist() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a>1.0.0 and root depends on a>1.0.0, version solving failed.
|
╰─▶ Because there are no versions of a that satisfy a>1.0.0 and root depends on a>1.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1794,7 +1881,7 @@ fn requires_less_version_does_not_exist() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a<2.0.0 and root depends on a<2.0.0, version solving failed.
|
╰─▶ Because there are no versions of a that satisfy a<2.0.0 and root depends on a<2.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1978,8 +2065,11 @@ fn requires_transitive_incompatible_with_root_version() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of a<1.0.0 | >1.0.0 and a==1.0.0 depends on b==2.0.0, a depends on b==2.0.0.
|
╰─▶ Because there are no versions of a that satisfy any of:
|
||||||
And because root depends on a and root depends on b==1.0.0, version solving failed.
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
and a==1.0.0 depends on b==2.0.0, we can conclude that a depends on b==2.0.0.
|
||||||
|
And because root depends on a and root depends on b==1.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2054,9 +2144,15 @@ fn requires_transitive_incompatible_with_transitive() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of b<1.0.0 | >1.0.0 and b==1.0.0 depends on c==2.0.0, b depends on c==2.0.0.
|
╰─▶ Because there are no versions of b that satisfy any of:
|
||||||
And because a==1.0.0 depends on c==1.0.0 and there are no versions of a<1.0.0 | >1.0.0, a*, b* are incompatible.
|
b<1.0.0
|
||||||
And because root depends on b and root depends on a, version solving failed.
|
b>1.0.0
|
||||||
|
and b==1.0.0 depends on c==2.0.0, we can conclude that b depends on c==2.0.0.
|
||||||
|
And because a==1.0.0 depends on c==1.0.0 and there are no versions of a that satisfy any of:
|
||||||
|
a<1.0.0
|
||||||
|
a>1.0.0
|
||||||
|
we can conclude that a*, b* are incompatible.
|
||||||
|
And because root depends on b and root depends on a, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2116,8 +2212,8 @@ fn requires_python_version_does_not_exist() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of Python>=4.0 and a==1.0.0 depends on Python>=4.0, a==1.0.0 is forbidden.
|
╰─▶ Because there are no versions of Python that satisfy Python>=4.0 and a==1.0.0 depends on Python>=4.0, we can conclude that a==1.0.0 is forbidden.
|
||||||
And because root depends on a==1.0.0, version solving failed.
|
And because root depends on a==1.0.0 we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2173,8 +2269,8 @@ fn requires_python_version_less_than_current() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of Python<=3.8 and a==1.0.0 depends on Python<=3.8, a==1.0.0 is forbidden.
|
╰─▶ Because there are no versions of Python that satisfy Python<=3.8 and a==1.0.0 depends on Python<=3.8, we can conclude that a==1.0.0 is forbidden.
|
||||||
And because root depends on a==1.0.0, version solving failed.
|
And because root depends on a==1.0.0 we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2233,8 +2329,8 @@ fn requires_python_version_greater_than_current() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of Python>=3.10 and a==1.0.0 depends on Python>=3.10, a==1.0.0 is forbidden.
|
╰─▶ Because there are no versions of Python that satisfy Python>=3.10 and a==1.0.0 depends on Python>=3.10, we can conclude that a==1.0.0 is forbidden.
|
||||||
And because root depends on a==1.0.0, version solving failed.
|
And because root depends on a==1.0.0 we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2315,7 +2411,7 @@ fn requires_python_version_greater_than_current_many() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there is no version of a==1.0.0 and root depends on a==1.0.0, version solving failed.
|
╰─▶ Because there is no version of a==1.0.0 and root depends on a==1.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2451,17 +2547,24 @@ fn requires_python_version_greater_than_current_excluded() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of Python>=3.10, <3.11 and there are no versions of Python>=3.12, Python>=3.10, <3.11 | >=3.12 are incompatible.
|
╰─▶ Because there are no versions of Python that satisfy Python>=3.10,<3.11 and there are no versions of Python that satisfy Python>=3.12, we can conclude that any of:
|
||||||
And because there are no versions of Python>=3.11, <3.12, Python>=3.10 are incompatible.
|
Python>=3.10,<3.11
|
||||||
And because a==2.0.0 depends on Python>=3.10 and there are no versions of a>2.0.0, <3.0.0 | >3.0.0, <4.0.0 | >4.0.0, a>=2.0.0, <3.0.0 is forbidden. (1)
|
Python>=3.12
|
||||||
|
are incompatible.
|
||||||
|
And because there are no versions of Python that satisfy Python>=3.11,<3.12 we can conclude that Python>=3.10 are incompatible.
|
||||||
|
And because a==2.0.0 depends on Python>=3.10 and there are no versions of a that satisfy any of:
|
||||||
|
a>2.0.0,<3.0.0
|
||||||
|
a>3.0.0,<4.0.0
|
||||||
|
a>4.0.0
|
||||||
|
we can conclude that a>=2.0.0,<3.0.0 is forbidden. (1)
|
||||||
|
|
||||||
Because there are no versions of Python>=3.11, <3.12 and there are no versions of Python>=3.12, Python>=3.11 are incompatible.
|
Because there are no versions of Python that satisfy Python>=3.11,<3.12 and there are no versions of Python that satisfy Python>=3.12, we can conclude that Python>=3.11 are incompatible.
|
||||||
And because a==3.0.0 depends on Python>=3.11, a==3.0.0 is forbidden.
|
And because a==3.0.0 depends on Python>=3.11 we can conclude that a==3.0.0 is forbidden.
|
||||||
And because a>=2.0.0, <3.0.0 is forbidden (1), a>=2.0.0, <4.0.0 is forbidden. (2)
|
And because we know from (1) that a>=2.0.0,<3.0.0 is forbidden, we can conclude that a>=2.0.0,<4.0.0 is forbidden. (2)
|
||||||
|
|
||||||
Because there are no versions of Python>=3.12 and a==4.0.0 depends on Python>=3.12, a==4.0.0 is forbidden.
|
Because there are no versions of Python that satisfy Python>=3.12 and a==4.0.0 depends on Python>=3.12, we can conclude that a==4.0.0 is forbidden.
|
||||||
And because a>=2.0.0, <4.0.0 is forbidden (2), a>=2.0.0 is forbidden.
|
And because we know from (2) that a>=2.0.0,<4.0.0 is forbidden, we can conclude that a>=2.0.0 is forbidden.
|
||||||
And because root depends on a>=2.0.0, version solving failed.
|
And because root depends on a>=2.0.0 we can conclude that the requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use pubgrub::report::{DerivationTree, External, ReportFormatter};
|
use pubgrub::report::{DerivationTree, Derived, External, ReportFormatter};
|
||||||
use pubgrub::term::Term;
|
use pubgrub::term::Term;
|
||||||
use pubgrub::type_aliases::Map;
|
use pubgrub::type_aliases::Map;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
@ -37,7 +38,11 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
} else if set.as_singleton().is_some() {
|
} else if set.as_singleton().is_some() {
|
||||||
format!("there is no version of {package}{set}")
|
format!("there is no version of {package}{set}")
|
||||||
} else {
|
} else {
|
||||||
format!("there are no versions of {package}{set}")
|
format!(
|
||||||
|
"there are no versions of {} that satisfy {}",
|
||||||
|
package,
|
||||||
|
PackageRange::requires(package, &set)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
External::UnavailableDependencies(package, set) => {
|
External::UnavailableDependencies(package, set) => {
|
||||||
|
|
@ -45,7 +50,10 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
if set.as_ref() == &Range::full() {
|
if set.as_ref() == &Range::full() {
|
||||||
format!("dependencies of {package} are unavailable")
|
format!("dependencies of {package} are unavailable")
|
||||||
} else {
|
} else {
|
||||||
format!("dependencies of {package}{set} are unavailable")
|
format!(
|
||||||
|
"dependencies of {}are unavailable",
|
||||||
|
Padded::new("", &PackageRange::requires(package, &set), " ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
External::UnusableDependencies(package, set, reason) => {
|
External::UnusableDependencies(package, set, reason) => {
|
||||||
|
|
@ -57,7 +65,10 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
if set.as_ref() == &Range::full() {
|
if set.as_ref() == &Range::full() {
|
||||||
format!("dependencies of {package} are unusable: {reason}")
|
format!("dependencies of {package} are unusable: {reason}")
|
||||||
} else {
|
} else {
|
||||||
format!("dependencies of {package}{set} are unusable: {reason}",)
|
format!(
|
||||||
|
"dependencies of {}are unusable: {reason}",
|
||||||
|
Padded::new("", &PackageRange::requires(package, &set), " ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -65,7 +76,10 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
if set.as_ref() == &Range::full() {
|
if set.as_ref() == &Range::full() {
|
||||||
format!("dependencies of {package} are unusable")
|
format!("dependencies of {package} are unusable")
|
||||||
} else {
|
} else {
|
||||||
format!("dependencies of {package}{set} are unusable")
|
format!(
|
||||||
|
"dependencies of {}are unusable",
|
||||||
|
Padded::new("", &PackageRange::requires(package, &set), " ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,20 +91,33 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
{
|
{
|
||||||
format!("{package} depends on {dependency}")
|
format!("{package} depends on {dependency}")
|
||||||
} else if package_set.as_ref() == &Range::full() {
|
} else if package_set.as_ref() == &Range::full() {
|
||||||
format!("{package} depends on {dependency}{dependency_set}")
|
format!(
|
||||||
|
"{package} depends on {}",
|
||||||
|
PackageRange::depends(dependency, &dependency_set)
|
||||||
|
)
|
||||||
} else if dependency_set.as_ref() == &Range::full() {
|
} else if dependency_set.as_ref() == &Range::full() {
|
||||||
if matches!(package, PubGrubPackage::Root(_)) {
|
if matches!(package, PubGrubPackage::Root(_)) {
|
||||||
// Exclude the dummy version for root packages
|
// Exclude the dummy version for root packages
|
||||||
format!("{package} depends on {dependency}")
|
format!("{package} depends on {dependency}")
|
||||||
} else {
|
} else {
|
||||||
format!("{package}{package_set} depends on {dependency}")
|
format!(
|
||||||
|
"{}depends on {dependency}",
|
||||||
|
Padded::new("", &PackageRange::requires(package, &package_set), " ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if matches!(package, PubGrubPackage::Root(_)) {
|
if matches!(package, PubGrubPackage::Root(_)) {
|
||||||
// Exclude the dummy version for root packages
|
// Exclude the dummy version for root packages
|
||||||
format!("{package} depends on {dependency}{dependency_set}")
|
format!(
|
||||||
|
"{package} depends on {}",
|
||||||
|
PackageRange::depends(dependency, &dependency_set)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("{package}{package_set} depends on {dependency}{dependency_set}")
|
format!(
|
||||||
|
"{}depends on {}",
|
||||||
|
Padded::new("", &PackageRange::requires(package, &package_set), " "),
|
||||||
|
PackageRange::depends(dependency, &dependency_set)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +128,7 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
fn format_terms(&self, terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>) -> String {
|
fn format_terms(&self, terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>) -> String {
|
||||||
let terms_vec: Vec<_> = terms.iter().collect();
|
let terms_vec: Vec<_> = terms.iter().collect();
|
||||||
match terms_vec.as_slice() {
|
match terms_vec.as_slice() {
|
||||||
[] | [(PubGrubPackage::Root(_), _)] => "version solving failed".into(),
|
[] | [(PubGrubPackage::Root(_), _)] => "the requirements are unsatisfiable".into(),
|
||||||
[(package @ PubGrubPackage::Package(..), Term::Positive(range))] => {
|
[(package @ PubGrubPackage::Package(..), Term::Positive(range))] => {
|
||||||
let range = range.simplify(
|
let range = range.simplify(
|
||||||
self.available_versions
|
self.available_versions
|
||||||
|
|
@ -109,7 +136,7 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.iter(),
|
.iter(),
|
||||||
);
|
);
|
||||||
format!("{package}{range} is forbidden")
|
format!("{} is forbidden", PackageRange::requires(package, &range))
|
||||||
}
|
}
|
||||||
[(package @ PubGrubPackage::Package(..), Term::Negative(range))] => {
|
[(package @ PubGrubPackage::Package(..), Term::Negative(range))] => {
|
||||||
let range = range.simplify(
|
let range = range.simplify(
|
||||||
|
|
@ -118,7 +145,7 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.iter(),
|
.iter(),
|
||||||
);
|
);
|
||||||
format!("{package}{range} is mandatory")
|
format!("{} is mandatory", PackageRange::requires(package, &range))
|
||||||
}
|
}
|
||||||
[(p1, Term::Positive(r1)), (p2, Term::Negative(r2))] => self.format_external(
|
[(p1, Term::Positive(r1)), (p2, Term::Negative(r2))] => self.format_external(
|
||||||
&External::FromDependencyOf((*p1).clone(), r1.clone(), (*p2).clone(), r2.clone()),
|
&External::FromDependencyOf((*p1).clone(), r1.clone(), (*p2).clone(), r2.clone()),
|
||||||
|
|
@ -129,12 +156,134 @@ impl ReportFormatter<PubGrubPackage, Range<PubGrubVersion>> for PubGrubReportFor
|
||||||
slice => {
|
slice => {
|
||||||
let str_terms: Vec<_> = slice
|
let str_terms: Vec<_> = slice
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(p, t)| format!("{p}{}", PubGrubTerm::from_term((*t).clone())))
|
.map(|(p, t)| format!("{}", PackageTerm::new(p, t)))
|
||||||
.collect();
|
.collect();
|
||||||
str_terms.join(", ") + " are incompatible"
|
str_terms.join(", ") + " are incompatible"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simplest case, we just combine two external incompatibilities.
|
||||||
|
fn explain_both_external(
|
||||||
|
&self,
|
||||||
|
external1: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
external2: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
let external1 = self.format_external(external1);
|
||||||
|
let external2 = self.format_external(external2);
|
||||||
|
let terms = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"Because {}and {}we can conclude that {}",
|
||||||
|
Padded::from_string("", &external1, " "),
|
||||||
|
Padded::from_string("", &external2, ", "),
|
||||||
|
Padded::from_string("", &terms, ".")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Both causes have already been explained so we use their refs.
|
||||||
|
fn explain_both_ref(
|
||||||
|
&self,
|
||||||
|
ref_id1: usize,
|
||||||
|
derived1: &Derived<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
ref_id2: usize,
|
||||||
|
derived2: &Derived<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
// TODO: order should be chosen to make it more logical.
|
||||||
|
|
||||||
|
let derived1_terms = self.format_terms(&derived1.terms);
|
||||||
|
let derived2_terms = self.format_terms(&derived2.terms);
|
||||||
|
let current_terms = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"Because we know from ({}) that {}and we know from ({}) that {}{}",
|
||||||
|
ref_id1,
|
||||||
|
Padded::new("", &derived1_terms, " "),
|
||||||
|
ref_id2,
|
||||||
|
Padded::new("", &derived2_terms, ", "),
|
||||||
|
Padded::new("", ¤t_terms, "."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One cause is derived (already explained so one-line),
|
||||||
|
/// the other is a one-line external cause,
|
||||||
|
/// and finally we conclude with the current incompatibility.
|
||||||
|
fn explain_ref_and_external(
|
||||||
|
&self,
|
||||||
|
ref_id: usize,
|
||||||
|
derived: &Derived<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
external: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
// TODO: order should be chosen to make it more logical.
|
||||||
|
|
||||||
|
let derived_terms = self.format_terms(&derived.terms);
|
||||||
|
let external = self.format_external(external);
|
||||||
|
let current_terms = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"Because we know from ({}) that {}and {}we can conclude that {}",
|
||||||
|
ref_id,
|
||||||
|
Padded::new("", &derived_terms, " "),
|
||||||
|
Padded::new("", &external, ", "),
|
||||||
|
Padded::new("", ¤t_terms, "."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an external cause to the chain of explanations.
|
||||||
|
fn and_explain_external(
|
||||||
|
&self,
|
||||||
|
external: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
let external = self.format_external(external);
|
||||||
|
let terms = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"And because {}we can conclude that {}",
|
||||||
|
Padded::from_string("", &external, " "),
|
||||||
|
Padded::from_string("", &terms, "."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an already explained incompat to the chain of explanations.
|
||||||
|
fn and_explain_ref(
|
||||||
|
&self,
|
||||||
|
ref_id: usize,
|
||||||
|
derived: &Derived<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
let derived = self.format_terms(&derived.terms);
|
||||||
|
let current = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"And because we know from ({}) that {}we can conclude that {}",
|
||||||
|
ref_id,
|
||||||
|
Padded::from_string("", &derived, ", "),
|
||||||
|
Padded::from_string("", ¤t, "."),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an already explained incompat to the chain of explanations.
|
||||||
|
fn and_explain_prior_and_external(
|
||||||
|
&self,
|
||||||
|
prior_external: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
external: &External<PubGrubPackage, Range<PubGrubVersion>>,
|
||||||
|
current_terms: &Map<PubGrubPackage, Term<Range<PubGrubVersion>>>,
|
||||||
|
) -> String {
|
||||||
|
let prior_external = self.format_external(prior_external);
|
||||||
|
let external = self.format_external(external);
|
||||||
|
let terms = self.format_terms(current_terms);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"And because {}and {}we can conclude that {}",
|
||||||
|
Padded::from_string("", &prior_external, " "),
|
||||||
|
Padded::from_string("", &external, ", "),
|
||||||
|
Padded::from_string("", &terms, "."),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PubGrubReportFormatter<'_> {
|
impl PubGrubReportFormatter<'_> {
|
||||||
|
|
@ -267,35 +416,174 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
"hint".bold().cyan(),
|
"hint".bold().cyan(),
|
||||||
":".bold(),
|
":".bold(),
|
||||||
package.bold(),
|
package.bold(),
|
||||||
range.bold()
|
PackageRange::requires(package, range).bold()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A derivative of the [Term] type with custom formatting.
|
/// A [`Term`] and [`PubGrubPackage`] combination for display.
|
||||||
struct PubGrubTerm {
|
struct PackageTerm<'a> {
|
||||||
inner: Term<Range<PubGrubVersion>>,
|
package: &'a PubGrubPackage,
|
||||||
|
term: &'a Term<Range<PubGrubVersion>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for PubGrubTerm {
|
impl std::fmt::Display for PackageTerm<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match &self.inner {
|
match &self.term {
|
||||||
Term::Positive(set) => write!(f, "{set}"),
|
Term::Positive(set) => write!(f, "{}", PackageRange::requires(self.package, set)),
|
||||||
Term::Negative(set) => {
|
Term::Negative(set) => {
|
||||||
if let Some(version) = set.as_singleton() {
|
if let Some(version) = set.as_singleton() {
|
||||||
write!(f, "!={version}")
|
let package = self.package;
|
||||||
|
write!(f, "{package}!={version}")
|
||||||
} else {
|
} else {
|
||||||
write!(f, "!( {set} )")
|
write!(f, "!( {} )", PackageRange::requires(self.package, set))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PubGrubTerm {
|
impl PackageTerm<'_> {
|
||||||
fn from_term(term: Term<Range<PubGrubVersion>>) -> PubGrubTerm {
|
fn new<'a>(
|
||||||
PubGrubTerm { inner: term }
|
package: &'a PubGrubPackage,
|
||||||
|
term: &'a Term<Range<PubGrubVersion>>,
|
||||||
|
) -> PackageTerm<'a> {
|
||||||
|
PackageTerm { package, term }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of version ranges being displayed in [`PackageRange`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum PackageRangeKind {
|
||||||
|
Depends,
|
||||||
|
Requires,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`Range`] and [`PubGrubPackage`] combination for display.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PackageRange<'a> {
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<PubGrubVersion>,
|
||||||
|
kind: PackageRangeKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PackageRange<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if self.range.is_empty() {
|
||||||
|
write!(f, "∅")?;
|
||||||
|
} else {
|
||||||
|
let segments: Vec<_> = self.range.iter().collect();
|
||||||
|
if segments.len() > 1 {
|
||||||
|
match self.kind {
|
||||||
|
PackageRangeKind::Depends => write!(f, "one of:")?,
|
||||||
|
PackageRangeKind::Requires => write!(f, "any of:")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for segment in &segments {
|
||||||
|
if segments.len() > 1 {
|
||||||
|
write!(f, "\n ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", self.package)?;
|
||||||
|
match segment {
|
||||||
|
(Bound::Unbounded, Bound::Unbounded) => write!(f, "*")?,
|
||||||
|
(Bound::Unbounded, Bound::Included(v)) => write!(f, "<={v}")?,
|
||||||
|
(Bound::Unbounded, Bound::Excluded(v)) => write!(f, "<{v}")?,
|
||||||
|
(Bound::Included(v), Bound::Unbounded) => write!(f, ">={v}")?,
|
||||||
|
(Bound::Included(v), Bound::Included(b)) => {
|
||||||
|
if v == b {
|
||||||
|
write!(f, "=={v}")?;
|
||||||
|
} else {
|
||||||
|
write!(f, ">={v},<={b}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Bound::Included(v), Bound::Excluded(b)) => write!(f, ">={v},<{b}")?,
|
||||||
|
(Bound::Excluded(v), Bound::Unbounded) => write!(f, ">{v}")?,
|
||||||
|
(Bound::Excluded(v), Bound::Included(b)) => write!(f, ">{v},<={b}")?,
|
||||||
|
(Bound::Excluded(v), Bound::Excluded(b)) => write!(f, ">{v},<{b}")?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if segments.len() > 1 {
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageRange<'_> {
|
||||||
|
fn requires<'a>(
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<PubGrubVersion>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Requires,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn depends<'a>(
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<PubGrubVersion>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Depends,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts the given padding on the left and right sides of the content if
|
||||||
|
/// the content does not start and end with whitespace respectively.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Padded<'a, T: std::fmt::Display> {
|
||||||
|
left: &'a str,
|
||||||
|
content: &'a T,
|
||||||
|
right: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: std::fmt::Display> Padded<'a, T> {
|
||||||
|
fn new(left: &'a str, content: &'a T, right: &'a str) -> Self {
|
||||||
|
Padded {
|
||||||
|
left,
|
||||||
|
content,
|
||||||
|
right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Padded<'a, String> {
|
||||||
|
fn from_string(left: &'a str, content: &'a String, right: &'a str) -> Self {
|
||||||
|
Padded {
|
||||||
|
left,
|
||||||
|
content,
|
||||||
|
right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::fmt::Display> std::fmt::Display for Padded<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut result = String::new();
|
||||||
|
let content = self.content.to_string();
|
||||||
|
|
||||||
|
if let Some(char) = content.chars().next() {
|
||||||
|
if !char.is_whitespace() {
|
||||||
|
result.push_str(self.left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str(&content);
|
||||||
|
|
||||||
|
if let Some(char) = content.chars().last() {
|
||||||
|
if !char.is_whitespace() {
|
||||||
|
result.push_str(self.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{result}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ async fn black_disallow_prerelease() -> Result<()> {
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert_snapshot!(err, @r###"
|
assert_snapshot!(err, @r###"
|
||||||
Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.
|
Because there are no versions of black that satisfy black<=20.0 and root depends on black<=20.0, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
|
|
@ -524,7 +524,7 @@ async fn black_allow_prerelease_if_necessary() -> Result<()> {
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert_snapshot!(err, @r###"
|
assert_snapshot!(err, @r###"
|
||||||
Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.
|
Because there are no versions of black that satisfy black<=20.0 and root depends on black<=20.0, we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
|
|
@ -650,10 +650,10 @@ async fn msgraph_sdk() -> Result<()> {
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert_snapshot!(err, @r###"
|
assert_snapshot!(err, @r###"
|
||||||
Because there are no versions of msgraph-core>=1.0.0a2 and msgraph-sdk==1.0.0 depends on msgraph-core>=1.0.0a2, msgraph-sdk==1.0.0 is forbidden.
|
Because there are no versions of msgraph-core that satisfy msgraph-core>=1.0.0a2 and msgraph-sdk==1.0.0 depends on msgraph-core>=1.0.0a2, we can conclude that msgraph-sdk==1.0.0 is forbidden.
|
||||||
And because root depends on msgraph-sdk==1.0.0, version solving failed.
|
And because root depends on msgraph-sdk==1.0.0 we can conclude that the requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: msgraph-core was requested with a pre-release marker (e.g., >=1.0.0a2), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
hint: msgraph-core was requested with a pre-release marker (e.g., msgraph-core>=1.0.0a2), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue