Maintain/Run/
Definition.rs

1//=============================================================================//
2// File Path: Element/Maintain/Source/Run/Definition.rs
3//=============================================================================//
4// Module: Definition
5//
6// Brief Description: Run module type definitions and data structures.
7//
8// RESPONSIBILITIES:
9// ================
10//
11// Primary:
12// - Define argument parsing structures for run operations
13// - Define run configuration structures
14// - Define profile data structures
15//
16// Secondary:
17// - Provide type-safe access to run 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
28// - Internal modules: Constant
29// - Traits implemented: Parser, Clone, Debug
30//
31// Dependents (What depends on this module):
32// - Run orchestration functions
33// - Entry point functions
34//
35//=============================================================================//
36// IMPLEMENTATION
37//=============================================================================//
38
39use crate::Run::Constant::*;
40
41use clap::Parser;
42use serde::{Deserialize, Serialize};
43use std::collections::HashMap;
44use std::path::PathBuf;
45
46//=============================================================================
47// Argument Definition
48//=============================================================================
49
50/// Represents parsed command-line arguments and environment variables that
51/// control the development run process.
52///
53/// This struct is generated by `clap` from the `Parser` derive macro and
54/// automatically parses command-line arguments and environment variables into
55/// a strongly-typed configuration object.
56#[derive(Parser, Debug, Clone)]
57#[clap(
58    author,
59    version,
60    about = "Development run orchestrator with hot-reload support."
61)]
62pub struct Argument {
63    /// The working directory for the run process.
64    ///
65    /// This field specifies the directory where the development server
66    /// will be started. It can be set via:
67    /// - Command-line: `--directory <path>`
68    /// - Environment: `RUN_DIR`
69    /// - Default: "."
70    #[clap(long, env = DirEnv, default_value = DirectoryDefault)]
71    pub Directory: PathBuf,
72
73    /// The build profile to use for the run.
74    ///
75    /// This field specifies which profile from the configuration to use.
76    /// It can be set via:
77    /// - Command-line: `--profile <name>`
78    /// - Environment: `RUN_PROFILE`
79    /// - Default: "debug"
80    #[clap(long, short = 'p', env = ProfileEnv, default_value = ProfileDefault)]
81    pub Profile: String,
82
83    /// Enable hot-reload for development.
84    ///
85    /// When enabled, automatically reloads the application when source
86    /// files change.
87    #[clap(long, env = HotReloadEnv, default_value = "true")]
88    pub HotReload: bool,
89
90    /// Enable watch mode for file changes.
91    ///
92    /// When enabled, watches for file changes and triggers rebuilds.
93    #[clap(long, env = WatchEnv, default_value = "true")]
94    pub Watch: bool,
95
96    /// Port for live-reload server.
97    ///
98    /// Specifies the port used by the live-reload server.
99    #[clap(long, env = LiveReloadPortEnv, default_value = "3001")]
100    pub LiveReloadPort: u16,
101
102    /// Override workbench type.
103    ///
104    /// Specifies which workbench to use (Browser, Wind, Mountain, Electron).
105    #[clap(long, short = 'w', env = WorkbenchEnv)]
106    pub Workbench: Option<String>,
107
108    /// Enable debug mode.
109    ///
110    /// When enabled, runs in debug mode with additional logging.
111    #[clap(long, env = DebugEnv)]
112    pub Debug: Option<String>,
113
114    /// Log level for the run process.
115    #[clap(long, short = 'l', env = LevelEnv)]
116    pub Level: Option<String>,
117
118    /// Node.js environment (development, production).
119    #[clap(long, env = NodeEnv)]
120    pub NodeEnvironment: Option<String>,
121
122    /// Node.js version to use.
123    #[clap(long, env = NodeVersionEnv)]
124    pub NodeVersion: Option<String>,
125
126    /// Dependency source.
127    #[clap(long, env = DependencyEnv)]
128    pub Dependency: Option<String>,
129    
130    /// Override environment variables (key=value pairs).
131    #[clap(long = "env", value_parser = parse_key_val::<String, String>, action = clap::ArgAction::Append)]
132    pub env_override: Vec<(String, String)>,
133    
134    /// Enable verbose output.
135    #[clap(long, short = 'v')]
136    pub Verbose: bool,
137
138    /// Dry run mode (show configuration without running).
139    #[clap(long)]
140    pub DryRun: bool,
141
142    /// The run command and its arguments to execute.
143    ///
144    /// This field accepts all remaining command-line arguments as the run
145    /// command to execute.
146    #[clap(last = true)]
147    pub Command: Vec<String>,
148}
149
150//=============================================================================
151// RunConfig Definition
152//=============================================================================
153
154/// Represents the resolved run configuration.
155///
156/// This struct contains all the configuration needed to start a
157/// development run session, including environment variables, workbench
158/// settings, and run-specific options.
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct RunConfig {
161    /// The profile name used for this run.
162    pub profile_name: String,
163
164    /// The workbench type (Browser, Wind, Mountain, Electron).
165    pub workbench: Option<String>,
166
167    /// Resolved environment variables.
168    pub env_vars: HashMap<String, String>,
169
170    /// Whether hot-reload is enabled.
171    pub hot_reload: bool,
172
173    /// Whether watch mode is enabled.
174    pub watch: bool,
175
176    /// Port for live-reload server.
177    pub live_reload_port: u16,
178
179    /// The command to execute.
180    pub command: Vec<String>,
181
182    /// Working directory for the run.
183    pub working_dir: PathBuf,
184}
185
186impl RunConfig {
187    /// Creates a new RunConfig from an Argument and resolved environment.
188    ///
189    /// # Arguments
190    ///
191    /// * `arg` - The parsed command-line arguments
192    /// * `env_vars` - Resolved environment variables from profile
193    ///
194    /// # Returns
195    ///
196    /// A new RunConfig instance
197    pub fn new(arg: &Argument, env_vars: HashMap<String, String>) -> Self {
198        Self {
199            profile_name: arg.Profile.clone(),
200            workbench: arg.Workbench.clone(),
201            env_vars,
202            hot_reload: arg.HotReload,
203            watch: arg.Watch,
204            live_reload_port: arg.LiveReloadPort,
205            command: arg.Command.clone(),
206            working_dir: arg.Directory.clone(),
207        }
208    }
209
210    /// Checks if this is a debug run.
211    pub fn is_debug(&self) -> bool {
212        self.env_vars.get(DebugEnv).map(|s| s == "true").unwrap_or(false)
213    }
214
215    /// Gets the workbench type from environment or config.
216    pub fn get_workbench(&self) -> Option<String> {
217        self.workbench.clone().or_else(|| {
218            if self.env_vars.get(BrowserEnv).map(|s| s == "true").unwrap_or(false) {
219                Some("Browser".to_string())
220            } else if self.env_vars.get(WindEnv).map(|s| s == "true").unwrap_or(false) {
221                Some("Wind".to_string())
222            } else if self.env_vars.get(MountainEnv).map(|s| s == "true").unwrap_or(false) {
223                Some("Mountain".to_string())
224            } else if self.env_vars.get(ElectronEnv).map(|s| s == "true").unwrap_or(false) {
225                Some("Electron".to_string())
226            } else {
227                None
228            }
229        })
230    }
231}
232
233//=============================================================================
234// Profile Definition
235//=============================================================================
236
237/// Represents a run profile from the configuration.
238///
239/// This struct contains the configuration for a specific run profile,
240/// including workbench settings, environment variables, and run options.
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct Profile {
243    /// Profile name identifier.
244    pub name: String,
245
246    /// Human-readable description of the profile.
247    #[serde(default)]
248    pub description: Option<String>,
249
250    /// Workbench type for this profile.
251    #[serde(default)]
252    pub workbench: Option<String>,
253
254    /// Environment variables specific to this profile.
255    #[serde(default)]
256    pub env: Option<HashMap<String, String>>,
257
258    /// Run-specific configuration.
259    #[serde(default)]
260    pub run_config: Option<RunProfileConfig>,
261}
262
263/// Run-specific profile configuration.
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct RunProfileConfig {
266    /// Enable hot-reload.
267    #[serde(default = "default_true")]
268    pub hot_reload: bool,
269
270    /// Enable watch mode.
271    #[serde(default = "default_true")]
272    pub watch: bool,
273
274    /// Live-reload port.
275    #[serde(default = "default_reload_port")]
276    pub live_reload_port: u16,
277
278    /// Additional features enabled for this profile.
279    #[serde(default)]
280    pub features: Option<HashMap<String, bool>>,
281}
282
283fn default_true() -> bool {
284true
285}
286
287fn default_reload_port() -> u16 {
288DefaultLiveReloadPort
289}
290
291/// Parse a key=value pair from command line.
292fn parse_key_val<K, V>(s: &str) -> Result<(K, V), String>
293where
294K: std::str::FromStr,
295V: std::str::FromStr,
296K::Err: std::fmt::Display,
297V::Err: std::fmt::Display,
298{
299let pos = s
300.find('=')
301.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
302Ok((
303s[..pos].parse().map_err(|e| format!("key parse error: {e}"))?,
304s[pos + 1..].parse().map_err(|e| format!("value parse error: {e}"))?,
305))
306}