Skip to main content

lit/commands/
remote.rs

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            // Check if airgap mode is enabled
54            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}