diff options
Diffstat (limited to 'crates/atuin-dotfiles/src/shell.rs')
| -rw-r--r-- | crates/atuin-dotfiles/src/shell.rs | 241 |
1 files changed, 0 insertions, 241 deletions
diff --git a/crates/atuin-dotfiles/src/shell.rs b/crates/atuin-dotfiles/src/shell.rs deleted file mode 100644 index 73a9ce8c..00000000 --- a/crates/atuin-dotfiles/src/shell.rs +++ /dev/null @@ -1,241 +0,0 @@ -use eyre::{Result, ensure, eyre}; -use rmp::{decode, encode}; -use serde::Serialize; - -use atuin_common::shell::{Shell, ShellError}; - -use crate::store::AliasStore; - -pub mod bash; -pub mod fish; -pub mod powershell; -pub mod xonsh; -pub mod zsh; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] -pub struct Alias { - pub name: String, - pub value: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] -pub struct Var { - pub name: String, - pub value: String, - - // False? This is a _shell var_ - // True? This is an _env var_ - pub export: bool, -} - -impl Var { - /// Serialize into the given vec - /// This is intended to be called by the store - pub fn serialize(&self, output: &mut Vec<u8>) -> Result<()> { - encode::write_array_len(output, 3)?; // 3 fields - - encode::write_str(output, self.name.as_str())?; - encode::write_str(output, self.value.as_str())?; - encode::write_bool(output, self.export)?; - - Ok(()) - } - - pub fn deserialize(bytes: &mut decode::Bytes) -> Result<Self> { - fn error_report<E: std::fmt::Debug>(err: E) -> eyre::Report { - eyre!("{err:?}") - } - - let nfields = decode::read_array_len(bytes).map_err(error_report)?; - - ensure!( - nfields == 3, - "too many entries in v0 dotfiles env create record, got {}, expected {}", - nfields, - 3 - ); - - let bytes = bytes.remaining_slice(); - - let (key, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?; - let (value, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?; - - let mut bytes = decode::Bytes::new(bytes); - let export = decode::read_bool(&mut bytes).map_err(error_report)?; - - ensure!( - bytes.remaining_slice().is_empty(), - "trailing bytes in encoded dotfiles env record, malformed" - ); - - Ok(Var { - name: key.to_owned(), - value: value.to_owned(), - export, - }) - } -} - -pub fn parse_alias(line: &str) -> Option<Alias> { - // consider the fact we might be importing a fish alias - // 'alias' output - // fish: alias foo bar - // posix: foo=bar - - let is_fish = line.split(' ').next().unwrap_or("") == "alias"; - - let parts: Vec<&str> = if is_fish { - line.split(' ') - .enumerate() - .filter_map(|(n, i)| if n == 0 { None } else { Some(i) }) - .collect() - } else { - line.split('=').collect() - }; - - if parts.len() <= 1 { - return None; - } - - let mut parts = parts.iter().map(|s| s.to_string()); - - let name = parts.next().unwrap(); - - let remaining = if is_fish { - parts.collect::<Vec<String>>().join(" ") - } else { - parts.collect::<Vec<String>>().join("=") - }; - - Some(Alias { - name, - value: remaining.trim().to_string(), - }) -} - -pub fn existing_aliases(shell: Option<Shell>) -> Result<Vec<Alias>, ShellError> { - let shell = if let Some(shell) = shell { - shell - } else { - Shell::current() - }; - - // this only supports posix-y shells atm - if !shell.is_posixish() { - return Err(ShellError::NotSupported); - } - - // This will return a list of aliases, each on its own line - // They will be in the form foo=bar - let aliases = shell.run_interactive(["alias"])?; - - let aliases: Vec<Alias> = aliases.lines().filter_map(parse_alias).collect(); - - Ok(aliases) -} - -/// Import aliases from the current shell -/// This will not import aliases already in the store -/// Returns aliases that were set -pub async fn import_aliases(store: &AliasStore) -> Result<Vec<Alias>> { - let shell_aliases = existing_aliases(None)?; - let store_aliases = store.aliases().await?; - - let mut res = Vec::new(); - - for alias in shell_aliases { - // O(n), but n is small, and imports infrequent - // can always make a map - if store_aliases.contains(&alias) { - continue; - } - - res.push(alias.clone()); - store.set(&alias.name, &alias.value).await?; - } - - Ok(res) -} - -#[cfg(test)] -mod tests { - use crate::shell::{Alias, parse_alias}; - - #[test] - fn test_parse_simple_alias() { - let alias = super::parse_alias("foo=bar").expect("failed to parse alias"); - assert_eq!(alias.name, "foo"); - assert_eq!(alias.value, "bar"); - } - - #[test] - fn test_parse_quoted_alias() { - let alias = super::parse_alias("emacs='TERM=xterm-24bits emacs -nw'") - .expect("failed to parse alias"); - - assert_eq!(alias.name, "emacs"); - assert_eq!(alias.value, "'TERM=xterm-24bits emacs -nw'"); - - let git_alias = super::parse_alias("gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'").expect("failed to parse alias"); - assert_eq!(git_alias.name, "gwip"); - assert_eq!( - git_alias.value, - "'git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign --message \"--wip-- [skip ci]\"'" - ); - } - - #[test] - fn test_parse_quoted_alias_equals() { - let alias = super::parse_alias("emacs='TERM=xterm-24bits emacs -nw --foo=bar'") - .expect("failed to parse alias"); - assert_eq!(alias.name, "emacs"); - assert_eq!(alias.value, "'TERM=xterm-24bits emacs -nw --foo=bar'"); - } - - #[test] - fn test_parse_fish() { - let alias = super::parse_alias("alias foo bar").expect("failed to parse alias"); - assert_eq!(alias.name, "foo"); - assert_eq!(alias.value, "bar"); - - let alias = - super::parse_alias("alias x 'exa --icons --git --classify --group-directories-first'") - .expect("failed to parse alias"); - - assert_eq!(alias.name, "x"); - assert_eq!( - alias.value, - "'exa --icons --git --classify --group-directories-first'" - ); - } - - #[test] - fn test_parse_with_fortune() { - // Because we run the alias command in an interactive subshell - // there may be other output. - // Ensure that the parser can handle it - // Annoyingly not all aliases are picked up all the time if we use - // a non-interactive subshell. Boo. - let shell = " -/ In a consumer society there are \\ -| inevitably two kinds of slaves: the | -| prisoners of addiction and the | -\\ prisoners of envy. / - ------------------------------------- - \\ ^__^ - \\ (oo)\\_______ - (__)\\ )\\/\\ - ||----w | - || || -emacs='TERM=xterm-24bits emacs -nw --foo=bar' -k=kubectl -"; - - let aliases: Vec<Alias> = shell.lines().filter_map(parse_alias).collect(); - assert_eq!(aliases[0].name, "emacs"); - assert_eq!(aliases[0].value, "'TERM=xterm-24bits emacs -nw --foo=bar'"); - - assert_eq!(aliases[1].name, "k"); - assert_eq!(aliases[1].value, "kubectl"); - } -} |
