standard_version/
cargo.rs1use crate::toml_helpers;
9use crate::version_file::{VersionFile, VersionFileError};
10
11const PACKAGE_SECTION: &str = "[package]";
13
14const WORKSPACE_PACKAGE_SECTION: &str = "[workspace.package]";
16
17#[derive(Debug, Clone, Copy)]
22pub struct CargoVersionFile;
23
24impl CargoVersionFile {
25 fn active_section(content: &str) -> Option<&'static str> {
27 if toml_helpers::detect_version_in_section(content, PACKAGE_SECTION) {
28 Some(PACKAGE_SECTION)
29 } else if toml_helpers::detect_version_in_section(content, WORKSPACE_PACKAGE_SECTION) {
30 Some(WORKSPACE_PACKAGE_SECTION)
31 } else {
32 None
33 }
34 }
35}
36
37impl VersionFile for CargoVersionFile {
38 fn name(&self) -> &str {
39 "Cargo.toml"
40 }
41
42 fn filenames(&self) -> &[&str] {
43 &["Cargo.toml"]
44 }
45
46 fn detect(&self, content: &str) -> bool {
47 Self::active_section(content).is_some()
48 }
49
50 fn read_version(&self, content: &str) -> Option<String> {
51 let section = Self::active_section(content)?;
52 toml_helpers::read_version_in_section(content, section)
53 }
54
55 fn write_version(&self, content: &str, new_version: &str) -> Result<String, VersionFileError> {
56 let section = Self::active_section(content).ok_or(VersionFileError::NoVersionField)?;
57 toml_helpers::write_version_in_section(content, section, new_version)
58 }
59}
60
61#[cfg(test)]
66mod tests {
67 use super::*;
68
69 const BASIC_TOML: &str = r#"[package]
70name = "my-crate"
71version = "0.1.0"
72edition = "2021"
73"#;
74
75 const MULTI_SECTION_TOML: &str = r#"[package]
76name = "my-crate"
77version = "0.1.0"
78
79[dependencies]
80foo = { version = "1.0" }
81"#;
82
83 #[test]
86 fn detect_with_package_version() {
87 assert!(CargoVersionFile.detect(BASIC_TOML));
88 }
89
90 #[test]
91 fn detect_without_package_section() {
92 let content = "[dependencies]\nfoo = \"1\"\n";
93 assert!(!CargoVersionFile.detect(content));
94 }
95
96 #[test]
97 fn detect_version_only_in_deps() {
98 let content = "[package]\nname = \"x\"\n\n[dependencies]\nfoo = { version = \"1\" }\n";
99 assert!(!CargoVersionFile.detect(content));
100 }
101
102 #[test]
105 fn read_version_basic() {
106 assert_eq!(
107 CargoVersionFile.read_version(BASIC_TOML),
108 Some("0.1.0".to_string()),
109 );
110 }
111
112 #[test]
113 fn read_version_no_package() {
114 let content = "[dependencies]\nfoo = \"1\"\n";
115 assert_eq!(CargoVersionFile.read_version(content), None);
116 }
117
118 #[test]
121 fn write_version_basic() {
122 let result = CargoVersionFile.write_version(BASIC_TOML, "1.0.0").unwrap();
123 assert!(result.contains("version = \"1.0.0\""));
124 assert!(result.contains("name = \"my-crate\""));
125 assert!(result.contains("edition = \"2021\""));
126 }
127
128 #[test]
129 fn write_version_only_in_package_section() {
130 let result = CargoVersionFile
131 .write_version(MULTI_SECTION_TOML, "2.0.0")
132 .unwrap();
133 assert!(result.contains("version = \"2.0.0\""));
134 assert!(result.contains("foo = { version = \"1.0\" }"));
136 }
137
138 #[test]
139 fn write_version_no_field_returns_error() {
140 let content = "[package]\nname = \"x\"\n";
141 let err = CargoVersionFile.write_version(content, "1.0.0");
142 assert!(err.is_err());
143 }
144
145 #[test]
146 fn write_version_preserves_no_trailing_newline() {
147 let content = "[package]\nname = \"x\"\nversion = \"0.1.0\"";
148 let result = CargoVersionFile.write_version(content, "0.2.0").unwrap();
149 assert!(!result.ends_with('\n'));
150 assert!(result.contains("version = \"0.2.0\""));
151 }
152
153 const WORKSPACE_TOML: &str = r#"[workspace]
156members = ["app"]
157resolver = "2"
158
159[workspace.package]
160version = "0.10.0"
161edition = "2021"
162
163[workspace.dependencies]
164anyhow = "1"
165"#;
166
167 #[test]
168 fn detect_workspace_package_version() {
169 assert!(CargoVersionFile.detect(WORKSPACE_TOML));
170 }
171
172 #[test]
173 fn read_version_workspace_package() {
174 assert_eq!(
175 CargoVersionFile.read_version(WORKSPACE_TOML),
176 Some("0.10.0".to_string()),
177 );
178 }
179
180 #[test]
181 fn write_version_workspace_package() {
182 let result = CargoVersionFile
183 .write_version(WORKSPACE_TOML, "0.11.0")
184 .unwrap();
185 assert!(result.contains("version = \"0.11.0\""));
186 assert!(result.contains("anyhow = \"1\""));
188 assert!(result.contains("members = [\"app\"]"));
190 }
191
192 #[test]
193 fn package_section_takes_priority_over_workspace_package() {
194 let content = "[package]\nname = \"x\"\nversion = \"1.0.0\"\n\n[workspace.package]\nversion = \"2.0.0\"\n";
197 assert_eq!(
198 CargoVersionFile.read_version(content),
199 Some("1.0.0".to_string()),
200 );
201 }
202}