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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! It loads environment variables from a `.env` file, if available, and mashes
//! those with the environment variables provided by the operating system.

mod dotenv;
mod error;

#[cfg(test)]
mod tests;

use std::env;
use std::ffi::OsStr;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::sync::Once;

pub use crate::dotenv::Dotenv;
pub use crate::error::*;

static LOAD: Once = Once::new();

/// Load the `.env` file and fetch the environment variable key from the current process.
///
/// For more details, please visit [`load`] or [`std::env::var`].
///
/// # NOTE
///
/// - The `.env` file will be loaded once, so it's cheap to call [`var`] again and again.
/// - The error occurs in loading the `.env` file will be ignored.
///
/// # Examples
///
/// ```no_run
/// let var = dotenv::var("FOO").unwrap();
/// ```
pub fn var<K: AsRef<OsStr>>(key: K) -> Result<String> {
    LOAD.call_once(|| {
        load().ok();
    });

    env::var(key).map_err(Error::from)
}

/// Load the `.env` file and return an iterator of (variable, value) pairs of strings,
/// for all the environment variables of the current process.
///
/// The returned iterator contains a snapshot of the process's environment variables at the
/// time of this invocation, modifications to environment variables afterwards will not be
/// reflected in the returned iterator.
///
/// For more details, please visit [`load`] or [`std::env::vars`].
///
/// # Examples
///
/// ```no_run
/// let vars: Vec<(String, String)> = dotenv::vars().collect();
/// ```
pub fn vars() -> env::Vars {
    LOAD.call_once(|| {
        load().ok();
    });

    env::vars()
}

/// Load file at the given `path` and then set environment variables.
///
/// # Examples
///
/// ```
/// use std::env;
///
/// let my_path = env::home_dir().map(|dir| dir.join(".env")).unwrap();
/// dotenv::load_path(my_path.as_path()).ok();
/// ```
pub fn load_path<P: AsRef<Path>>(path: P) -> Result<()> {
    try_from_path(path).and_then(Dotenv::load)
}

/// Create a [`Dotenv`] instance from the given `path`.
///
/// # Examples
///
/// ```no_run
/// use std::env;
///
/// let my_path = env::home_dir().map(|dir| dir.join(".env")).unwrap();
/// for item in dotenv::try_from_path(my_path.as_path()).unwrap() {
///   let (key, val) = item.unwrap();
///   println!("{}={}", key, val);
/// }
/// ```
pub fn try_from_path<P: AsRef<Path>>(path: P) -> Result<Dotenv> {
    Ok(Dotenv::new(File::open(path)?))
}

/// Load the given `filename` and then set environment variables.
///
/// It will search for the given `filename` in the current directory or its parents in sequence.
///
/// # Examples
///
/// ```
/// dotenv::load_filename(".env.local").ok();
/// ```
pub fn load_filename<P: AsRef<Path>>(filename: P) -> Result<PathBuf> {
    search(filename.as_ref()).and_then(|path| load_path(&path).and(Ok(path)))
}

/// Create a [`Dotenv`] instance from the given `filename`
///
/// # Examples
///
/// ```no_run
/// let envs = dotenv::try_from_filename(".env.local").unwrap();
///
/// for (key, value) in envs.flatten() {
///   println!("{}={}", key, value);
/// }
/// ```
pub fn try_from_filename<P: AsRef<Path>>(filename: P) -> Result<Dotenv> {
    search(filename.as_ref()).and_then(try_from_path)
}

/// Load `.env` file and then set environment variables.
///
/// It will search for `.env` file in the current directory or its parents in sequence.
///
/// # Examples
///
/// ```
/// dotenv::load().ok();
/// ```
pub fn load() -> Result<PathBuf> {
    load_filename(".env")
}

/// Create a [`Dotenv`] instance from the `.env` file.
///
/// # Examples
///
/// ```no_run
/// for item in dotenv::try_init().unwrap() {
///   let (key, value) = item.unwrap();
///   println!("{}={}", key, value);
/// }
/// ```
pub fn try_init() -> Result<Dotenv> {
    try_from_filename(".env")
}

/// Search for `filename` in the current directory or its parents in sequence.
fn search(filename: &Path) -> Result<PathBuf> {
    let current_dir = env::current_dir()?;
    fn path_not_found() -> Error {
        std::io::Error::new(std::io::ErrorKind::NotFound, "path not found").into()
    }

    current_dir
        .ancestors()
        .map(|dir| dir.join(filename))
        .find(|path| path.is_file())
        .ok_or_else(path_not_found)
}