Skip to content

Commit

Permalink
Merge pull request #43 from superatomic/30-shell-specific-variables-i…
Browse files Browse the repository at this point in the history
…mproved

Add new form for shell-specific variables and deprecate the old form
  • Loading branch information
superatomic authored Apr 15, 2022
2 parents 309bf13 + 0b117ed commit b19d0dc
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 24 deletions.
63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ shellexpand = "2.1.0"
human-panic = "1.0.3"
log = "0.4.16"
env_logger = "0.9.0"

[dev-dependencies]
indoc = "1.0.4"
pretty_assertions = "1.2.1"
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,26 @@ PATH = ["$PATH", "$BIN_HOME", "$CARGO_HOME/bin"]

#### Shell Specific Environment Variables

To set environment variables for only one shell, add a new table called `[shell.NAME]` after all standard definitions,
where `NAME` is on of `bash`, `zsh`, or `fish`.
Then list the environment variables that will only be added if `xshe` is being used for the given shell.
To set environment variables for only one shell, add a `.NAME` prefix after the name of the environment variable,
where `NAME` is one of `bash`, `zsh`, or `fish`.
These environment variables will only be added if the given shell is used.

As an example, these lines make `$HISTFILE` be set to different values between different shells,
and to have `$ZSH_CACHE_DIR` only be set in **zsh**, do this:

For example, to make `$HISTFILE` be different between shells and `$ZSH_CACHE_DIR` only be set in **zsh**, do this:
```toml
[shell.bash]
HISTFILE = "$XDG_STATE_HOME/bash_history"
HISTFILE.bash = "$XDG_STATE_HOME/bash_history"
HISTFILE.zsh = "$XDG_STATE_HOME/zsh_history"

ZSH_CACHE_DIR.zsh = "$XDG_CACHE_HOME/oh-my-zsh"
```

[shell.zsh]
HISTFILE = "$XDG_STATE_HOME/zsh_history"
ZSH_CACHE_DIR = "$XDG_CACHE_HOME/oh-my-zsh"
You can use `._` instead of using a shell name to specify a default if an option doesn't apply to any of the shells.
For example, these lines set the `$EDITOR` to `nano` on **bash**, but [`micro`][micro] on everything else:

```toml
EDITOR.bash = "$(which nano)"
EDITOR._ = "$(which micro)"
```

### Sourcing the `xshe.toml` file
Expand Down Expand Up @@ -272,6 +280,7 @@ additional terms or conditions.
[Cargo]: https://doc.rust-lang.org/cargo/
[install Cargo/Rust]: https://www.rust-lang.org/tools/install
[toml]: https://toml.io/en/
[micro]: https://micro-editor.github.io/

[example]: https://gist.github.com/superatomic/8f22ada9864c85984d51e0cc6fae4250
[example]: https://gist.github.com/superatomic/52a46e53a4afce75ede4db7ba6354e0a
[path?]: https://askubuntu.com/questions/551990/what-does-path-mean
141 changes: 136 additions & 5 deletions src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ use serde::Deserialize;
use std::collections::HashMap;
use std::string::String;

pub(crate) type EnvironmentVariables = IndexMap<String, EnvValue>;
pub(crate) type EnvironmentVariables = IndexMap<String, EnvVariableOption>;

/// The TOML file to load environment variables from.
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
pub(crate) struct ConfigFile {
#[serde(flatten)]
pub(crate) vars: EnvironmentVariables,

pub(crate) shell: Option<HashMap<String, EnvironmentVariables>>,
// Deprecated
pub(crate) shell: Option<HashMap<String, IndexMap<String, EnvVariableValue>>>,
}

impl ConfigFile {
Expand All @@ -35,10 +36,140 @@ impl ConfigFile {
}
}

#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(untagged)]
pub(crate) enum EnvVariableOption {
Specific(IndexMap<String, EnvVariableValue>),
General(EnvVariableValue),
}

/// Enum of possible environment variable value types.
#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(untagged)]
pub(crate) enum EnvValue {
pub(crate) enum EnvVariableValue {
String(String),
Array(Vec<String>),
}

#[cfg(test)]
mod tests {
use indexmap::indexmap;
use indoc::indoc;
use pretty_assertions::assert_eq;

use super::*;
use EnvVariableOption::*;

/// A function to get a ConfigFile from a str more easily.
fn to_config(toml_str: &str) -> ConfigFile {
toml::from_str(toml_str).unwrap()
}

/// Used to compare TOML to its expected representation in all other tests.
fn assert_config_value(toml_str: &str, map: EnvironmentVariables) {
assert_eq!(to_config(toml_str).vars, map);
}

#[test]
fn test_config_file_load_string() {
assert_config_value(
indoc! {r#"
FOO = "Bar"
"#},
indexmap! {
"FOO".into() => General(EnvVariableValue::String("Bar".into())),
},
)
}

#[test]
fn test_config_file_load_path() {
assert_config_value(
indoc! {r#"
PATH = ["/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]
"#},
indexmap! {
"PATH".into() => General(EnvVariableValue::Array(vec![
"/usr/local/bin".into(),
"/usr/bin".into(),
"/bin".into(),
"/usr/sbin".into(),
"/sbin".into(),
])),
},
)
}

#[test]
fn test_config_file_load_specific() {
assert_config_value(
indoc! {r#"
ONLY_FOR_BASH.bash = "Do people read test cases?"
"#},
indexmap! {
"ONLY_FOR_BASH".into() => Specific(indexmap! {
"bash".into() => EnvVariableValue::String("Do people read test cases?".into()),
}),
},
)
}

#[test]
fn test_config_file_load_specific_other() {
assert_config_value(
indoc! {r#"
SOME_VARIABLE.fish = "you're pretty"
SOME_VARIABLE._ = '[ACCESS DENIED]'
"#},
indexmap! {
"SOME_VARIABLE".into() => Specific(indexmap! {
"fish".into() => EnvVariableValue::String("you're pretty".into()),
"_".into() => EnvVariableValue::String("[ACCESS DENIED]".into()),
})
},
)
}

#[test]
fn test_config_file_load_specific_other_alt() {
assert_config_value(
indoc! {r#"
[SOME_VARIABLE]
fish = "you're pretty"
_ = '[ACCESS DENIED]'
[ANOTHER_VARIABLE]
zsh = 'Zzz'
"#},
indexmap! {
"SOME_VARIABLE".into() => Specific(indexmap! {
"fish".into() => EnvVariableValue::String("you're pretty".into()),
"_".into() => EnvVariableValue::String("[ACCESS DENIED]".into()),
}),
"ANOTHER_VARIABLE".into() => Specific(indexmap! {
"zsh".into() => EnvVariableValue::String("Zzz".into()),
}),
},
)
}

#[test]
fn test_config_file_everything() {
assert_config_value(
indoc! {r#"
FOO = 'bar'
BAZ.zsh = 'zž'
BAZ.fish = ['gone', 'fishing']
BAZ._ = 'other'
"#},
indexmap! {
"FOO".into() => General(EnvVariableValue::String("bar".into())),
"BAZ".into() => Specific(indexmap! {
"zsh".into() => EnvVariableValue::String("zž".into()),
"fish".into() => EnvVariableValue::Array(vec!["gone".into(), "fishing".into()]),
"_".into() => EnvVariableValue::String("other".into()),
})
},
)
}
}
Loading

0 comments on commit b19d0dc

Please sign in to comment.