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}