1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! # envvars
//!
//! `envvars` helps to detect a list of available shells and related environment
//! variables. If works in two steps:
//!
//! - detecting a list of available shells and creating `Profile` for each found
//! shell
//! - loading a list of environment variables for selected or each shell
//!
//! ## Usage
//!
//! Getting all available (detected) shells and related to each shell list of
//! environment variables
//!
//! ```
//! use envvars::{get_profiles, Profile};
//!
//! let mut profiles: Vec<Profile> = get_profiles().unwrap();
//!
//! // By default profile doesn't have loaded list of environment variables.
//! // It should be loaded calling method load().
//! profiles.iter_mut().for_each(|profile| {
//!     // Attempt to load envvars
//!     if let Err(err) = profile.load() {
//!         eprintln!("Cannot load envvars for {}: {err}", profile.name);
//!     }
//!     if let Some(envvars) = &profile.envvars {
//!         println!("Environment variables for {}", profile.name);
//!         envvars.iter().for_each(|(key, value)| {
//!             println!("{key}: {value}");
//!         });
//!     }
//! });
//! ```
//!
//! Extract environment variables without shell context.
//!
//! ```
//! use std::collections::HashMap;
//! use envvars::get_context_envvars;
//!
//! let vars: HashMap<String, String> = get_context_envvars().unwrap();
//!
//! assert!(vars.contains_key("PATH") || vars.contains_key("Path") || vars.contains_key("path"));
//! ```
//!
//! ## Diffrence from `std::env::vars`
//!
//! `envvars` actually executes each found `shell` it means: all settings of the target
//! shell will be inited before a list of environment variables will be requested. That's
//! very sensitive if the configuration of some shell includes some initialization script,
//! which affects environment variables. That means in some cases `std::env::vars` and
//! `envvars` could give different results.
//!  
//! ## How it works
//!
//! Under the hood, `envvars` takes each shell, and executes it with a command,
//! which posts a list of environment variables to `stdout`. As soon as executing
//! is done, `envvars` reads `stdout` and parse environment variables into
//! `HashMap<String, String>`.
//!
//! As soon as extracting process could take a sensitive time (~1sec on windows
//! and ~10ms on Unix-based OS), `envvars` doesn't extract environment variables
//! during detecting the shell's profiles. That's the developer's decision when it
//! should be done for the selected or each profile.
//!
//! `envvars` creates a small executable application in the system's temporary folder.
//! This application is used to "drop" list of environment variables into `stdout`
//! of the parent process and does nothing else. As soon as `envvars` instance is
//! dropped, the application would be removed from the disk.
//!
//! For security reasons `envvars` checks the checksum of the extractor each time
//! before using it. If a checksum is invalid (the file was damaged/changed etc),
//! `envars` will remove a corrupted file and create a new one.
//!  
//! ## Unix specific
//!
//! `envvars` reads `/etc/shells` and analyze each shell from a list
//!
//! ## Windows specific
//!
//! `envvars` checks for availability next shells:
//! - Command Prompt
//! - Windows PowerShell
//! - .NET Core PowerShell Global Tool
//! - Cygwin x64
//! - Cygwin
//! - bash (MSYS2)
//! - GitBash
//!
//! ## Guaranteed results
//!
//! Because `envvars` tries to initialize each shell and "drop" a list of environment
//! variables to `stdout`, the shell should support the possibility to put a command as
//! an argument, for example: `/bin/bash -c path_to_command`. Obviously not many, but
//! still some shells don't support it (like windows command prompt). In this case, you
//! still can use `get_context_envvars()` to get a list of environment variables without
//! the shell's context.
//!

#[macro_use]
extern crate lazy_static;
use std::{collections::HashMap, sync::Mutex};
mod assets;
mod checksum;
mod decoder;
mod error;
mod extractor;
mod profiles;

pub use error::Error;
pub use extractor::cleanup;
use extractor::Extractor;
pub use profiles::{get as get_profiles, Profile};

lazy_static! {
    #[doc(hidden)]
    static ref EXTRACTOR: Mutex<Extractor> = Mutex::new(Extractor::new());
}

/// Extract environment variables without shell context.
///
/// # Examples
///
/// ```
/// use std::collections::HashMap;
/// use envvars::get_context_envvars;
///
/// let vars: HashMap<String, String> = get_context_envvars().unwrap();
///
/// assert!(vars.contains_key("PATH") || vars.contains_key("Path") || vars.contains_key("path"));
/// ```
pub fn get_context_envvars() -> Result<HashMap<String, String>, Error> {
    EXTRACTOR
        .lock()
        .map_err(|e| Error::PoisonError(e.to_string()))?
        .get(None, &Vec::new())
}