1use crate::core::find_repo_root;
2use crate::network::{AirgapConfig, AirgapValidator, NetworkValidator};
3use crate::response::{RemoteEntry, RemoteResponse};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fs;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9struct Remote {
10 url: String,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14struct RemoteConfig {
15 remotes: HashMap<String, Remote>,
16}
17
18impl RemoteConfig {
19 fn load(repo_path: &std::path::Path) -> Result<Self, crate::errors::LitError> {
20 let config_path = repo_path.join(".lit").join("remotes");
21
22 if !config_path.exists() {
23 return Ok(RemoteConfig {
24 remotes: HashMap::new(),
25 });
26 }
27
28 let content = fs::read_to_string(&config_path)
29 .map_err(|e| format!("Failed to read remotes config: {}", e))?;
30
31 serde_json::from_str(&content)
32 .map_err(|e| format!("Failed to parse remotes config: {}", e).into())
33 }
34
35 fn save(&self, repo_path: &std::path::Path) -> Result<(), crate::errors::LitError> {
36 let config_path = repo_path.join(".lit").join("remotes");
37
38 let content = serde_json::to_string_pretty(self)
39 .map_err(|e| format!("Failed to serialize remotes config: {}", e))?;
40
41 fs::write(&config_path, content)
42 .map_err(|e| format!("Failed to write remotes config: {}", e).into())
43 }
44}
45
46pub fn execute(
47 command: Option<crate::RemoteCommands>,
48) -> Result<RemoteResponse, crate::errors::LitError> {
49 let repo_root = find_repo_root()?;
50
51 match command {
52 Some(crate::RemoteCommands::Add { name, url }) => {
53 if AirgapConfig::is_airgap_mode() {
55 let validator = AirgapValidator::new()?;
56 validator.validate_transport(&url)?;
57 } else if url.starts_with("lit://") {
58 let validator = NetworkValidator::new()?;
59 validator.validate_url(&url)?;
60 }
61
62 let mut config = RemoteConfig::load(&repo_root)?;
63 if config.remotes.contains_key(&name) {
64 return Err(format!("Remote '{}' already exists", name).into());
65 }
66 config
67 .remotes
68 .insert(name.clone(), Remote { url: url.clone() });
69 config.save(&repo_root)?;
70 Ok(RemoteResponse::Add { name, url })
71 }
72 Some(crate::RemoteCommands::Remove { name }) => {
73 let mut config = RemoteConfig::load(&repo_root)?;
74 if config.remotes.remove(&name).is_none() {
75 return Err(format!("Remote '{}' not found", name).into());
76 }
77 config.save(&repo_root)?;
78 Ok(RemoteResponse::Remove { name })
79 }
80 Some(crate::RemoteCommands::List { verbose: _ }) | None => {
81 let config = RemoteConfig::load(&repo_root)?;
82 let remotes = config
83 .remotes
84 .into_iter()
85 .map(|(name, remote)| RemoteEntry {
86 name,
87 url: remote.url,
88 })
89 .collect();
90 Ok(RemoteResponse::List { remotes })
91 }
92 }
93}