Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new form for shell-specific variables and deprecate the old form #43

Merged
merged 3 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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