env-file-reader

Build Status Codecov Latest Version Docs License: MIT

Library for reading environment variables from an environment file in rust.

Usage

Imagine this to be the content of your environment file located at examples/.env:

ini CLIENT_ID=YOUR_CLIENT_ID CLIENT_SECRET=YOUR_CLIENT_SECRET

Now you want to read this file and expose the environment variables to your rust application. You can easily do this using the env-file-reader crate:

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env")?;

asserteq!(&envvariables["CLIENTID"], "YOURCLIENTID"); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET");

Ok(()) } ```

The env-file-reader crate exposes the read_file function to which you can pass the path to your environment file. The read_file function then parses the environment file and extracts the contained variables, returning them as a HashMap<String, String>, from which they can be accessed easily by your rust application.

Variable names and unicode support

Variables and values support UTF-8. It is perfectly okay to have an environment file that looks like this:

ini πŸ¦„=πŸ’– πŸ’–=πŸ¦„

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.utf8")?;

asserteq!(&envvariables["πŸ¦„"], "πŸ’–"); asserteq!(&envvariables["πŸ’–"], "πŸ¦„");

Ok(()) } ```

Variables (and non-quoted values) support every character except whitespace characters, quotes (`, ', or "), # and =, so go nuts:

```rust use envfilereader::read_str;

fn main() -> std::io::Result<()> { let envvariables = readstr( r"123-variable\$*-@πŸ¦„=sprinkely-sprinkely-πŸ’–s-and🐱s@the🏟️", )?;

asserteq!( &envvariables[r"123-variable\$*-@πŸ¦„"], "sprinkely-sprinkely-πŸ’–s-and🐱s@the🏟️", );

Ok(()) } ```

Optional export keyword

env-file-reader supports bash-like environment files where the variables are exported to the environment via the export command:

bash export CLIENT_ID=YOUR_EXPORTED_CLIENT_ID export CLIENT_SECRET=YOUR_EXPORTED_CLIENT_SECRET

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.exported")?;

asserteq!(&envvariables["CLIENTID"], "YOUREXPORTEDCLIENTID"); asserteq!( &envvariables["CLIENTSECRET"], "YOUREXPORTEDCLIENTSECRET", );

Ok(()) } ```

Reading multiple environment files

Sometimes your environment is split into multiple files (e.g. one environment file with your secrets you want to store in a kubernetes secret and one environment file with non-secrets you want to store in a kubernetes config map). env-file-reader supports reading multiple environment files into one HashMap with all variables with the read_files function:

```rust use envfilereader::read_files;

fn main() -> std::io::Result<()> { let envvariables = readfiles(&[ "examples/.env", "examples/.env.utf8", ])?;

asserteq!(&envvariables["CLIENTID"], "YOURCLIENTID"); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET"); asserteq!(&envvariables["πŸ¦„"], "πŸ’–"); asserteq!(&envvariables["πŸ’–"], "πŸ¦„");

Ok(()) } ```

The environment files are read consecutively in the order they are supplied to read_files. Therefore, variables are overridden by the ones that are defined later:

```rust use envfilereader::read_files;

fn main() -> std::io::Result<()> { let envvariables = readfiles(&[ "examples/.env", "examples/.env.exported", ])?;

asserteq!(&envvariables["CLIENTID"], "YOUREXPORTEDCLIENTID"); asserteq!( &envvariables["CLIENTSECRET"], "YOUREXPORTEDCLIENTSECRET", );

Ok(()) } ```

Reading environment variables from string

Besides read_file and read_files env-file-reader offers the option to read environment variables directly from a string:

```rust use envfilereader::read_str;

const ENVFILE: &str = " CLIENTID=YOURCLIENTID CLIENTSECRET=YOURCLIENT_SECRET ";

fn main() -> std::io::Result<()> { let envvariables = readstr(ENV_FILE)?;

asserteq!(&envvariables["CLIENTID"], "YOURCLIENTID"); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET");

Ok(()) } ```

Comments and empty lines

Environment files can contain single line comments beginning with a # and empty lines. Imagine this to be your environment file located at examples/.env.comments:

```ini

A comment

CLIENTID=YOURCLIENT_ID # A comment at the end of the line

Empty lines are fine, too

Another comment

CLIENTSECRET=YOURCLIENT_SECRET # Another comment behind a value ```

env-file-reader can parse this file, ignoring empty lines and comments:

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.comments")?;

asserteq!(&envvariables["CLIENTID"], "YOURCLIENTID"); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET");

Ok(()) } ```

Quoted and multiline values

If you need a value to be more powerful, e.g. contain whitespaces, quotes, equal sign, etc. (see the section about unicode support), you can wrap them in quotes. The supported quotes are double quotes ("), single quotes (') and backticks (`). A string wrapped in double quotes can contain single quotes and backticks and so on. They also support escaped quotes, so "a string with \"double quotes\"" will work.

