with_java/
commands.rs

1use std::{collections::HashMap, process::ExitStatus};
2
3use color_eyre::{
4    eyre::{eyre, Context},
5    Result,
6};
7
8use crate::persistence;
9
10///
11/// add a new entry with the given name and path
12/// replacing it if it already exists
13///
14pub fn add(name: &str, path: &str) -> Result<()> {
15    let mut entries = persistence::read_config()?;
16
17    entries.insert(name.to_owned(), path.to_owned());
18
19    persistence::write_config(&entries)?;
20
21    Ok(())
22}
23
24///
25/// remove the entry with the given name, returning
26/// an error if it doesn't exist
27///
28pub fn remove(name: &str) -> Result<String> {
29    let mut entries = persistence::read_config()?;
30
31    check_entry(&entries, name)?;
32
33    let removed = entries.remove(name).expect("key to be present");
34
35    persistence::write_config(&entries)?;
36
37    Ok(removed)
38}
39
40///
41/// list all available entries, if any
42///
43pub fn list() -> Result<()> {
44    let entries = persistence::read_config()?;
45
46    if entries.is_empty() {
47        println!("no entries available");
48    } else {
49        for (name, path) in entries {
50            println!("{name}: \t\t{path}");
51        }
52    }
53
54    Ok(())
55}
56
57///
58/// run the given command with `JAVA_HOME` overriden to the given
59/// entry name and return whatever status code we get
60///
61pub fn run(name: &str, command: &Vec<String>) -> Result<ExitStatus> {
62    let entries = persistence::read_config()?;
63
64    let entry = check_entry(&entries, name)?;
65
66    let cmd = &command[0];
67    let args = &command[1..];
68
69    let mut process = std::process::Command::new(cmd)
70        .env("JAVA_HOME", entry)
71        .env("PATH", build_path(entry)?)
72        .args(args)
73        .spawn()
74        .expect("to be able to spawn process");
75
76    Ok(process.wait()?)
77}
78
79///
80/// bail out if this entry is not present
81///
82fn check_entry<'a>(entries: &'a HashMap<String, String>, name: &str) -> Result<&'a str> {
83    if !entries.contains_key(name) {
84        Err(eyre!(Error::NoSuchEntry))
85    } else {
86        Ok(entries.get(name).expect("key to be present"))
87    }
88}
89
90fn build_path(home: &str) -> Result<String> {
91    let formatted = format_bin_path(home);
92    if let Some(path) = std::env::var_os("PATH") {
93        let mut paths = std::env::split_paths(&path).collect::<Vec<_>>();
94        paths.insert(0, formatted.into());
95
96        let joined = std::env::join_paths(paths)
97            .context("failed to read PATH variable")?
98            .into_string()
99            .map_err(|_| Error::Encoding)?;
100
101        Ok(joined)
102    } else {
103        Ok(formatted)
104    }
105}
106
107fn format_bin_path(home: &str) -> String {
108    format!("{home}/bin")
109}
110
111#[derive(Debug, thiserror::Error)]
112pub enum Error {
113    #[error("no such entry exists")]
114    NoSuchEntry,
115
116    #[error("encoding error on PATH variable")]
117    Encoding,
118}