sdkman_cli_native/
lib.rs

1pub mod constants {
2    pub const CANDIDATES_DIR: &str = "candidates";
3    pub const CANDIDATES_FILE: &str = "candidates";
4    pub const CURRENT_DIR: &str = "current";
5    pub const DEFAULT_SDKMAN_HOME: &str = ".sdkman";
6    pub const SDKMAN_DIR_ENV_VAR: &str = "SDKMAN_DIR";
7    pub const TMP_DIR: &str = "tmp";
8    pub const VAR_DIR: &str = "var";
9}
10
11pub mod helpers {
12    use colored::Colorize;
13    use directories::UserDirs;
14    use std::path::PathBuf;
15    use std::{env, fs, process};
16
17    use crate::constants::{
18        CANDIDATES_DIR, CANDIDATES_FILE, DEFAULT_SDKMAN_HOME, SDKMAN_DIR_ENV_VAR, VAR_DIR,
19    };
20
21    pub fn infer_sdkman_dir() -> PathBuf {
22        match env::var(SDKMAN_DIR_ENV_VAR) {
23            Ok(s) => PathBuf::from(s),
24            Err(_) => fallback_sdkman_dir(),
25        }
26    }
27
28    fn fallback_sdkman_dir() -> PathBuf {
29        UserDirs::new()
30            .map(|dir| dir.home_dir().join(DEFAULT_SDKMAN_HOME))
31            .unwrap()
32    }
33
34    pub fn check_file_exists(path: PathBuf) -> PathBuf {
35        if path.exists() && path.is_file() {
36            path
37        } else {
38            panic!("not a valid path: {}", path.to_str().unwrap())
39        }
40    }
41
42    pub fn read_file_content(path: PathBuf) -> Option<String> {
43        match fs::read_to_string(path) {
44            Ok(s) => Some(s),
45            Err(_) => None,
46        }
47        .filter(|s| !s.trim().is_empty())
48        .map(|s| s.trim().to_string())
49    }
50
51    pub fn known_candidates<'a>(sdkman_dir: PathBuf) -> Vec<&'static str> {
52        let absolute_path = sdkman_dir.join(VAR_DIR).join(CANDIDATES_FILE);
53        let verified_path = check_file_exists(absolute_path);
54        let panic = format!(
55            "the candidates file is missing: {}",
56            verified_path.to_str().unwrap()
57        );
58        let content = read_file_content(verified_path).expect(&panic);
59        let line_str: &'static str = Box::leak(content.into_boxed_str());
60        let mut fields = Vec::new();
61        for field in line_str.split(',') {
62            fields.push(field.trim());
63        }
64
65        fields
66    }
67
68    pub fn validate_candidate(all_candidates: Vec<&str>, candidate: &str) -> String {
69        if !all_candidates.contains(&candidate) {
70            eprintln!("{} is not a valid candidate.", candidate.bold());
71            process::exit(1);
72        } else {
73            candidate.to_string()
74        }
75    }
76
77    pub fn validate_version_path(base_dir: PathBuf, candidate: &str, version: &str) -> PathBuf {
78        let version_path = base_dir.join(CANDIDATES_DIR).join(candidate).join(version);
79        if version_path.exists() && version_path.is_dir() {
80            version_path
81        } else {
82            eprintln!(
83                "{} {} is not installed on your system",
84                candidate.bold(),
85                version.bold()
86            );
87            process::exit(1)
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use std::env;
95    use std::io::Write;
96    use std::path::PathBuf;
97
98    use serial_test::serial;
99    use tempfile::NamedTempFile;
100
101    use crate::constants::SDKMAN_DIR_ENV_VAR;
102    use crate::helpers::infer_sdkman_dir;
103    use crate::helpers::read_file_content;
104
105    #[test]
106    #[serial]
107    fn should_infer_sdkman_dir_from_env_var() {
108        let sdkman_dir = PathBuf::from("/home/someone/.sdkman");
109        env::set_var(SDKMAN_DIR_ENV_VAR, sdkman_dir.to_owned());
110        assert_eq!(sdkman_dir, infer_sdkman_dir());
111    }
112
113    #[test]
114    #[serial]
115    fn should_infer_fallback_dir() {
116        env::remove_var(SDKMAN_DIR_ENV_VAR);
117        let actual_sdkman_dir = dirs::home_dir().unwrap().join(".sdkman");
118        assert_eq!(actual_sdkman_dir, infer_sdkman_dir());
119    }
120
121    #[test]
122    #[serial]
123    fn should_read_content_from_file() {
124        let expected_version = "5.0.0";
125        let mut file = NamedTempFile::new().unwrap();
126        file.write(expected_version.as_bytes()).unwrap();
127        let path = file.path().to_path_buf();
128        let maybe_version = read_file_content(path);
129        assert_eq!(maybe_version, Some(expected_version.to_string()));
130    }
131
132    #[test]
133    #[serial]
134    fn should_fail_reading_file_content_from_empty_file() {
135        let file = NamedTempFile::new().unwrap();
136        let path = file.path().to_path_buf();
137        let maybe_version = read_file_content(path);
138        assert_eq!(maybe_version, None);
139    }
140}