Skip to content

Commit

Permalink
feat: Implement overriding API methods and correct inconsistent naming (
Browse files Browse the repository at this point in the history
#47)

* feat: Implement overriding api methods

* fix: Use consistent naming
  • Loading branch information
LeoniePhiline committed Dec 10, 2022
1 parent 639e9b1 commit 69a0623
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 37 deletions.
14 changes: 11 additions & 3 deletions dotenv/src/iter.rs
Expand Up @@ -21,7 +21,11 @@ impl<R: Read> Iter<R> {
}
}

/// Loads all variables found in the `reader` into the environment.
/// Loads all variables found in the `reader` into the environment,
/// preserving any existing environment variables of the same name.
///
/// 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()?;

Expand All @@ -35,8 +39,12 @@ impl<R: Read> Iter<R> {
Ok(())
}

/// A version of [load] that overrides older variables in case of duplicates.
pub fn overload(mut self) -> Result<()> {
/// 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 load_override(mut self) -> Result<()> {
self.remove_bom()?;

for item in self {
Expand Down
143 changes: 133 additions & 10 deletions dotenv/src/lib.rs
Expand Up @@ -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_override`] instead.
///
/// # Examples
///
/// ```no_run
Expand All @@ -73,7 +82,30 @@ pub fn from_path<P: AsRef<Path>>(path: P) -> Result<()> {
iter.load()
}

/// Returns an iterator over environment variables from the specified path.
/// 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 precedence,
/// 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_override(my_path.as_path());
/// ```
pub fn from_path_override<P: AsRef<Path>>(path: P) -> Result<()> {
let iter = Iter::new(File::open(path).map_err(Error::Io)?);
iter.load_override()
}

/// Returns an iterator over environment variables from the specified path.
///
/// # Examples
///
Expand All @@ -93,12 +125,21 @@ pub fn from_path_iter<P: AsRef<Path>>(path: P) -> Result<Iter<File>> {

/// 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_override`] 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();
Expand All @@ -109,6 +150,32 @@ pub fn from_filename<P: AsRef<Path>>(filename: P) -> Result<PathBuf> {
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 precedence,
/// 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_override("custom.env").unwrap();
/// ```
///
/// It is also possible to load from a typical *.env* file like so. However, using [`dotenv_override`] is preferred.
///
/// ```
/// dotenvy::from_filename_override(".env").unwrap();
/// ```
pub fn from_filename_override<P: AsRef<Path>>(filename: P) -> Result<PathBuf> {
let (path, iter) = Finder::new().filename(filename.as_ref()).find()?;
iter.load_override()?;
Ok(path)
}

/// Returns an iterator over environment variables from the specified file.
///
/// # Examples
Expand All @@ -125,11 +192,20 @@ pub fn from_filename_iter<P: AsRef<Path>>(filename: P) -> Result<Iter<File>> {
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_override`] instead.
///
/// For regular files, use [`from_path`] or [`from_filename`].
///
/// # Examples
///
Expand All @@ -147,7 +223,36 @@ pub fn from_read<R: io::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 precedence,
/// 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_override`] or [`from_filename_override`].
///
/// # 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_override(stream).unwrap();
/// ```
pub fn from_read_override<R: io::Read>(reader: R) -> Result<()> {
let iter = Iter::new(reader);
iter.load_override()?;
Ok(())
}

/// Returns an iterator over environment variables from [`io::Read`](std::io::Read).
///
/// # Examples
///
Expand All @@ -166,8 +271,18 @@ pub fn from_read<R: io::Read>(reader: R) -> Result<()> {
pub fn from_read_iter<R: io::Read>(reader: R) -> Iter<R> {
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 [`dotenv_override`] instead.
///
/// An error will be returned if the file is not found.
///
/// # Examples
Expand All @@ -181,16 +296,24 @@ pub fn dotenv() -> Result<PathBuf> {
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 precedence,
/// or if you want to be able to override environment variables on the command line,
/// then use [`dotenv`] instead.
///
/// # Examples
/// ```
/// use dotenvy::overload;
/// overload().ok();
/// use dotenvy::dotenv_override;
/// dotenv_override().ok();
/// ```
pub fn overload() -> Result<PathBuf> {
pub fn dotenv_override() -> Result<PathBuf> {
let (path, iter) = Finder::new().find()?;
iter.overload()?;
iter.load_override()?;
Ok(path)
}

Expand Down
3 changes: 2 additions & 1 deletion dotenv/tests/common/mod.rs
Expand Up @@ -4,6 +4,7 @@ use std::{env, io};
use tempfile::{tempdir, TempDir};

pub fn tempdir_with_dotenv(dotenv_text: &str) -> io::Result<TempDir> {
env::set_var("EXISTING", "from_env");
let dir = tempdir()?;
env::set_current_dir(dir.path())?;
let dotenv_path = dir.path().join(".env");
Expand All @@ -15,5 +16,5 @@ pub fn tempdir_with_dotenv(dotenv_text: &str) -> io::Result<TempDir> {

#[allow(dead_code)]
pub fn make_test_dotenv() -> io::Result<TempDir> {
tempdir_with_dotenv("TESTKEY=test_val")
tempdir_with_dotenv("TESTKEY=test_val\nTESTKEY=test_val_overridden\nEXISTING=from_file")
}
21 changes: 21 additions & 0 deletions dotenv/tests/test-default-location-override.rs
@@ -0,0 +1,21 @@
mod common;

use std::{env, error::Error, result::Result};

use dotenvy::*;

use crate::common::*;

#[test]
fn test_default_location_override() -> Result<(), Box<dyn Error>> {
let dir = make_test_dotenv()?;

dotenv_override()?;

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(())
}
2 changes: 2 additions & 0 deletions dotenv/tests/test-default-location.rs
Expand Up @@ -11,7 +11,9 @@ fn test_default_location() -> Result<(), Box<dyn Error>> {
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()?;
Expand Down
20 changes: 20 additions & 0 deletions dotenv/tests/test-from-filename-override.rs
@@ -0,0 +1,20 @@
mod common;

use dotenvy::*;
use std::{env, error::Error, result::Result};

use crate::common::*;

#[test]
fn test_from_filename_override() -> Result<(), Box<dyn Error>> {
let dir = make_test_dotenv()?;

from_filename_override(".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(())
}
1 change: 1 addition & 0 deletions dotenv/tests/test-from-filename.rs
Expand Up @@ -12,6 +12,7 @@ fn test_from_filename() -> Result<(), Box<dyn Error>> {
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()?;
Expand Down
22 changes: 22 additions & 0 deletions dotenv/tests/test-from-path-override.rs
@@ -0,0 +1,22 @@
mod common;

use crate::common::*;
use dotenvy::*;
use std::{env, error::Error, result::Result};

#[test]
fn test_from_path_override() -> Result<(), Box<dyn Error>> {
let dir = make_test_dotenv()?;

let mut path = env::current_dir()?;
path.push(".env");

from_path_override(&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(())
}
1 change: 1 addition & 0 deletions dotenv/tests/test-from-path.rs
Expand Up @@ -14,6 +14,7 @@ fn test_from_path() -> Result<(), Box<dyn Error>> {
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()?;
Expand Down
20 changes: 20 additions & 0 deletions dotenv/tests/test-from-read-override.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_override() -> Result<(), Box<dyn Error>> {
let dir = make_test_dotenv()?;

from_read_override(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(())
}
20 changes: 20 additions & 0 deletions 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<dyn Error>> {
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(())
}

0 comments on commit 69a0623

Please sign in to comment.