1use std::{collections::HashMap, path::Path};
25
26use serde::Deserialize;
27
28#[derive(Debug, Deserialize, Clone)]
34pub struct LandConfig {
35 pub version:String,
37
38 pub workbench:Option<WorkbenchConfig>,
40
41 pub features:Option<HashMap<String, FeatureConfig>>,
43
44 pub binary:Option<BinaryConfig>,
46
47 pub profiles:HashMap<String, Profile>,
49
50 pub templates:Option<Templates>,
52
53 #[serde(rename = "env_prefixes")]
55 pub env_prefixes:Option<HashMap<String, String>>,
56
57 #[serde(rename = "build_commands")]
59 pub build_commands:Option<HashMap<String, String>>,
60
61 #[serde(rename = "environment_variables")]
63 pub environment_variables:Option<EnvironmentVariableInventory>,
64
65 pub cli:Option<CliConfig>,
67}
68
69#[derive(Debug, Deserialize, Clone)]
71pub struct CliConfig {
72 #[serde(rename = "default_profile")]
74 pub default_profile:Option<String>,
75
76 #[serde(rename = "config_file")]
78 pub config_file:Option<String>,
79
80 #[serde(rename = "log_format")]
82 pub log_format:Option<String>,
83
84 pub colors:Option<bool>,
86
87 pub progress:Option<bool>,
89
90 #[serde(rename = "dry_run_default")]
92 pub dry_run_default:Option<bool>,
93
94 #[serde(rename = "profile_aliases")]
96 pub profile_aliases:HashMap<String, String>,
97}
98
99#[derive(Debug, Deserialize, Clone)]
101pub struct EnvironmentVariableInventory {
102 #[serde(rename = "build_flags")]
104 pub build_flags:Option<HashMap<String, EnvironmentVariableInfo>>,
105
106 #[serde(rename = "build_config")]
108 pub build_config:Option<HashMap<String, EnvironmentVariableInfo>>,
109
110 pub node:Option<HashMap<String, EnvironmentVariableInfo>>,
112
113 pub rust:Option<HashMap<String, EnvironmentVariableInfo>>,
115
116 pub mountain:Option<HashMap<String, EnvironmentVariableInfo>>,
118
119 pub tauri:Option<HashMap<String, EnvironmentVariableInfo>>,
121
122 pub apple:Option<HashMap<String, EnvironmentVariableInfo>>,
124
125 pub android:Option<HashMap<String, EnvironmentVariableInfo>>,
127
128 pub ci:Option<HashMap<String, EnvironmentVariableInfo>>,
130
131 pub api:Option<HashMap<String, EnvironmentVariableInfo>>,
133
134 pub other:Option<HashMap<String, EnvironmentVariableInfo>>,
136}
137
138#[derive(Debug, Deserialize, Clone)]
140pub struct EnvironmentVariableInfo {
141 #[serde(rename = "type")]
143 pub var_type:Option<String>,
144
145 pub description:Option<String>,
147
148 pub values:Option<Vec<String>>,
150
151 pub default:Option<String>,
153
154 #[serde(rename = "config_path")]
156 pub config_path:Option<String>,
157
158 pub sensitive:Option<bool>,
160}
161
162#[derive(Debug, Deserialize, Clone)]
164pub struct WorkbenchConfig {
165 pub default:Option<String>,
167
168 pub available:Option<Vec<String>>,
170
171 pub features:Option<HashMap<String, WorkbenchFeatures>>,
173}
174
175#[derive(Debug, Deserialize, Clone)]
177pub struct WorkbenchFeatures {
178 pub description:Option<String>,
180
181 pub coverage:Option<String>,
183
184 pub complexity:Option<String>,
186
187 pub polyfills:Option<bool>,
189
190 #[serde(rename = "mountain_providers")]
192 pub mountain_providers:Option<bool>,
193
194 #[serde(rename = "wind_services")]
196 pub wind_services:Option<bool>,
197
198 #[serde(rename = "electron_apis")]
200 pub electron_apis:Option<bool>,
201
202 pub recommended:Option<bool>,
204
205 #[serde(rename = "recommended_for")]
207 pub recommended_for:Option<Vec<String>>,
208}
209
210#[derive(Debug, Deserialize, Clone)]
212pub struct FeatureConfig {
213 pub description:Option<String>,
215
216 pub default:Option<bool>,
218
219 #[serde(rename = "depends_on")]
221 pub depends_on:Option<Vec<String>>,
222}
223
224#[derive(Debug, Deserialize, Clone)]
226pub struct BinaryConfig {
227 #[serde(rename = "name_template")]
229 pub name_template:Option<String>,
230
231 #[serde(rename = "identifier_template")]
233 pub identifier_template:Option<String>,
234
235 #[serde(rename = "version_format")]
237 pub version_format:Option<String>,
238
239 pub sign:Option<SignConfig>,
241
242 pub notarize:Option<NotarizeConfig>,
244
245 pub updater:Option<UpdaterConfig>,
247}
248
249#[derive(Debug, Deserialize, Clone)]
251pub struct SignConfig {
252 pub macos:Option<MacOSSignConfig>,
254
255 pub windows:Option<WindowsSignConfig>,
257
258 pub linux:Option<LinuxSignConfig>,
260}
261
262#[derive(Debug, Deserialize, Clone)]
264pub struct MacOSSignConfig {
265 pub identity:Option<String>,
267
268 pub entitlements:Option<String>,
270
271 #[serde(rename = "hardenedRuntime")]
273 pub hardened_runtime:Option<bool>,
274
275 #[serde(rename = "gatekeeper_assess")]
277 pub gatekeeper_assess:Option<bool>,
278}
279
280#[derive(Debug, Deserialize, Clone)]
282pub struct WindowsSignConfig {
283 pub certificate:Option<String>,
285
286 #[serde(rename = "timestamp_server")]
288 pub timestamp_server:Option<String>,
289
290 #[serde(rename = "tsa_can_only_access_urls")]
292 pub tsa_can_only_access_urls:Option<Vec<String>>,
293}
294
295#[derive(Debug, Deserialize, Clone)]
297pub struct LinuxSignConfig {
298 #[serde(rename = "gpg_key")]
300 pub gpg_key:Option<String>,
301
302 #[serde(rename = "gpg_passphrase_env")]
304 pub gpg_passphrase_env:Option<String>,
305}
306
307#[derive(Debug, Deserialize, Clone)]
309pub struct NotarizeConfig {
310 pub macos:Option<MacOSNotarizeConfig>,
312}
313
314#[derive(Debug, Deserialize, Clone)]
316pub struct MacOSNotarizeConfig {
317 #[serde(rename = "apple_id")]
319 pub apple_id:Option<String>,
320
321 #[serde(rename = "password_env")]
323 pub password_env:Option<String>,
324
325 #[serde(rename = "team_id")]
327 pub team_id:Option<String>,
328}
329
330#[derive(Debug, Deserialize, Clone)]
332pub struct UpdaterConfig {
333 pub enabled:Option<bool>,
335
336 pub endpoints:Option<Vec<String>>,
338
339 pub pubkey:Option<String>,
341}
342
343#[derive(Debug, Deserialize, Clone)]
345pub struct Profile {
346 pub description:Option<String>,
348
349 pub workbench:Option<String>,
351
352 pub env:Option<HashMap<String, String>>,
354
355 pub features:Option<HashMap<String, bool>>,
357
358 #[serde(rename = "rhai_script")]
360 pub rhai_script:Option<String>,
361}
362
363#[derive(Debug, Deserialize, Clone)]
365pub struct Templates {
366 pub env:HashMap<String, String>,
368}
369
370pub fn load(workspace_root:&str) -> Result<LandConfig, String> {
392 let config_path = Path::new(workspace_root).join(".vscode").join("land-config.json");
393
394 load_config(&config_path)
395}
396
397pub fn load_config(config_path:&Path) -> Result<LandConfig, String> {
415 if !config_path.exists() {
416 return Err(format!("Configuration file not found: {}", config_path.display()));
417 }
418
419 let content = std::fs::read_to_string(config_path).map_err(|e| format!("Failed to read config file: {}", e))?;
420
421 let config:LandConfig = json5::from_str(&content).map_err(|e| format!("Failed to parse config JSON: {}", e))?;
423
424 Ok(config)
425}
426
427pub fn get_profile<'a>(config:&'a LandConfig, profile_name:&str) -> Option<&'a Profile> {
438 config.profiles.get(profile_name)
439}
440
441pub fn get_workbench_type(config:&LandConfig, profile_name:&str) -> String {
452 if let Some(profile) = config.profiles.get(profile_name) {
453 if let Some(workbench) = &profile.workbench {
454 return workbench.clone();
455 }
456 }
457
458 if let Some(workbench_config) = &config.workbench {
460 if let Some(default) = &workbench_config.default {
461 return default.clone();
462 }
463 }
464
465 "Browser".to_string()
467}
468
469pub fn get_workbench_features<'a>(config:&'a LandConfig, workbench_type:&str) -> Option<&'a WorkbenchFeatures> {
480 if let Some(workbench_config) = &config.workbench {
481 if let Some(features) = &workbench_config.features {
482 return features.get(workbench_type);
483 }
484 }
485
486 None
487}
488
489pub fn resolve_profile_env(config:&LandConfig, profile_name:&str) -> HashMap<String, String> {
503 let mut env_vars = HashMap::new();
504
505 if let Some(templates) = &config.templates {
507 for (key, value) in &templates.env {
508 env_vars.insert(key.clone(), value.clone());
509 }
510 }
511
512 if let Some(profile) = config.profiles.get(profile_name) {
514 if let Some(profile_env) = &profile.env {
515 for (key, value) in profile_env {
516 env_vars.insert(key.clone(), value.clone());
517 }
518 }
519 }
520
521 let workbench_type = get_workbench_type(config, profile_name);
523
524 env_vars.insert(workbench_type.clone(), "true".to_string());
525
526 env_vars
527}
528
529pub fn resolve_profile_features(config:&LandConfig, profile_name:&str) -> HashMap<String, bool> {
542 let mut features = HashMap::new();
543
544 if let Some(feature_config) = &config.features {
546 for (name, config) in feature_config {
547 features.insert(name.clone(), config.default.unwrap_or(false));
548 }
549 }
550
551 if let Some(profile) = config.profiles.get(profile_name) {
553 if let Some(profile_features) = &profile.features {
554 for (key, value) in profile_features {
555 features.insert(key.clone(), *value);
556 }
557 }
558 }
559
560 features
561}
562
563pub fn features_to_env(features:&HashMap<String, bool>) -> HashMap<String, String> {
575 let mut env_vars = HashMap::new();
576
577 for (name, value) in features {
578 let env_key = format!("FEATURE_{}", name.to_uppercase().replace("-", "_"));
579
580 env_vars.insert(env_key, value.to_string());
581 }
582
583 env_vars
584}
585
586pub fn get_build_command(config:&LandConfig, profile_name:&str) -> Option<String> {
597 config.build_commands.as_ref()?.get(profile_name).cloned()
598}
599
600#[cfg(test)]
605mod tests {
606
607 use super::*;
608
609 #[test]
610 fn test_load_config() {
611
612 }
615
616 #[test]
617 fn test_features_to_env() {
618 let mut features = HashMap::new();
619
620 features.insert("tauri_ipc".to_string(), true);
621
622 features.insert("wind_services".to_string(), false);
623
624 let env = features_to_env(&features);
625
626 assert_eq!(env.get("FEATURE_TAURI_IPC"), Some(&"true".to_string()));
627
628 assert_eq!(env.get("FEATURE_WIND_SERVICES"), Some(&"false".to_string()));
629 }
630}