Skip to main content

Maintain/Build/
Definition.rs

1//=============================================================================//
2// File Path: Element/Maintain/Source/Build/Definition.rs
3//=============================================================================//
4// Module: Definition
5//
6// Brief Description: Build system type definitions and data structures.
7//
8// RESPONSIBILITIES:
9// ================
10//
11// Primary:
12// - Define argument parsing structures
13// - Define configuration file parsing structures
14// - Define file guard structures
15//
16// Secondary:
17// - Provide type-safe access to build configuration
18//
19// ARCHITECTURAL ROLE:
20// ===================
21//
22// Position:
23// - Infrastructure/Data structures layer
24// - Type definitions
25//
26// Dependencies (What this module requires):
27// - External crates: clap, serde, std (fs, log, path)
28// - Internal modules: Constant
29// - Traits implemented: Drop (for Guard), Parser (for Argument)
30//
31// Dependents (What depends on this module):
32// - Build orchestration functions
33// - Entry point functions
34//
35// IMPLEMENTATION DETAILS:
36// =======================
37//
38// Design Patterns:
39// - Builder pattern (via clap derive)
40// - Data transfer object pattern
41// - RAII pattern (Guard)
42//
43// Performance Considerations:
44// - Complexity: O(n) - depends on operation
45// - Memory usage patterns: Struct-based memory layout
46// - Hot path optimizations: None
47//
48// Thread Safety:
49// - Thread-safe: Yes (Argument derives Clone, Guard not thread-safe by design)
50// - Synchronization mechanisms used: None
51// - Interior mutability considerations: None
52//
53// Error Handling:
54// - Error types returned: BuildError
55// - Recovery strategies: Guard restores files on error
56//
57// EXAMPLES:
58// =========
59//
60// Example 1: Parsing arguments
61use std::{
62	fs,
63	path::{Path, PathBuf},
64};
65
66use clap::Parser;
67use serde::Deserialize;
68use log::{error, info};
69
70/// ```rust
71/// use clap::Parser;
72///
73/// use crate::Maintain::Source::Build::Definition;
74/// let argument = Argument::parse();
75/// println!("Building directory: {}", argument.Directory);
76/// ```
77// Example 2: Creating a guard
78/// ```rust
79/// use crate::Maintain::Source::Build::Definition;
80/// let guard = Guard::New(file_path, description.to_string())?;
81/// ```
82// Example 3: Parsing cargo.toml
83/// ```rust
84/// use crate::Maintain::Source::Build::Definition;
85/// let content = std::fs::read_to_string("Cargo.toml")?;
86/// let manifest:Manifest = toml::from_str(&content)?;
87/// ```
88//
89//=============================================================================//
90// IMPLEMENTATION
91//=============================================================================//
92use crate::Build::Constant::*;
93
94//=============================================================================
95// Argument Definition
96//=============================================================================
97
98/// Represents parsed command-line arguments and environment variables that
99/// control the build.
100///
101/// This struct is generated by `clap` from the `Parser` derive macro and
102/// automatically parses command-line arguments and environment variables into
103/// a strongly-typed configuration object. Each field can be configured via:
104/// - Command-line arguments (long form, e.g., `--directory`)
105/// - Environment variables (as specified in the `env` attribute)
106/// - Default values (as specified in the `default_value` attribute)
107///
108/// Required fields must be provided, while optional fields can be omitted.
109/// The `Command` field is special in that it accepts all remaining arguments
110/// as the build command to execute.
111///
112/// # Field Descriptions
113///
114/// - **Directory**: The main directory of the project containing Cargo.toml
115/// - **Name**: The original base name of the project/package
116/// - **Prefix**: The prefix for the application's bundle identifier
117/// - **Browser**: Flag for browser-specific build aspects
118/// - **Bundle**: Flag for bundling-specific aspects
119/// - **Compile**: Flag for compile-specific aspects
120/// - **Clean**: Flag for cleaning-specific aspects
121/// - **Debug**: Flag for debug-specific aspects
122/// - **Dependency**: Information about a dependency (org/repo or boolean)
123/// - **Environment**: The Node.js environment (development, production, etc.)
124/// - **NodeVersion**: The Node.js sidecar version to bundle
125/// - **Command**: The build command and its arguments to execute
126#[derive(Parser, Debug, Clone)]
127#[clap(
128	author,
129	version,
130	about = "Prepares, builds, and restores project configurations."
131)]
132pub struct Argument {
133	/// The main directory of the project.
134	///
135	/// This field specifies the project directory that contains the
136	/// `Cargo.toml` and `tauri.conf.json` files. It can be set via:
137	/// - Command-line: `--directory <path>`
138	/// - Environment: `MOUNTAIN_DIR`
139	/// - Default: "Element/Mountain"
140	#[clap(long, env = DirEnv, default_value = DirectoryDefault)]
141	pub Directory:String,
142
143	/// The original base name of the project/package.
144	///
145	/// This field specifies the base name of the project, which serves as
146	/// the suffix for generated product names. It can be set via:
147	/// - Command-line: `--name <name>`
148	/// - Environment: `MOUNTAIN_ORIGINAL_BASE_NAME`
149	/// - Default: "Mountain"
150	#[clap(long, env = NameEnv, default_value = NameDefault)]
151	pub Name:String,
152
153	/// The prefix for the application's bundle identifier.
154	///
155	/// This field specifies the reverse domain prefix for the bundle
156	/// identifier. It can be set via:
157	/// - Command-line: `--prefix <prefix>`
158	/// - Environment: `MOUNTAIN_BUNDLE_ID_PREFIX`
159	/// - Default: "land.editor.binary"
160	#[clap(long, env = PrefixEnv, default_value = PrefixDefault)]
161	pub Prefix:String,
162
163	/// Flag or value indicating browser-specific build aspects.
164	///
165	/// When set to "true", enables browser-specific configuration and affects
166	/// the generated product name and bundle identifier.
167	#[clap(long, env = BrowserEnv)]
168	pub Browser:Option<String>,
169
170	/// Flag or value indicating bundling-specific aspects.
171	///
172	/// When set to "true", enables bundling-specific configuration and affects
173	/// the generated product name and bundle identifier.
174	#[clap(long, env = BundleEnv)]
175	pub Bundle:Option<String>,
176
177	/// Flag or value indicating compile-specific aspects.
178	///
179	/// When set to "true", enables compile-specific configuration and affects
180	/// the generated product name and bundle identifier.
181	#[clap(long, env = CompileEnv)]
182	pub Compile:Option<String>,
183
184	/// Flag or value indicating cleaning-specific aspects.
185	///
186	/// When set to "true", enables clean-specific configuration and affects
187	/// the generated product name and bundle identifier.
188	#[clap(long, env = CleanEnv)]
189	pub Clean:Option<String>,
190
191	/// Flag or value indicating debug-specific aspects.
192	///
193	/// When set to "true", enables debug-specific configuration and affects
194	/// the generated product name and bundle identifier. Also automatically
195	/// detected if the command contains "--debug".
196	#[clap(long, env = DebugEnv)]
197	pub Debug:Option<String>,
198
199	/// Information about a dependency, often 'org/repo' or a boolean string.
200	///
201	/// This field specifies dependency information that affects the generated
202	/// product name and bundle identifier. Can be:
203	/// - "true" for generic dependencies
204	/// - "org/repo" for specific repository dependencies
205	/// - Any custom string
206	#[clap(long, env = DependencyEnv)]
207	pub Dependency:Option<String>,
208
209	/// The Node.js environment (e.g., "development", "production").
210	///
211	/// This field specifies the Node.js runtime environment and affects the
212	/// generated product name and bundle identifier.
213	#[clap(long, env = NodeEnv)]
214	pub Environment:Option<String>,
215
216	/// Specifies the Node.js sidecar version to bundle (e.g., "22").
217	///
218	/// This field specifies which Node.js version should be bundled as a
219	/// sidecar binary with the application. The executable is sourced from
220	/// the Element/SideCar directory.
221	#[clap(long, env = NodeVersionEnv)]
222	pub NodeVersion:Option<String>,
223
224	/// The build command and its arguments to execute.
225	///
226	/// This field accepts all remaining command-line arguments as the build
227	/// command to execute after configuring the project files. This field
228	/// is required and must be provided as the final arguments.
229	///
230	/// Example: `pnpm tauri build`
231	#[clap(required = true, last = true)]
232	pub Command:Vec<String>,
233}
234
235//=============================================================================
236// Guard Definition (RAII Pattern)
237//=============================================================================
238
239/// Manages the backup and restoration of a single file using the RAII pattern.
240///
241/// This struct ensures that an original file is restored to its initial state
242/// when the Guard goes out of scope, providing a safe way to temporarily
243/// modify configuration files during the build process.
244///
245/// # RAII Pattern
246///
247/// The Guard implements the RAII (Resource Acquisition Is Initialization)
248/// pattern:
249/// - **Acquisition**: When created, it creates a backup of the original file
250/// - **Release**: When dropped, it restores the original file from the backup
251///
252/// This ensures that files are restored even if:
253/// - The function returns early
254/// - An error occurs and propagates up
255/// - A panic occurs
256///
257/// # Backup Naming
258///
259/// Backup files are created by appending a suffix to the original file
260/// extension:
261/// - `Cargo.toml` → `Cargo.toml.Backup`
262/// - `tauri.conf.json` → `tauri.conf.json.Backup`
263///
264/// # Error Handling
265///
266/// The Guard constructor returns an error if:
267/// - A backup file already exists at the target location
268/// - The original file cannot be copied (IO error)
269///
270/// This prevents accidental overwriting of existing backups and ensures
271/// data safety.
272///
273/// # Fields
274///
275/// - **Path**: The path to the original file
276/// - **Store**: The path to the backup file
277/// - **Active**: Whether a backup was created
278/// - **Note**: A descriptive note for logging purposes
279pub struct Guard {
280	/// The path to the original file that will be modified.
281	Path:PathBuf,
282
283	/// The path to the backup file created by this guard.
284	Store:PathBuf,
285
286	/// Whether a backup was actually created
287	/// (false if the original file didn't exist).
288	Active:bool,
289
290	/// Whether the guard is armed to restore on drop.
291	/// When true, the file will be restored on drop.
292	/// When false (disarmed), the modified file is preserved.
293	Armed:bool,
294
295	/// A descriptive note for logging and debugging purposes.
296	#[allow(dead_code)]
297	Note:String,
298}
299
300impl Guard {
301	/// Creates a new Guard that backs up the specified file.
302	///
303	/// This constructor creates a backup of the original file if it exists,
304	/// and prepares to restore it when the Guard is dropped. The backup
305	/// file is created with a special suffix to prevent accidental conflicts.
306	///
307	/// # Parameters
308	///
309	/// * `OriginalPath` - The path to the file to be backed up
310	/// * `Description` - A descriptive note for logging purposes
311	///
312	/// # Returns
313	///
314	/// Returns a `Result` containing the Guard or a `BuildError` if:
315	/// - A backup file already exists
316	/// - The file cannot be copied
317	///
318	/// # Errors
319	///
320	/// * `BuildError::Exists` - If a backup file already exists
321	/// * `BuildError::Io` - If the file copy operation fails
322	///
323	/// # Example
324	///
325	/// ```no_run
326	/// use crate::Maintain::Source::Build::Definition;
327	/// let path = PathBuf::from("Cargo.toml");
328	/// let guard = Guard::New(path, "Cargo manifest".to_string())?;
329	/// ```
330	///
331	/// # Safety
332	///
333	/// The Guard ensures that the original file is restored even in the
334	/// presence of panics, providing exception safety.
335	pub fn New(OriginalPath:PathBuf, Description:String) -> Result<Self, crate::Build::Error::Error> {
336		let BackupPath = OriginalPath.with_extension(format!(
337			"{}{}",
338			OriginalPath.extension().unwrap_or_default().to_str().unwrap_or(""),
339			BackupSuffix
340		));
341
342		if BackupPath.exists() {
343			error!("Backup file {} already exists.", BackupPath.display());
344
345			return Err(crate::Build::Error::Error::Exists(BackupPath));
346		}
347
348		let mut BackupMade = false;
349
350		if OriginalPath.exists() {
351			fs::copy(&OriginalPath, &BackupPath)?;
352
353			info!(
354				target: "Build::Guard",
355				"Backed {} to {}",
356				OriginalPath.display(),
357				BackupPath.display()
358			);
359
360			BackupMade = true;
361		}
362
363		Ok(Self {
364			Path:OriginalPath,
365			Store:BackupPath,
366			Active:BackupMade,
367			Armed:true,
368			Note:Description,
369		})
370	}
371
372	/// Returns a reference to the original file path.
373	///
374	/// This method provides read access to the path of the original file
375	/// that this guard is protecting.
376	///
377	/// # Returns
378	///
379	/// A reference to the original file's `Path`.
380	///
381	/// # Example
382	///
383	/// ```no_run
384	/// use crate::Maintain::Source::Build::Definition;
385	/// let guard = Guard::New(original_path, description.to_string())?;
386	/// println!("Original file: {}", guard.Path().display());
387	/// ```
388	pub fn Path(&self) -> &Path { &self.Path }
389
390	/// Returns a reference to the backup file path.
391	///
392	/// This method provides read access to the path where the backup
393	/// file is stored.
394	///
395	/// # Returns
396	///
397	/// A reference to the backup file's `Path`.
398	///
399	/// # Example
400	///
401	/// ```no_run
402	/// use crate::Maintain::Source::Build::Definition;
403	/// let guard = Guard::New(original_path, description.to_string())?;
404	/// println!("Backup file: {}", guard.Store().display());
405	/// ```
406	pub fn Store(&self) -> &Path { &self.Store }
407
408	/// Disarms the guard, preventing restoration of the original file,
409	/// and deletes the backup so the next build is not blocked.
410	pub fn disarm(&mut self) {
411		self.Armed = false;
412		if self.Active && self.Store.exists() {
413			let _ = fs::remove_file(&self.Store);
414		}
415	}
416}
417
418/// Drop implementation that automatically restores the original file.
419///
420/// This is the core of the RAII pattern: when the Guard goes out of scope,
421/// it automatically restores the original file from the backup. This ensures
422/// that file modifications are temporary and that the system is left in a
423/// consistent state even if an error occurs.
424///
425/// # Behavior
426///
427/// - If no backup was created (original file didn't exist), does nothing
428/// - If backup exists, copies it back to the original location
429/// - After successful restore, deletes the backup file
430/// - Logs success or failure of the restoration process
431///
432/// # Panics
433///
434/// This implementation handles panics internally and does not propagate them.
435/// If the restoration fails, it logs an error but does not panic, ensuring
436/// that cleanup failures don't cause secondary failures.
437impl Drop for Guard {
438	fn drop(&mut self) {
439		// Only restore if armed (build failed) and backup is active
440		if self.Armed && self.Active && self.Store.exists() {
441			info!(
442				target: "Build::Guard",
443				"Restoring {} from {}...",
444				self.Path.display(),
445				self.Store.display()
446			);
447
448			if let Ok(_) = fs::copy(&self.Store, &self.Path) {
449				info!(target: "Build::Guard", "Restore successful.");
450
451				if let Err(e) = fs::remove_file(&self.Store) {
452					error!(
453						target: "Build::Guard",
454						"Failed to delete backup {}: {}",
455						self.Store.display(),
456						e
457					);
458				}
459			} else if let Err(e) = fs::copy(&self.Store, &self.Path) {
460				error!(
461					target: "Build::Guard",
462					"Restore FAILED: {}. {} is now inconsistent.",
463					e,
464					self.Path.display()
465				);
466			}
467		}
468	}
469}
470
471//=============================================================================
472// Manifest Definition
473//=============================================================================
474
475/// Represents the `package` section of a `Cargo.toml` manifest.
476///
477/// This struct contains metadata extracted from the `[package]` section
478/// of a Cargo.toml file. It is used for deserializing TOML content using
479/// the `toml` crate's deserialization functionality.
480///
481/// Currently, this struct only extracts the version information, which
482/// is needed for updating the Tauri configuration file with the correct
483/// version during the build process.
484///
485/// # TOML Format
486///
487/// The expected TOML structure:
488/// ```toml
489/// [package]
490/// name = "Mountain"
491/// version = "1.0.0"
492/// # ... other fields
493/// ```
494///
495/// # Fields
496///
497/// - **package**: Contains the metadata extracted from the package section
498#[derive(Deserialize, Debug)]
499pub struct Manifest {
500	/// Represents metadata within the `package` section of `Cargo.toml`.
501	///
502	/// This nested struct contains the individual metadata fields from the
503	/// package section, including the version string.
504	package:Meta,
505}
506
507impl Manifest {
508	/// Retrieves the version string from the manifest.
509	///
510	/// This method provides access to the version field, which is stored
511	/// privately. The version is used when updating the Tauri configuration
512	/// file during the build process.
513	///
514	/// # Returns
515	///
516	/// A string slice containing the version number.
517	///
518	/// # Example
519	///
520	/// ```no_run
521	/// use crate::Maintain::Source::Build::Definition;
522	/// let version = manifest.get_version();
523	/// println!("Application version: {}", version);
524	/// ```
525	pub fn get_version(&self) -> &str { &self.package.version }
526}
527
528/// Represents metadata within the `package` section of `Cargo.toml`.
529///
530/// This struct contains individual metadata fields from the package section.
531/// Currently, only the version field is stored, but additional fields can
532/// be added as needed (e.g., name, description, authors, etc.).
533///
534/// All fields are private and should be accessed through methods on the
535/// parent `Manifest` struct.
536#[derive(Deserialize, Debug)]
537struct Meta {
538	/// The version string from the package metadata.
539	///
540	/// This field contains the version identifier as specified in the
541	/// Cargo.toml file (e.g., "1.0.0", "2.3.4-beta.1").
542	version:String,
543}