Skip to main content

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}