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