Library for reading environment variables from an environment file in rust.
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.
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(()) } ```
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(()) } ```
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(()) } ```
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(()) } ```
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
CLIENTID=YOURCLIENT_ID # A comment at the end of the line
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(()) } ```
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-----"
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 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(()) } ```
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(()) } ```
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"); ```