ini 1="I support whitespaces and = and # and even this: \"" 2='single quotes work, too and they can contain "double quotes"' 3=`backticks are "also" valid 'quotes'`

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.quotes")?;

asserteq!( &envvariables["1"], "I support whitespaces and = and # and even this: "", ); asserteq!( &envvariables["2"], "single quotes work, too and they can contain "double quotes"", ); asserteq!( &envvariables["3"], "backticks are "also" valid 'quotes'", );

Ok(()) } ```

Multiline strings are supported as well and look like this, either with a literal line break or with an explicitly typed \n:

```ini PRIVATE_KEY1="-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----"

PRIVATEKEY2 is identical to PRIVATEKEY1

PRIVATE_KEY2="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" ```

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.multiline")?;

asserteq!( &envvariables["PRIVATEKEY1"], "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----", ); asserteq!( &envvariables["PRIVATEKEY2"], "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----", );

Ok(()) } ```

Note on escaped characters: A quoted string only supports explicitly typed newlines and the escaped quote itself. Other explicitly typed special characters like \t or \r are not supported. So a value equal to "hello\n\t\"world\"" will have the following value when being processed by env-file-reader

txt hello \t"world"

not

txt hello "world"

If you need support for other explicitly typed special characters, please open an issue.

Whitespaces

Whitespaces around the equal sign are allowed. As are whitespaces before the variable name and after the value. They are trimmed during parsing.

```rust use envfilereader::read_str;

const ENVFILE: &str = " CLIENTID = YOURCLIENTID CLIENTSECRET =YOURCLIENT_SECRET ";

fn main() -> std::io::Result<()> { let envvariables = readstr(ENV_FILE)?;

asserteq!(&envvariables["CLIENTID"], "YOURCLIENTID"); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET");

Ok(()) } ```

If you need leading or trailing whitespaces in your value, consider wrapping it in quotes:

```rust use envfilereader::read_str;

const ENVFILE: &str = " CLIENTID = ' YOURCLIENTID ' CLIENTSECRET =YOURCLIENT_SECRET ";

fn main() -> std::io::Result<()> { let envvariables = readstr(ENV_FILE)?;

asserteq!(&envvariables["CLIENTID"], " YOURCLIENTID "); asserteq!(&envvariables["CLIENTSECRET"], "YOURCLIENTSECRET");

Ok(()) } ```

Empty values

Your variables can be empty:

ini CLIENT_ID= CLIENT_SECRET=

```rust use envfilereader::read_file;

fn main() -> std::io::Result<()> { let envvariables = readfile("examples/.env.empty")?;

asserteq!(&envvariables["CLIENTID"], ""); asserteq!(&envvariables["CLIENTSECRET"], "");

Ok(()) } ```

Errors

Should parsing the environment file fail a std::io::Error is returned. The error includes all normal io mishaps, like a missing file:

```rust use envfilereader::read_file;

let err = read_file(".env.which.does.not.exist") .err() .unwrap();

assert_eq!(err.kind(), std::io::ErrorKind::NotFound); ```

Additionally, parsing can fail due to an ill-formatted environment file. If that is the case, a custom error, ParseError, is returned:

```rust use envfilereader::read_str;

let err = read_str("badly formatted env file") .err() .unwrap();

asserteq!(err.kind(), std::io::ErrorKind::InvalidInput); asserteq!(err.to_string(), "ParseError"); ```