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 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 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}