Skip to main content

Maintain/Build/
TomlEdit.rs

1//=============================================================================//
2// File Path: Element/Maintain/Source/Build/TomlEdit.rs
3//=============================================================================//
4// Module: TomlEdit
5//
6// Brief Description: Implements TOML file editing for Cargo.toml.
7//
8// RESPONSIBILITIES:
9// ================
10//
11// Primary:
12// - Dynamically modify name fields in Cargo.toml files
13// - Update package.name, package.default-run, lib.name, and bin.name
14// - Preserve the original structure and formatting
15//
16// Secondary:
17// - Provide detailed logging of changes made
18// - Return whether any modifications occurred
19//
20// ARCHITECTURAL ROLE:
21// ===================
22//
23// Position:
24// - Infrastructure/File manipulation layer
25// - TOML editing functionality
26//
27// Dependencies (What this module requires):
28// - External crates: toml_edit, std (fs, log)
29// - Internal modules: Error::BuildError
30// - Traits implemented: None
31//
32// Dependents (What depends on this module):
33// - Build orchestration functions
34// - Process function
35//
36// IMPLEMENTATION DETAILS:
37// =======================
38//
39// Design Patterns:
40// - Builder pattern (via toml_edit)
41// - Functional pattern
42//
43// Performance Considerations:
44// - Complexity: O(n) - parsing and modifying based on file size
45// - Memory usage patterns: In-memory document manipulation
46// - Hot path optimizations: None needed
47//
48// Thread Safety:
49// - Thread-safe: No (not designed for concurrent access to files)
50// - Synchronization mechanisms used: None
51// - Interior mutability considerations: None
52//
53// Error Handling:
54// - Error types returned: BuildError (Edit type)
55// - Recovery strategies: Propagate error up; Guard restores original file
56//
57// EXAMPLES:
58// =========
59//
60// Example 1: Product name change
61use std::{fs, path::Path};
62
63use log::{debug, info, warn};
64use toml_edit::{DocumentMut as TomlDocument, Item as TomlItem, Value as TomlValue};
65
66/// ```rust
67/// use crate::Maintain::Source::Build::TomlEdit;
68/// let cargo_path = PathBuf::from("Cargo.toml");
69/// let old_name = "Mountain";
70/// let new_name = "Debug_NodeEnvironment_Mountain";
71/// let modified = TomlEdit(&cargo_path, old_name, new_name)?;
72/// if modified {
73/// 	println!("Cargo.toml was updated");
74/// }
75/// ```
76// Example 2: No change needed
77/// ```rust
78/// use crate::Maintain::Source::Build::TomlEdit;
79/// let modified = TomlEdit(&cargo_path, "Mountain", "Mountain")?;
80/// // modified = false, no changes made
81/// ```
82//
83//=============================================================================//
84// IMPLEMENTATION
85//=============================================================================//
86use crate::Build::Error::Error as BuildError;
87
88/// Dynamically modifies specific name fields within a `Cargo.toml` file.
89///
90/// This function searches for and updates the following fields if they match
91/// the old name:
92/// - `package.name` - The package name identifier
93/// - `package.default-run` - The default binary to run
94/// - `lib.name` - The library name
95/// - `bin.name` - The binary name (first occurrence)
96///
97/// The function preserves the existing file structure and ensures that only
98/// matching names are updated, preventing unintended modifications.
99///
100/// # Parameters
101///
102/// * `File` - Path to the Cargo.toml file to modify
103/// * `Old` - The current name to search for and replace
104/// * `Current` - The new name to set
105///
106/// # Returns
107///
108/// Returns a `Result<bool>` indicating:
109/// - `Ok(true)` - The file was modified and saved
110/// - `Ok(false)` - No changes were made (either no matches or old == current)
111/// - `Err(BuildError)` - An error occurred during modification
112///
113/// # Errors
114///
115/// * `BuildError::Io` - If the file cannot be read or written
116/// * `BuildError::Edit` - If the TOML document cannot be parsed or modified
117///
118/// # Behavior
119///
120/// - If `Old` equals `Current`, returns `Ok(false)` without modifying the file
121/// - Only updates fields that exactly match the old name
122/// - Updates are atomic: the file is written only if at least one change occurs
123/// - Logs all changes made at INFO level
124///
125/// # Example
126///
127/// ```no_run
128/// use crate::Maintain::Source::Build::TomlEdit;
129/// let path = PathBuf::from("Cargo.toml");
130/// let modified = TomlEdit(&path, "Mountain", "Debug_Mountain")?;
131/// if modified {
132/// 	println!("Successfully updated Cargo.toml");
133/// }
134/// ```
135pub fn TomlEdit(File:&Path, Old:&str, Current:&str) -> Result<bool, BuildError> {
136	debug!(target: "Build::Toml", "Attempting to modify TOML file: {}", File.display());
137
138	let Data = fs::read_to_string(File)?;
139
140	if Old == Current {
141		info!(
142			target: "Build::Toml",
143
144			"Old name '{}' is the same as current name '{}'. No changes needed for {}.",
145
146			Old,
147
148			Current,
149
150			File.display()
151		);
152
153		return Ok(false);
154	}
155
156	debug!(target: "Build::Toml", "Old name: '{}', Current name: '{}'", Old, Current);
157
158	let mut Parsed:TomlDocument = Data.parse()?;
159
160	let mut PackageChange = false;
161
162	let mut LibraryChange = false;
163
164	let mut BinaryChange = false;
165
166	let mut DefaultChange = false;
167
168	// Update package.name
169	if let Some(PackageTable) = Parsed.get_mut("package").and_then(|Item| Item.as_table_mut()) {
170		if let Some(NameItem) = PackageTable.get_mut("name") {
171			if NameItem.as_str() == Some(Old) {
172				*NameItem = TomlItem::Value(TomlValue::String(toml_edit::Formatted::new(Current.to_string())));
173
174				PackageChange = true;
175
176				debug!(target: "Build::Toml", "Changed package.name");
177			}
178		}
179
180		// Update package.default-run
181		if let Some(RunItem) = PackageTable.get_mut("default-run") {
182			if RunItem.as_str() == Some(Old) {
183				*RunItem = TomlItem::Value(TomlValue::String(toml_edit::Formatted::new(Current.to_string())));
184
185				DefaultChange = true;
186
187				debug!(target: "Build::Toml", "Changed package.default-run");
188			}
189		}
190	}
191
192	// Update lib.name
193	if let Some(LibraryTable) = Parsed.get_mut("lib").and_then(|Item| Item.as_table_mut()) {
194		if let Some(NameItem) = LibraryTable.get_mut("name") {
195			if NameItem.as_str() == Some(Old) {
196				*NameItem = TomlItem::Value(TomlValue::String(toml_edit::Formatted::new(Current.to_string())));
197
198				LibraryChange = true;
199
200				debug!(target: "Build::Toml", "Changed lib.name");
201			}
202		}
203	}
204
205	// Update bin.name (first occurrence)
206	if let Some(BinArray) = Parsed.get_mut("bin").and_then(|Item| Item.as_array_of_tables_mut()) {
207		for Table in BinArray.iter_mut() {
208			if let Some(NameItem) = Table.get_mut("name") {
209				if NameItem.as_str() == Some(Old) {
210					*NameItem = TomlItem::Value(TomlValue::String(toml_edit::Formatted::new(Current.to_string())));
211
212					BinaryChange = true;
213
214					debug!(target: "Build::Toml", "Changed a bin.name entry to '{}'", Current);
215
216					break;
217				}
218			}
219		}
220	}
221
222	// Write the file if any changes were made
223	if PackageChange || LibraryChange || BinaryChange || DefaultChange {
224		let Output = Parsed.to_string();
225
226		fs::write(File, Output)?;
227
228		let mut ModifiedItems = Vec::new();
229
230		if PackageChange {
231			ModifiedItems.push("package.name");
232		}
233
234		if DefaultChange {
235			ModifiedItems.push("package.default-run");
236		}
237
238		if LibraryChange {
239			ModifiedItems.push("lib.name");
240		}
241
242		if BinaryChange {
243			ModifiedItems.push("bin.name");
244		}
245
246		info!(
247			target: "Build::Toml",
248
249			"Temporarily changed {} in {} to: {}",
250
251			ModifiedItems.join(", "),
252
253			File.display(),
254
255			Current
256		);
257
258		Ok(true)
259	} else {
260		warn!(
261			target: "Build::Toml",
262
263			"Name '{}' not found in relevant sections of {}. No changes made to file.",
264
265			Old,
266
267			File.display()
268		);
269
270		Ok(false)
271	}
272}