Coverage for rust2rpm/metadata.py: 71%

63 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-10-27 15:21 +0100

1from typing import Optional 

2 

3from cargo2rpm.metadata import Metadata, Package 

4from cargo2rpm.semver import VersionReq, Comparator, Op as Operator 

5 

6from rust2rpm import log 

7 

8 

9def get_required_features_for_binaries(package: Package) -> set[str]: 

10 required_features = set() 

11 

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) 

19 

20 return required_features 

21 

22 

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] 

26 

27 with_bin = [] 

28 with_cdylib = [] 

29 

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) 

36 

37 if len(with_cdylib) == 1: 

38 return with_cdylib[0] 

39 

40 if len(with_bin) == 1: 

41 return with_bin[0] 

42 

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: ") 

52 

53 if hint not in choices: 

54 log.error("Invalid crate name.") 

55 raise ValueError 

56 

57 for package in metadata.packages: 

58 if package.name == hint: 

59 return package 

60 

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 

68 

69 log.error("Heuristic for determining the main crate of the workspace failed.") 

70 log.error("Please use '--interactive' mode.") 

71 

72 raise ValueError 

73 

74 

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 

80 

81 if dep.startswith("dep:"): 

82 if len(deps) != 1: 

83 return True 

84 if dep.removeprefix("dep:") != name: 

85 return True 

86 

87 return False 

88 

89 

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}") 

97 

98 

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