@@ -52,20 +52,77 @@ def get_codebase_version() -> Version:
5252 return Version (str (_version_table (doc , PRIMARY_PYPROJECT )["version" ]))
5353
5454
55- def replace_top_level_version (path : Path , new_version : str , * , dry_run : bool ) -> str | None :
56- doc = tomlkit .parse (path .read_text ())
57- table = _version_table (doc , path )
55+ def _set_top_level_version (doc , path : Path , new_version : str ) -> str | None :
56+ """Update the top-level version in an already-parsed tomlkit doc.
5857
58+ Returns the old version string, or *None* if it was already up-to-date.
59+ """
60+ table = _version_table (doc , path )
5961 old_version = str (table ["version" ])
6062 if old_version == new_version :
6163 return None
62-
6364 table ["version" ] = new_version
64- if not dry_run :
65+ return old_version
66+
67+
68+ def _set_path_dep_versions (doc , new_version : str ) -> list [tuple [str , str ]]:
69+ """Update the ``version`` field of every dependency that also has a ``path`` key.
70+
71+ Path dependencies are local dependencies, but they need to have the correct version,
72+ otherwise `cargo publish` will croak.
73+
74+ Returns a list of ``(dep_name, old_version)`` for each dependency that was
75+ changed. Dependencies that only have a ``version`` (external crates.io deps)
76+ are left untouched.
77+ """
78+ changed : list [tuple [str , str ]] = []
79+ for dep_name , dep_value in doc .get ("dependencies" , {}).items ():
80+ if not isinstance (dep_value , dict ):
81+ continue
82+ if "path" not in dep_value or "version" not in dep_value :
83+ continue
84+ old = str (dep_value ["version" ])
85+ if old == new_version :
86+ continue
87+ dep_value ["version" ] = new_version
88+ changed .append ((dep_name , old ))
89+ return changed
90+
91+
92+ def replace_pyproject_version (
93+ path : Path , new_version : str , * , dry_run : bool
94+ ) -> str | None :
95+ """Rewrite the top-level version in a pyproject.toml file.
96+
97+ Returns the old version string, or *None* if the file was already
98+ up-to-date (in which case the file is not written).
99+ """
100+ doc = tomlkit .parse (path .read_text ())
101+ old_version = _set_top_level_version (doc , path , new_version )
102+ if old_version is not None and not dry_run :
65103 path .write_text (tomlkit .dumps (doc ))
66104 return old_version
67105
68106
107+ def replace_cargo_version (
108+ path : Path , new_version : str , * , dry_run : bool
109+ ) -> tuple [str | None , list [tuple [str , str ]]]:
110+ """Rewrite the package version and all versioned path-dependency versions
111+ in a Cargo.toml file in a single parse/write pass.
112+
113+ Returns ``(old_package_version | None, [(dep_name, old_dep_version), ...])``,
114+ where the first element is *None* when the package version was already
115+ up-to-date. The file is written at most once, and only when something
116+ actually changed.
117+ """
118+ doc = tomlkit .parse (path .read_text ())
119+ old_package = _set_top_level_version (doc , path , new_version )
120+ changed_deps = _set_path_dep_versions (doc , new_version )
121+ if (old_package is not None or changed_deps ) and not dry_run :
122+ path .write_text (tomlkit .dumps (doc ))
123+ return old_package , changed_deps
124+
125+
69126def confirm (prompt : str ) -> bool :
70127 try :
71128 answer = input (f"{ prompt } [y/N]: " ).strip ().lower ()
0 commit comments