Coverage for rust2rpm/metadata.py: 71%
63 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-27 15:21 +0100
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-27 15:21 +0100
1from typing import Optional
3from cargo2rpm.metadata import Metadata, Package
4from cargo2rpm.semver import VersionReq, Comparator, Op as Operator
6from rust2rpm import log
9def get_required_features_for_binaries(package: Package) -> set[str]:
10 required_features = set()
12 for target in package.targets:
13 if "bin" in target.kind and "bin" in target.crate_types:
14 if reqs := target.required_features:
15 required_features.update(reqs)
16 if "cdylib" in target.kind and "cdylib" in target.crate_types: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true
17 if reqs := target.required_features:
18 required_features.update(reqs)
20 return required_features
23def guess_main_package(metadata: Metadata, hint: Optional[str] = None, interactive: bool = False) -> Package:
24 if not metadata.is_workspace(): 24 ↛ 25line 24 didn't jump to line 25 because the condition on line 24 was never true
25 return metadata.packages[0]
27 with_bin = []
28 with_cdylib = []
30 for package in metadata.packages:
31 for target in package.targets:
32 if "bin" in target.kind and "bin" in target.crate_types:
33 with_bin.append(package)
34 if "cdylib" in target.kind and "cdylib" in target.crate_types:
35 with_cdylib.append(package)
37 if len(with_cdylib) == 1:
38 return with_cdylib[0]
40 if len(with_bin) == 1:
41 return with_bin[0]
43 if interactive: # pragma nocover
44 choices = [package.name for package in with_bin] + [package.name for package in with_cdylib]
45 log.warn(
46 'Heuristic for determining the "main" crate of the workspace failed. '
47 + 'Please specify the name of the "main" crate (usually the crate which '
48 + "provides the user-facing CLI) manually."
49 )
50 log.info("Choices: " + ", ".join(choices))
51 hint = input("Crate name: ")
53 if hint not in choices:
54 log.error("Invalid crate name.")
55 raise ValueError
57 for package in metadata.packages:
58 if package.name == hint:
59 return package
61 if hint:
62 for package in metadata.packages:
63 if package.name == hint:
64 log.warn(
65 f'Using {hint!r} as the name of the "main" crate. If this is not correct, use interactive mode.'
66 )
67 return package
69 log.error("Heuristic for determining the main crate of the workspace failed.")
70 log.error("Please use '--interactive' mode.")
72 raise ValueError
75def package_uses_rust_1_60_feature_syntax(features: dict[str, list[str]]) -> bool:
76 for name, deps in features.items():
77 for dep in deps:
78 if "?/" in dep:
79 return True
81 if dep.startswith("dep:"):
82 if len(deps) != 1:
83 return True
84 if dep.removeprefix("dep:") != name:
85 return True
87 return False
90def warn_if_package_uses_restrictive_dependencies(package: Package):
91 for dependency in package.dependencies:
92 name = dependency.rename or dependency.name
93 req = VersionReq.parse(dependency.req)
94 for comp in req.comparators:
95 if _is_strict_dep(comp):
96 log.warn(f"Dependency on {name!r} stricter than necessary: {comp}")
99def _is_strict_dep(comp: Comparator) -> bool:
100 # dependencies like "~1.0" are stricter than SemVer
101 if comp.op == Operator.TILDE and comp.major > 0 and comp.minor is not None:
102 return True
103 # dependencies like "1.0.*" are stricter than SemVer
104 if comp.op == Operator.WILDCARD and comp.major > 0 and comp.minor is not None:
105 return True
106 # dependencies like "=1.2" are stricter than SemVer
107 if comp.op == Operator.EXACT and comp.major > 0 and comp.minor is not None and comp.patch is None:
108 return True
109 return False