-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2180680
commit dffd117
Showing
3 changed files
with
123 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use std::env::var; | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
pub enum Shell { | ||
Posix, | ||
Bash, | ||
Zsh, | ||
} | ||
|
||
impl Shell { | ||
pub const ALL: [Self; 3] = [Self::Posix, Self::Bash, Self::Zsh]; | ||
|
||
pub const fn name(&self) -> &'static str { | ||
match self { | ||
Self::Posix => "sh", | ||
Self::Bash => "bash", | ||
Self::Zsh => "zsh", | ||
} | ||
} | ||
|
||
pub const fn env_file_path(&self) -> &'static str { | ||
match self { | ||
Self::Posix => ".profile", | ||
Self::Bash => ".bashrc", | ||
Self::Zsh => ".zshenv", | ||
} | ||
} | ||
|
||
pub fn env_file_should_create_if_nonexistent(&self) -> bool { | ||
// Create a new shell env file for the user if we are | ||
// confident that this is the shell that they are using | ||
var("SHELL").map_or(false, |current_shell| { | ||
// Detect /bin/sh, /bin/bash, /bin/zsh, etc | ||
current_shell.ends_with(&format!("/{}", self.name())) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,98 @@ | ||
use tokio::fs::write; | ||
use std::path::PathBuf; | ||
|
||
use futures::{stream::FuturesUnordered, TryStreamExt}; | ||
use tokio::{ | ||
fs::{read_to_string, write}, | ||
io::ErrorKind, | ||
}; | ||
|
||
use crate::{ | ||
result::{RokitError, RokitResult}, | ||
storage::Home, | ||
}; | ||
|
||
use super::shell::Shell; | ||
|
||
const ENV_SHELL_FILE_PATH: &str = "env"; | ||
const ENV_SHELL_SCRIPT: &str = include_str!("./env.sh"); | ||
|
||
pub async fn add_to_path(home: &Home) -> RokitResult<bool> { | ||
// Write our shell script to the known location | ||
// Find our binaries dir and try to format it as "$HOME/.rokit/bin" | ||
let bin_dir = home.path().join("bin"); | ||
let bin_dir_str = bin_dir.to_str().ok_or(RokitError::InvalidUtf8)?; | ||
let bin_dir_in_home = replace_home_path_with_var(bin_dir_str); | ||
|
||
// Do the same for the shell script path - "$HOME/.rokit/env" | ||
let file_path = home.path().join(ENV_SHELL_FILE_PATH); | ||
let file_contents = ENV_SHELL_SCRIPT.replace( | ||
"{rokit_bin_path}", | ||
bin_dir.to_str().ok_or(RokitError::InvalidUtf8)?, | ||
); | ||
let file_path_str = file_path.to_str().ok_or(RokitError::InvalidUtf8)?; | ||
let file_path_in_home = replace_home_path_with_var(file_path_str); | ||
|
||
// Write our shell init script to the known location | ||
let file_contents = ENV_SHELL_SCRIPT.replace("{rokit_bin_path}", &bin_dir_in_home); | ||
write(file_path, file_contents).await?; | ||
|
||
// TODO: Add the path to known shell profile(s) | ||
// Add the path to known shell profiles | ||
let added_any = if let Some(home_dir) = dirs::home_dir() { | ||
let futs = Shell::ALL | ||
.iter() | ||
.map(|shell| { | ||
let shell_env_path = home_dir.join(shell.env_file_path()); | ||
let shell_should_create = shell.env_file_should_create_if_nonexistent(); | ||
append_to_shell_file( | ||
shell_env_path, | ||
format!(". \"{file_path_in_home}\""), | ||
shell_should_create, | ||
) | ||
}) | ||
.collect::<FuturesUnordered<_>>(); | ||
futs.try_collect::<Vec<_>>() | ||
.await? | ||
.iter() | ||
.any(|added| *added) | ||
} else { | ||
false | ||
}; | ||
|
||
Ok(added_any) | ||
} | ||
|
||
async fn append_to_shell_file( | ||
file_path: PathBuf, | ||
line_to_append: String, | ||
create_if_nonexistent: bool, | ||
) -> RokitResult<bool> { | ||
let mut file_contents = match read_to_string(&file_path).await { | ||
Ok(contents) => contents, | ||
Err(e) if e.kind() == ErrorKind::NotFound && create_if_nonexistent => String::new(), | ||
Err(e) => return Err(e.into()), | ||
}; | ||
|
||
if file_contents.contains(&line_to_append) { | ||
return Ok(false); | ||
} | ||
|
||
// NOTE: Make sure we put the new contents on their own | ||
// line and not conflicting with any existing command(s) | ||
if !file_contents.ends_with('\n') { | ||
file_contents.push('\n'); | ||
} | ||
|
||
file_contents.push_str(&line_to_append); | ||
file_contents.push('\n'); | ||
|
||
write(file_path, file_contents).await?; | ||
|
||
Ok(true) | ||
} | ||
|
||
Ok(false) | ||
fn replace_home_path_with_var(path: &str) -> String { | ||
let home_dir = match dirs::home_dir() { | ||
Some(home_dir) => home_dir, | ||
None => return path.to_string(), | ||
}; | ||
let home_dir_str = match home_dir.to_str() { | ||
Some(home_dir_str) => home_dir_str, | ||
None => return path.to_string(), | ||
}; | ||
path.replace(home_dir_str, "$HOME") | ||
} |