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