robin/config/
robin_config.rs1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct RobinConfig {
10 #[serde(default)]
11 pub include: Vec<String>,
12 pub scripts: HashMap<String, Value>,
13}
14
15impl RobinConfig {
16 pub fn load(path: &Path) -> Result<Self> {
17 if !path.exists() {
18 return Err(anyhow::anyhow!(
19 "No .robin.json found. Run 'robin init' first"
20 ));
21 }
22
23 let content = fs::read_to_string(path)
24 .with_context(|| format!("Failed to read config file: {}", path.display()))?;
25
26 let mut config: Self = serde_json::from_str(&content)
27 .with_context(|| "The .robin.json file exists but contains malformed JSON. Please check the file format.")?;
28
29 if !config.include.is_empty() {
31 let base_dir = path.parent().unwrap_or_else(|| Path::new("."));
32 config = config.merge_includes(base_dir)?;
33 }
34
35 Ok(config)
36 }
37
38 fn merge_includes(&self, base_dir: &Path) -> Result<Self> {
39 let mut merged_scripts = self.scripts.clone();
40
41 for include_path in &self.include {
42 let full_path = base_dir.join(include_path);
43 let included_config = Self::load(&full_path)
44 .with_context(|| format!("Failed to load included config: {}", include_path))?;
45
46 for (key, value) in included_config.scripts {
48 merged_scripts.entry(key).or_insert(value);
49 }
50 }
51
52 Ok(Self {
53 include: self.include.clone(),
54 scripts: merged_scripts,
55 })
56 }
57
58 pub fn save(&self, path: &Path) -> Result<()> {
59 let content =
60 serde_json::to_string_pretty(self).with_context(|| "Failed to serialize config")?;
61
62 fs::write(path, content)
63 .with_context(|| format!("Failed to write config to: {}", path.display()))?;
64
65 Ok(())
66 }
67
68 pub fn create_template() -> Self {
69 let mut scripts = HashMap::new();
70 scripts.insert("clean".to_string(), Value::String("...".to_string()));
71 scripts.insert(
72 "deploy staging".to_string(),
73 Value::String("echo 'ruby deploy tool --staging'".to_string()),
74 );
75 scripts.insert(
76 "deploy production".to_string(),
77 Value::String("...".to_string()),
78 );
79 scripts.insert("release beta".to_string(), Value::String("...".to_string()));
80 scripts.insert(
81 "release alpha".to_string(),
82 Value::String("...".to_string()),
83 );
84 scripts.insert("release dev".to_string(), Value::String("...".to_string()));
85
86 Self {
87 include: Vec::new(),
88 scripts,
89 }
90 }
91}