clone_lib/
lib.rs

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