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
197 workbench:arg.Workbench.clone(),
198
199 env_vars,
200
201 hot_reload:arg.HotReload,
202
203 watch:arg.Watch,
204
205 live_reload_port:arg.LiveReloadPort,
206
207 command:arg.Command.clone(),
208
209 working_dir:arg.Directory.clone(),
210 }
211 }
212
213 /// Checks if this is a debug run.
214 pub fn is_debug(&self) -> bool { self.env_vars.get(DebugEnv).map(|s| s == "true").unwrap_or(false) }
215
216 /// Gets the workbench type from environment or config.
217 pub fn get_workbench(&self) -> Option<String> {
218 self.workbench.clone().or_else(|| {
219 if self.env_vars.get(BrowserEnv).map(|s| s == "true").unwrap_or(false) {
220 Some("Browser".to_string())
221 } else if self.env_vars.get(WindEnv).map(|s| s == "true").unwrap_or(false) {
222 Some("Wind".to_string())
223 } else if self.env_vars.get(MountainEnv).map(|s| s == "true").unwrap_or(false) {
224 Some("Mountain".to_string())
225 } else if self.env_vars.get(ElectronEnv).map(|s| s == "true").unwrap_or(false) {
226 Some("Electron".to_string())
227 } else {
228 None
229 }
230 })
231 }
232}
233
234//=============================================================================
235// Profile Definition
236//=============================================================================
237
238/// Represents a run profile from the configuration.
239///
240/// This struct contains the configuration for a specific run profile,
241/// including workbench settings, environment variables, and run options.
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct Profile {
244 /// Profile name identifier.
245 pub name:String,
246
247 /// Human-readable description of the profile.
248 #[serde(default)]
249 pub description:Option<String>,
250
251 /// Workbench type for this profile.
252 #[serde(default)]
253 pub workbench:Option<String>,
254
255 /// Environment variables specific to this profile.
256 #[serde(default)]
257 pub env:Option<HashMap<String, String>>,
258
259 /// Run-specific configuration.
260 #[serde(default)]
261 pub run_config:Option<RunProfileConfig>,
262}
263
264/// Run-specific profile configuration.
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct RunProfileConfig {
267 /// Enable hot-reload.
268 #[serde(default = "default_true")]
269 pub hot_reload:bool,
270
271 /// Enable watch mode.
272 #[serde(default = "default_true")]
273 pub watch:bool,
274
275 /// Live-reload port.
276 #[serde(default = "default_reload_port")]
277 pub live_reload_port:u16,
278
279 /// Additional features enabled for this profile.
280 #[serde(default)]
281 pub features:Option<HashMap<String, bool>>,
282}
283
284fn default_true() -> bool { true }
285
286fn default_reload_port() -> u16 { DefaultLiveReloadPort }
287
288/// Parse a key=value pair from command line.
289fn parse_key_val<K, V>(s:&str) -> Result<(K, V), String>
290where
291 K: std::str::FromStr,
292 V: std::str::FromStr,
293 K::Err: std::fmt::Display,
294 V::Err: std::fmt::Display, {
295 let pos = s.find('=').ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
296
297 Ok((
298 s[..pos].parse().map_err(|e| format!("key parse error: {e}"))?,
299 s[pos + 1..].parse().map_err(|e| format!("value parse error: {e}"))?,
300 ))
301}