Skip to main content

clone_lib/
lib.rs

1use dirs::config_dir;
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::{Path, PathBuf};
6use std::process::Command;
7
8pub mod projectmanager;
9use crate::projectmanager::{nvim, vscode};
10
11const DEFAULT_REGEX: &str = r"(?m)^(?:https?://|git@)?([^/:]+)[/:]([^/]+)/([^\.]+)(?:\.git)?/?$";
12
13#[derive(Serialize, Deserialize, Debug)]
14#[serde(default)]
15pub struct AppConfig {
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub vscode_path_prefix: Option<String>,
18    pub workspaces_dir: PathBuf,
19    pub nvim_projectmanager_path: PathBuf,
20    pub vscode_projectmanager_path: PathBuf,
21    pub regex: String,
22}
23
24impl AppConfig {
25    pub fn load() -> Self {
26        let config_path = Self::config_path();
27        if config_path.exists() {
28            let config_data = fs::read_to_string(config_path).expect("Unable to read config file");
29            toml::from_str(&config_data).expect("Invalid config file")
30        } else {
31            // Default configuration
32            Self::default()
33        }
34    }
35
36    pub fn save(&self) {
37        let config_path = Self::config_path();
38        println!("Saving configfile to: {:?}", config_path);
39        let config_data = toml::to_string(self).expect("Unable to serialize config");
40        fs::create_dir_all(config_path.parent().unwrap())
41            .expect("Failed to create config directory");
42        fs::write(config_path, config_data).expect("Unable to write config file");
43    }
44
45    fn config_path() -> PathBuf {
46        config_dir().unwrap().join("clone").join("config.toml")
47    }
48}
49
50impl Default for AppConfig {
51    fn default() -> Self {
52        match detect_current_os() {
53            "wsl" => {
54                if let Some(username) = get_wsl_user_name() {
55                    let win_user_home_path = format!("/mnt/c/Users/{}", username);
56                    let wsl_user_home_path =
57                        std::env::var("HOME").expect("'HOME' environment variable must be set.");
58                    let wsl_distro_name = std::env::var("WSL_DISTRO_NAME").expect(
59                        "'WSL_DISTRO_NAME' environment variable must be set in WSL environment.",
60                    );
61
62                    AppConfig {
63                            vscode_path_prefix: Some(format!("vscode-remote://wsl+{}", wsl_distro_name)),
64                            workspaces_dir: format!("{}/workspaces", wsl_user_home_path).into(),
65                            nvim_projectmanager_path: format!("{}/.local/share/nvim/lazy/projectmgr.nvim/projects.json", wsl_user_home_path).into(),
66                            vscode_projectmanager_path: format!("{}/AppData/Roaming/Code/User/globalStorage/alefragnani.project-manager/projects.json", win_user_home_path).into(),
67                            regex: DEFAULT_REGEX.to_string()
68                        }
69                } else {
70                    panic!("Cannot get WSL username in a WSL environment, that must never happen, that mean powershell.exe didn't exist or didn't provide the environment variable $env:USERNAME.");
71                }
72            }
73            "macos" => {
74                let user_home_path =
75                    std::env::var("HOME").expect("'HOME' environment variable must be set.");
76                AppConfig {
77                    vscode_path_prefix: None,
78                    workspaces_dir: format!("{}/workspaces", user_home_path).into(),
79                    nvim_projectmanager_path: format!("{}/.local/share/nvim/lazy/projectmgr.nvim/projects.json", user_home_path).into(),
80                    vscode_projectmanager_path: format!("{}/Library/Application Support/Code/User/globalStorage/alefragnani.project-manager/projects.json", user_home_path).into(),
81                    regex: DEFAULT_REGEX.to_string()
82                }
83            }
84            "windows" => {
85                let user_home_path = std::env::var("USERPROFILE")
86                    .expect("'USERPROFILE' environment variable must be set.");
87                AppConfig {
88                    vscode_path_prefix: None,
89                        workspaces_dir: format!("{}\\workspaces", user_home_path).into(),
90                        nvim_projectmanager_path: format!("{}\\AppData\\Roaming\\nvim\\", user_home_path).into(),
91                        vscode_projectmanager_path: format!("{}\\AppData\\Roaming\\Code\\User\\globalStorage\\alefragnani.project-manager\\projects.json", user_home_path).into(),
92                        regex: DEFAULT_REGEX.to_string()
93                    }
94            }
95            _linux => {
96                let user_home_path =
97                    std::env::var("HOME").expect("'HOME' environment variable must be set.");
98                AppConfig {
99                    vscode_path_prefix: None,
100                    workspaces_dir: format!("{}/workspaces", user_home_path).into(),
101                    nvim_projectmanager_path: format!("{}/.local/share/nvim/lazy/projectmgr.nvim/projects.json", user_home_path).into(),
102                    vscode_projectmanager_path:format!("{}/.config/Code/User/globalStorage/alefragnani.project-manager/projects.json", user_home_path).into(),
103                    regex: DEFAULT_REGEX.to_string()
104                }
105            }
106        }
107    }
108}
109
110pub fn parse_repo_url(url: &str, regex: &str) -> Result<(String, String, String), String> {
111    let parser = Regex::new(regex).map_err(|e| format!("Invalid regex: {}", e))?;
112    if let Some(result) = parser.captures(url) {
113        let host = &result[1];
114        let group = &result[2];
115        let name = &result[3];
116        Ok((host.to_string(), group.to_string(), name.to_string()))
117    } else {
118        Err(format!("{:?} didn't match the regex {:?}", url, regex))
119    }
120}
121
122pub fn clone_repo(url: &str, repo_path: &Path) -> Result<(), String> {
123    if repo_path.exists() {
124        Err(format!("repository already exist at {:?}", repo_path))
125    } else {
126        // Try to create parent directories if needed
127        if let Some(parent) = repo_path.parent() {
128            if let Err(e) = fs::create_dir_all(parent) {
129                return Err(format!("Failed to create parent directories: {}", e));
130            }
131        }
132        let status = Command::new("git")
133            .arg("clone")
134            .arg(url)
135            .arg(repo_path)
136            .status();
137
138        match status {
139            Ok(s) if s.success() => {
140                println!("Cloned into {:?}", repo_path);
141                Ok(())
142            }
143            Ok(s) => Err(format!("git exited with status: {}", s)),
144            Err(e) => Err(format!("Failed to execute git: {}", e)),
145        }
146    }
147}
148
149pub fn detect_current_os() -> &'static str {
150    match std::env::consts::OS {
151        "linux" => match detect_wsl_with_envs() {
152            true => "wsl",
153            false => "linux",
154        },
155        os => os,
156    }
157}
158
159fn _detect_wsl_with_powershell() -> bool {
160    get_wsl_user_name().is_some()
161}
162
163fn get_wsl_user_name() -> Option<String> {
164    if let Ok(output_utf8) = std::process::Command::new("powershell.exe")
165        .arg("-c")
166        .arg("echo $env:USERNAME")
167        .output()
168    {
169        if let Ok(output) = String::from_utf8(output_utf8.stdout) {
170            Some(output.trim().to_string())
171        } else {
172            None
173        }
174    } else {
175        None
176    }
177}
178
179fn detect_wsl_with_envs() -> bool {
180    std::env::var("WSL_DISTRO_NAME").is_ok()
181}
182
183pub fn add_project_to_nvim(
184    target_path: PathBuf,
185    workspace: PathBuf,
186    host: String,
187    group: String,
188    name: String,
189    debug: bool,
190) {
191    nvim::add_project(target_path, workspace, host, group, name, debug)
192}
193pub fn add_project_to_vscode(
194    target_path: PathBuf,
195    workspace: PathBuf,
196    group: String,
197    name: String,
198    debug: bool,
199) {
200    vscode::add_project(target_path, workspace, group, name, debug)
201}