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 /// Flag indicating the Mountain workbench profile (debug-mountain).
200 /// Mutually exclusive with `Electron` and `Browser`; when set to "true"
201 /// the product name gains a `_Mountain` suffix so Cargo/Tauri emit a
202 /// distinct binary path that doesn't collide with other workbenches.
203 #[clap(long, env = MountainEnv)]
204 pub Mountain:Option<String>,
205
206 /// Flag indicating the Electron workbench profile (debug-electron).
207 /// Mutually exclusive with `Mountain` and `Browser`; adds a
208 /// `_Electron` suffix to the generated product name + identifier.
209 #[clap(long, env = ElectronEnv)]
210 pub Electron:Option<String>,
211
212 /// Compiler variant (e.g. "Rest"). When present the product name gains
213 /// a `_Compiler<Name>` suffix so variant compilers don't collide with
214 /// the default TypeScript/tsc path.
215 #[clap(long, env = CompilerEnv)]
216 pub Compiler:Option<String>,
217
218 /// Comma-joined Cargo features derived from `.env.Land` tier selections
219 /// by `Maintain/Script/TierEnvironment.sh`. When present and the build
220 /// command is `pnpm tauri build [--debug]`, these are forwarded to Cargo
221 /// via `-- --features "<list>"` so tier-gated Rust code compiles in.
222 /// Example: "TierRemoteProcedureCallSharedMemory,TierLoggerRing".
223 #[clap(long, env = CargoFeaturesEnv)]
224 pub CargoFeatures:Option<String>,
225
226 /// JSON blob of `__LandTier_<Capability>__` replacement tokens produced
227 /// by `Maintain/Script/TierEnvironment.sh`. Cocoon's `TargetConfig.ts`
228 /// merges it into its esbuild `define` options so tier constants compile
229 /// into the Cocoon bundle. Maintain only needs to pass it through to
230 /// the child process environment - no parsing happens here.
231 #[clap(long, env = CocoonEsbuildDefineEnv)]
232 pub CocoonEsbuildDefine:Option<String>,
233
234 /// Information about a dependency, often 'org/repo' or a boolean string.
235 ///
236 /// This field specifies dependency information that affects the generated
237 /// product name and bundle identifier. Can be:
238 /// - "true" for generic dependencies
239 /// - "org/repo" for specific repository dependencies
240 /// - Any custom string
241 #[clap(long, env = DependencyEnv)]
242 pub Dependency:Option<String>,
243
244 /// The Node.js environment (e.g., "development", "production").
245 ///
246 /// This field specifies the Node.js runtime environment and affects the
247 /// generated product name and bundle identifier.
248 #[clap(long, env = NodeEnv)]
249 pub Environment:Option<String>,
250
251 /// Specifies the Node.js sidecar version to bundle (e.g., "22").
252 ///
253 /// This field specifies which Node.js version should be bundled as a
254 /// sidecar binary with the application. The executable is sourced from
255 /// the Element/SideCar directory.
256 #[clap(long, env = NodeVersionEnv)]
257 pub NodeVersion:Option<String>,
258
259 /// The build command and its arguments to execute.
260 ///
261 /// This field accepts all remaining command-line arguments as the build
262 /// command to execute after configuring the project files. This field
263 /// is required and must be provided as the final arguments.
264 ///
265 /// Example: `pnpm tauri build`
266 #[clap(required = true, last = true)]
267 pub Command:Vec<String>,
268}
269
270//=============================================================================
271// Guard Definition (RAII Pattern)
272//=============================================================================
273
274/// Manages the backup and restoration of a single file using the RAII pattern.
275///
276/// This struct ensures that an original file is restored to its initial state
277/// when the Guard goes out of scope, providing a safe way to temporarily
278/// modify configuration files during the build process.
279///
280/// # RAII Pattern
281///
282/// The Guard implements the RAII (Resource Acquisition Is Initialization)
283/// pattern:
284/// - **Acquisition**: When created, it creates a backup of the original file
285/// - **Release**: When dropped, it restores the original file from the backup
286///
287/// This ensures that files are restored even if:
288/// - The function returns early
289/// - An error occurs and propagates up
290/// - A panic occurs
291///
292/// # Backup Naming
293///
294/// Backup files are created by appending a suffix to the original file
295/// extension:
296/// - `Cargo.toml` → `Cargo.toml.Backup`
297/// - `tauri.conf.json` → `tauri.conf.json.Backup`
298///
299/// # Error Handling
300///
301/// The Guard constructor returns an error if:
302/// - A backup file already exists at the target location
303/// - The original file cannot be copied (IO error)
304///
305/// This prevents accidental overwriting of existing backups and ensures
306/// data safety.
307///
308/// # Fields
309///
310/// - **Path**: The path to the original file
311/// - **Store**: The path to the backup file
312/// - **Active**: Whether a backup was created
313/// - **Note**: A descriptive note for logging purposes
314pub struct Guard {
315 /// The path to the original file that will be modified.
316 Path:PathBuf,
317
318 /// The path to the backup file created by this guard.
319 Store:PathBuf,
320
321 /// Whether a backup was actually created
322 /// (false if the original file didn't exist).
323 Active:bool,
324
325 /// Whether the guard is armed to restore on drop.
326 /// When true, the file will be restored on drop.
327 /// When false (disarmed), the modified file is preserved.
328 Armed:bool,
329
330 /// A descriptive note for logging and debugging purposes.
331 Note:String,
332}
333
334impl Guard {
335 /// Creates a new Guard that backs up the specified file.
336 ///
337 /// This constructor creates a backup of the original file if it exists,
338 /// and prepares to restore it when the Guard is dropped. The backup
339 /// file is created with a special suffix to prevent accidental conflicts.
340 ///
341 /// # Parameters
342 ///
343 /// * `OriginalPath` - The path to the file to be backed up
344 /// * `Description` - A descriptive note for logging purposes
345 ///
346 /// # Returns
347 ///
348 /// Returns a `Result` containing the Guard or a `BuildError` if:
349 /// - A backup file already exists
350 /// - The file cannot be copied
351 ///
352 /// # Errors
353 ///
354 /// * `BuildError::Exists` - If a backup file already exists
355 /// * `BuildError::Io` - If the file copy operation fails
356 ///
357 /// # Example
358 ///
359 /// ```no_run
360 /// use crate::Maintain::Source::Build::Definition;
361 /// let path = PathBuf::from("Cargo.toml");
362 /// let guard = Guard::New(path, "Cargo manifest".to_string())?;
363 /// ```
364 ///
365 /// # Safety
366 ///
367 /// The Guard ensures that the original file is restored even in the
368 /// presence of panics, providing exception safety.
369 pub fn New(OriginalPath:PathBuf, Description:String) -> Result<Self, crate::Build::Error::Error> {
370 let BackupPath = OriginalPath.with_extension(format!(
371 "{}{}",
372 OriginalPath.extension().unwrap_or_default().to_str().unwrap_or(""),
373 BackupSuffix
374 ));
375
376 if BackupPath.exists() {
377 error!("Backup file {} already exists.", BackupPath.display());
378
379 return Err(crate::Build::Error::Error::Exists(BackupPath));
380 }
381
382 let mut BackupMade = false;
383
384 if OriginalPath.exists() {
385 fs::copy(&OriginalPath, &BackupPath)?;
386
387 info!(
388 target: "Build::Guard",
389
390 "Backed {} to {}",
391
392 OriginalPath.display(),
393
394 BackupPath.display()
395 );
396
397 BackupMade = true;
398 }
399
400 Ok(Self {
401 Path:OriginalPath,
402 Store:BackupPath,
403 Active:BackupMade,
404 Armed:true,
405 Note:Description,
406 })
407 }
408
409 /// Returns a reference to the original file path.
410 ///
411 /// This method provides read access to the path of the original file
412 /// that this guard is protecting.
413 ///
414 /// # Returns
415 ///
416 /// A reference to the original file's `Path`.
417 ///
418 /// # Example
419 ///
420 /// ```no_run
421 /// use crate::Maintain::Source::Build::Definition;
422 /// let guard = Guard::New(original_path, description.to_string())?;
423 /// println!("Original file: {}", guard.Path().display());
424 /// ```
425 pub fn Path(&self) -> &Path { &self.Path }
426
427 /// Returns a reference to the backup file path.
428 ///
429 /// This method provides read access to the path where the backup
430 /// file is stored.
431 ///
432 /// # Returns
433 ///
434 /// A reference to the backup file's `Path`.
435 ///
436 /// # Example
437 ///
438 /// ```no_run
439 /// use crate::Maintain::Source::Build::Definition;
440 /// let guard = Guard::New(original_path, description.to_string())?;
441 /// println!("Backup file: {}", guard.Store().display());
442 /// ```
443 pub fn Store(&self) -> &Path { &self.Store }
444
445 /// Disarms the guard, preventing restoration of the original file,
446 /// and deletes the backup so the next build is not blocked.
447 pub fn disarm(&mut self) {
448 self.Armed = false;
449
450 if self.Active && self.Store.exists() {
451 let _ = fs::remove_file(&self.Store);
452 }
453 }
454}
455
456/// Drop implementation that automatically restores the original file.
457///
458/// This is the core of the RAII pattern: when the Guard goes out of scope,
459/// it automatically restores the original file from the backup. This ensures
460/// that file modifications are temporary and that the system is left in a
461/// consistent state even if an error occurs.
462///
463/// # Behavior
464///
465/// - If no backup was created (original file didn't exist), does nothing
466/// - If backup exists, copies it back to the original location
467/// - After successful restore, deletes the backup file
468/// - Logs success or failure of the restoration process
469///
470/// # Panics
471///
472/// This implementation handles panics internally and does not propagate them.
473/// If the restoration fails, it logs an error but does not panic, ensuring
474/// that cleanup failures don't cause secondary failures.
475impl Drop for Guard {
476 fn drop(&mut self) {
477 // Only restore if armed (build failed) and backup is active
478 if self.Armed && self.Active && self.Store.exists() {
479 info!(
480 target: "Build::Guard",
481
482 "Restoring {} from {}...",
483
484 self.Path.display(),
485
486 self.Store.display()
487 );
488
489 if let Ok(_) = fs::copy(&self.Store, &self.Path) {
490 info!(target: "Build::Guard", "Restore successful.");
491
492 if let Err(e) = fs::remove_file(&self.Store) {
493 error!(
494 target: "Build::Guard",
495
496 "Failed to delete backup {}: {}",
497
498 self.Store.display(),
499
500 e
501 );
502 }
503 } else if let Err(e) = fs::copy(&self.Store, &self.Path) {
504 error!(
505 target: "Build::Guard",
506
507 "Restore FAILED: {}. {} is now inconsistent.",
508
509 e,
510
511 self.Path.display()
512 );
513 }
514 }
515 }
516}
517
518//=============================================================================
519// Manifest Definition
520//=============================================================================
521
522/// Represents the `package` section of a `Cargo.toml` manifest.
523///
524/// This struct contains metadata extracted from the `[package]` section
525/// of a Cargo.toml file. It is used for deserializing TOML content using
526/// the `toml` crate's deserialization functionality.
527///
528/// Currently, this struct only extracts the version information, which
529/// is needed for updating the Tauri configuration file with the correct
530/// version during the build process.
531///
532/// # TOML Format
533///
534/// The expected TOML structure:
535/// ```toml
536/// [package]
537/// name = "Mountain"
538/// version = "1.0.0"
539/// # ... other fields
540/// ```
541///
542/// # Fields
543///
544/// - **package**: Contains the metadata extracted from the package section
545#[derive(Deserialize, Debug)]
546pub struct Manifest {
547 /// Represents metadata within the `package` section of `Cargo.toml`.
548 ///
549 /// This nested struct contains the individual metadata fields from the
550 /// package section, including the version string.
551 package:Meta,
552}
553
554impl Manifest {
555 /// Retrieves the version string from the manifest.
556 ///
557 /// This method provides access to the version field, which is stored
558 /// privately. The version is used when updating the Tauri configuration
559 /// file during the build process.
560 ///
561 /// # Returns
562 ///
563 /// A string slice containing the version number.
564 ///
565 /// # Example
566 ///
567 /// ```no_run
568 /// use crate::Maintain::Source::Build::Definition;
569 /// let version = manifest.get_version();
570 /// println!("Application version: {}", version);
571 /// ```
572 pub fn get_version(&self) -> &str { &self.package.version }
573}
574
575/// Represents metadata within the `package` section of `Cargo.toml`.
576///
577/// This struct contains individual metadata fields from the package section.
578/// Currently, only the version field is stored, but additional fields can
579/// be added as needed (e.g., name, description, authors, etc.).
580///
581/// All fields are private and should be accessed through methods on the
582/// parent `Manifest` struct.
583#[derive(Deserialize, Debug)]
584struct Meta {
585 /// The version string from the package metadata.
586 ///
587 /// This field contains the version identifier as specified in the
588 /// Cargo.toml file (e.g., "1.0.0", "2.3.4-beta.1").
589 version:String,
590}