1# This script implements the workspace inheritance mechanism described
2# here: https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table
3#
4# Please run `mypy --strict`, `black`, and `isort --profile black` on this after editing, thanks!
5
6import sys
7from typing import Any
8
9import tomli
10import tomli_w
11
12
13def load_file(path: str) -> dict[str, Any]:
14 with open(path, "rb") as f:
15 return tomli.load(f)
16
17
18def replace_key(
19 workspace_manifest: dict[str, Any], table: dict[str, Any], section: str, key: str
20) -> bool:
21 if (
22 isinstance(table[key], dict)
23 and "workspace" in table[key]
24 and table[key]["workspace"] is True
25 ):
26 print("replacing " + key)
27
28 replaced = table[key]
29 del replaced["workspace"]
30
31 workspace_copy = workspace_manifest[section][key]
32
33 if section == "dependencies":
34 crate_features = replaced.get("features")
35
36 if type(workspace_copy) is str:
37 replaced["version"] = workspace_copy
38 else:
39 replaced.update(workspace_copy)
40
41 merged_features = (crate_features or []) + (
42 workspace_copy.get("features") or []
43 )
44
45 if len(merged_features) > 0:
46 # Dictionaries are guaranteed to be ordered (https://stackoverflow.com/a/7961425)
47 replaced["features"] = list(dict.fromkeys(merged_features))
48 elif section == "package":
49 table[key] = replaced = workspace_copy
50
51 return True
52
53 return False
54
55
56def replace_dependencies(
57 workspace_manifest: dict[str, Any], root: dict[str, Any]
58) -> bool:
59 changed = False
60
61 for key in ["dependencies", "dev-dependencies", "build-dependencies"]:
62 if key in root:
63 for k in root[key].keys():
64 changed |= replace_key(workspace_manifest, root[key], "dependencies", k)
65
66 return changed
67
68
69def main() -> None:
70 top_cargo_toml = load_file(sys.argv[2])
71
72 if "workspace" not in top_cargo_toml:
73 # If top_cargo_toml is not a workspace manifest, then this script was probably
74 # ran on something that does not actually use workspace dependencies
75 print(f"{sys.argv[2]} is not a workspace manifest, doing nothing.")
76 return
77
78 crate_manifest = load_file(sys.argv[1])
79 workspace_manifest = top_cargo_toml["workspace"]
80
81 if "workspace" in crate_manifest:
82 return
83
84 changed = False
85
86 for key in crate_manifest["package"].keys():
87 changed |= replace_key(
88 workspace_manifest, crate_manifest["package"], "package", key
89 )
90
91 changed |= replace_dependencies(workspace_manifest, crate_manifest)
92
93 if "target" in crate_manifest:
94 for key in crate_manifest["target"].keys():
95 changed |= replace_dependencies(
96 workspace_manifest, crate_manifest["target"][key]
97 )
98
99 if not changed:
100 return
101
102 with open(sys.argv[1], "wb") as f:
103 tomli_w.dump(crate_manifest, f)
104
105
106if __name__ == "__main__":
107 main()