diff --git a/dotenv/src/iter.rs b/dotenv/src/iter.rs index ed1c20a..5919b55 100644 --- a/dotenv/src/iter.rs +++ b/dotenv/src/iter.rs @@ -21,7 +21,11 @@ impl Iter { } } - /// Loads all variables found in the `reader` into the environment. + /// Loads all variables found in the `reader`, which not yet exist in the environment, + /// into the environment. + /// + /// If a variable is specified multiple times within the reader's data, + /// then the first occurrence is applied. pub fn load(mut self) -> Result<()> { self.remove_bom()?; @@ -35,7 +39,11 @@ impl Iter { Ok(()) } - /// A version of [load] that overrides older variables in case of duplicates. + /// Loads all variables found in the `reader` into the environment, + /// overriding any existing environment variables of the same name. + /// + /// If a variable is specified multiple times within the reader's data, + /// then the last occurrence is applied. pub fn overload(mut self) -> Result<()> { self.remove_bom()?; diff --git a/dotenv/src/lib.rs b/dotenv/src/lib.rs index 2fb4789..ee2631f 100644 --- a/dotenv/src/lib.rs +++ b/dotenv/src/lib.rs @@ -60,6 +60,15 @@ pub fn vars() -> Vars { /// Loads environment variables from the specified path. /// +/// If variables with the same names already exist in the environment, then their values will be +/// preserved. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* +/// file, the *first one* is applied. +/// +/// If you wish to ensure all variables are loaded from your *.env* file, ignoring variables +/// already existing in the environment, then use [`from_path_overload()`] instead. +/// /// # Examples /// /// ```no_run @@ -73,6 +82,29 @@ pub fn from_path>(path: P) -> Result<()> { iter.load() } +/// Loads environment variables from the specified path, +/// overriding existing environment variables. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* file, the +/// *last one* is applied. +/// +/// If you want the existing environment to take precendence, +/// or if you want to be able to override environment variables on the command line, +/// then use [`from_path()`] instead. +/// +/// # Examples +/// +/// ```no_run +/// use dirs::home_dir; +/// +/// let my_path = home_dir().map(|a| a.join("/absolute/path/.env")).unwrap(); +/// dotenvy::from_path_overload(my_path.as_path()); +/// ``` +pub fn from_path_overload>(path: P) -> Result<()> { + let iter = Iter::new(File::open(path).map_err(Error::Io)?); + iter.overload() +} + /// Returns an iterator over environment variables from the specified path. /// /// # Examples @@ -93,12 +125,21 @@ pub fn from_path_iter>(path: P) -> Result> { /// Loads environment variables from the specified file. /// +/// If variables with the same names already exist in the environment, then their values will be +/// preserved. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* +/// file, the *first one* is applied. +/// +/// If you wish to ensure all variables are loaded from your *.env* file, ignoring variables +/// already existing in the environment, then use [`from_filename_overload()`] instead. +/// /// # Examples /// ```no_run /// dotenvy::from_filename("custom.env").unwrap(); /// ``` /// -/// It is also possible to load from a typical *.env* file like so. However, using [dotenv] is preferred. +/// It is also possible to load from a typical *.env* file like so. However, using [`dotenv()`] is preferred. /// /// ``` /// dotenvy::from_filename(".env").unwrap(); @@ -109,6 +150,32 @@ pub fn from_filename>(filename: P) -> Result { Ok(path) } +/// Loads environment variables from the specified file, +/// overriding existing environment variables. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* file, the +/// *last one* is applied. +/// +/// If you want the existing environment to take precendence, +/// or if you want to be able to override environment variables on the command line, +/// then use [`from_filename()`] instead. +/// +/// # Examples +/// ```no_run +/// dotenvy::from_filename_overload("custom.env").unwrap(); +/// ``` +/// +/// It is also possible to load from a typical *.env* file like so. However, using [`overload()`] is preferred. +/// +/// ``` +/// dotenvy::from_filename_overload(".env").unwrap(); +/// ``` +pub fn from_filename_overload>(filename: P) -> Result { + let (path, iter) = Finder::new().filename(filename.as_ref()).find()?; + iter.overload()?; + Ok(path) +} + /// Returns an iterator over environment variables from the specified file. /// /// # Examples @@ -125,11 +192,20 @@ pub fn from_filename_iter>(filename: P) -> Result> { Ok(iter) } -/// Loads environment variables from [io::Read](std::io::Read). +/// Loads environment variables from [`io::Read`](std::io::Read). /// /// This is useful for loading environment variables from from IPC or the network. /// -/// For regular files, use [from_path] or [from_filename]. +/// If variables with the same names already exist in the environment, then their values will be +/// preserved. +/// +/// Where multiple declarations for the same environment variable exist in your `reader`, +/// the *first one* is applied. +/// +/// If you wish to ensure all variables are loaded from your `reader`, ignoring variables +/// already existing in the environment, then use [`from_read_overload()`] instead. +/// +/// For regular files, use [`from_path()`] or [`from_filename()`]. /// /// # Examples /// @@ -147,7 +223,36 @@ pub fn from_read(reader: R) -> Result<()> { Ok(()) } -/// Returns an iterator over environment variables from [io::Read](std::io::Read). +/// Loads environment variables from [`io::Read`](std::io::Read), +/// overriding existing environment variables. +/// +/// This is useful for loading environment variables from from IPC or the network. +/// +/// Where multiple declarations for the same environment variable exist in your `reader`, the +/// *last one* is applied. +/// +/// If you want the existing environment to take precendence, +/// or if you want to be able to override environment variables on the command line, +/// then use [`from_read()`] instead. +/// +/// For regular files, use [`from_path_overload()`] or [`from_filename_overload()`]. +/// +/// # Examples +/// ```no_run +/// # #![cfg(unix)] +/// use std::io::Read; +/// use std::os::unix::net::UnixStream; +/// +/// let mut stream = UnixStream::connect("/some/socket").unwrap(); +/// dotenvy::from_read_overload(stream).unwrap(); +/// ``` +pub fn from_read_overload(reader: R) -> Result<()> { + let iter = Iter::new(reader); + iter.overload()?; + Ok(()) +} + +/// Returns an iterator over environment variables from [`io::Read`](std::io::Read). /// /// # Examples /// @@ -166,8 +271,18 @@ pub fn from_read(reader: R) -> Result<()> { pub fn from_read_iter(reader: R) -> Iter { Iter::new(reader) } + /// Loads the *.env* file from the current directory or parents. This is typically what you want. /// +/// If variables with the same names already exist in the environment, then their values will be +/// preserved. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* +/// file, the *first one* is applied. +/// +/// If you wish to ensure all variables are loaded from your *.env* file, ignoring variables +/// already existing in the environment, then use [`overload()`] instead. +/// /// An error will be returned if the file is not found. /// /// # Examples @@ -181,7 +296,15 @@ pub fn dotenv() -> Result { Ok(path) } -/// A version of [dotenv] that overrides existing env vars +/// Loads all variables found in the `reader` into the environment, +/// overriding any existing environment variables of the same name. +/// +/// Where multiple declarations for the same environment variable exist in your *.env* file, the +/// *last one* is applied. +/// +/// If you want the existing environment to take precendence, +/// or if you want to be able to override environment variables on the command line, +/// then use [`dotenv()`] instead. /// /// # Examples /// ``` diff --git a/dotenv/tests/common/mod.rs b/dotenv/tests/common/mod.rs index 9b984cc..449f58c 100644 --- a/dotenv/tests/common/mod.rs +++ b/dotenv/tests/common/mod.rs @@ -4,6 +4,7 @@ use std::{env, io}; use tempfile::{tempdir, TempDir}; pub fn tempdir_with_dotenv(dotenv_text: &str) -> io::Result { + env::set_var("EXISTING", "from_env"); let dir = tempdir()?; env::set_current_dir(dir.path())?; let dotenv_path = dir.path().join(".env"); @@ -15,5 +16,5 @@ pub fn tempdir_with_dotenv(dotenv_text: &str) -> io::Result { #[allow(dead_code)] pub fn make_test_dotenv() -> io::Result { - tempdir_with_dotenv("TESTKEY=test_val") + tempdir_with_dotenv("TESTKEY=test_val\nTESTKEY=test_val_overridden\nEXISTING=from_file") } diff --git a/dotenv/tests/test-overload.rs b/dotenv/tests/test-default-location-overload.rs similarity index 63% rename from dotenv/tests/test-overload.rs rename to dotenv/tests/test-default-location-overload.rs index 8f151c6..f299027 100644 --- a/dotenv/tests/test-overload.rs +++ b/dotenv/tests/test-default-location-overload.rs @@ -8,14 +8,12 @@ use crate::common::*; #[test] fn test_overload() -> Result<(), Box> { - let dir = tempdir_with_dotenv( - " -var=old -var=new -", - )?; + let dir = make_test_dotenv()?; + overload()?; - assert_eq!(var("var")?, "new"); + + assert_eq!(env::var("TESTKEY")?, "test_val_overridden"); + assert_eq!(env::var("EXISTING")?, "from_file"); env::set_current_dir(dir.path().parent().unwrap())?; dir.close()?; diff --git a/dotenv/tests/test-default-location.rs b/dotenv/tests/test-default-location.rs index 7941fe1..60a6256 100644 --- a/dotenv/tests/test-default-location.rs +++ b/dotenv/tests/test-default-location.rs @@ -11,7 +11,9 @@ fn test_default_location() -> Result<(), Box> { let dir = make_test_dotenv()?; dotenv()?; + assert_eq!(env::var("TESTKEY")?, "test_val"); + assert_eq!(env::var("EXISTING")?, "from_env"); env::set_current_dir(dir.path().parent().unwrap())?; dir.close()?; diff --git a/dotenv/tests/test-from-filename-overload.rs b/dotenv/tests/test-from-filename-overload.rs new file mode 100644 index 0000000..abc132b --- /dev/null +++ b/dotenv/tests/test-from-filename-overload.rs @@ -0,0 +1,20 @@ +mod common; + +use dotenvy::*; +use std::{env, error::Error, result::Result}; + +use crate::common::*; + +#[test] +fn test_from_filename() -> Result<(), Box> { + let dir = make_test_dotenv()?; + + from_filename_overload(".env")?; + + assert_eq!(env::var("TESTKEY")?, "test_val_overridden"); + assert_eq!(env::var("EXISTING")?, "from_file"); + + env::set_current_dir(dir.path().parent().unwrap())?; + dir.close()?; + Ok(()) +} diff --git a/dotenv/tests/test-from-filename.rs b/dotenv/tests/test-from-filename.rs index 75145ea..daa5772 100644 --- a/dotenv/tests/test-from-filename.rs +++ b/dotenv/tests/test-from-filename.rs @@ -12,6 +12,7 @@ fn test_from_filename() -> Result<(), Box> { from_filename(".env")?; assert_eq!(env::var("TESTKEY")?, "test_val"); + assert_eq!(env::var("EXISTING")?, "from_env"); env::set_current_dir(dir.path().parent().unwrap())?; dir.close()?; diff --git a/dotenv/tests/test-from-path-overload.rs b/dotenv/tests/test-from-path-overload.rs new file mode 100644 index 0000000..fa8292e --- /dev/null +++ b/dotenv/tests/test-from-path-overload.rs @@ -0,0 +1,22 @@ +mod common; + +use crate::common::*; +use dotenvy::*; +use std::{env, error::Error, result::Result}; + +#[test] +fn test_from_path() -> Result<(), Box> { + let dir = make_test_dotenv()?; + + let mut path = env::current_dir()?; + path.push(".env"); + + from_path_overload(&path)?; + + assert_eq!(env::var("TESTKEY")?, "test_val_overridden"); + assert_eq!(env::var("EXISTING")?, "from_file"); + + env::set_current_dir(dir.path().parent().unwrap())?; + dir.close()?; + Ok(()) +} diff --git a/dotenv/tests/test-from-path.rs b/dotenv/tests/test-from-path.rs index f773166..71beaa3 100644 --- a/dotenv/tests/test-from-path.rs +++ b/dotenv/tests/test-from-path.rs @@ -14,6 +14,7 @@ fn test_from_path() -> Result<(), Box> { from_path(&path)?; assert_eq!(env::var("TESTKEY")?, "test_val"); + assert_eq!(env::var("EXISTING")?, "from_env"); env::set_current_dir(dir.path().parent().unwrap())?; dir.close()?; diff --git a/dotenv/tests/test-from-read-overload.rs b/dotenv/tests/test-from-read-overload.rs new file mode 100644 index 0000000..3b5ef1f --- /dev/null +++ b/dotenv/tests/test-from-read-overload.rs @@ -0,0 +1,20 @@ +mod common; + +use dotenvy::*; +use std::{env, error::Error, fs::File, result::Result}; + +use crate::common::*; + +#[test] +fn test_from_read() -> Result<(), Box> { + let dir = make_test_dotenv()?; + + from_read_overload(File::open(".env")?)?; + + assert_eq!(env::var("TESTKEY")?, "test_val_overridden"); + assert_eq!(env::var("EXISTING")?, "from_file"); + + env::set_current_dir(dir.path().parent().unwrap())?; + dir.close()?; + Ok(()) +} diff --git a/dotenv/tests/test-from-read.rs b/dotenv/tests/test-from-read.rs new file mode 100644 index 0000000..3e0c3ee --- /dev/null +++ b/dotenv/tests/test-from-read.rs @@ -0,0 +1,20 @@ +mod common; + +use dotenvy::*; +use std::{env, error::Error, fs::File, result::Result}; + +use crate::common::*; + +#[test] +fn test_from_read() -> Result<(), Box> { + let dir = make_test_dotenv()?; + + from_read(File::open(".env")?)?; + + assert_eq!(env::var("TESTKEY")?, "test_val"); + assert_eq!(env::var("EXISTING")?, "from_env"); + + env::set_current_dir(dir.path().parent().unwrap())?; + dir.close()?; + Ok(()) +}