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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
#![doc = include_str!("../README.md")]
use lalrpop_util::lalrpop_mod;
use std::collections::HashMap;
use std::fs;
use std::io::{Error, ErrorKind};
use std::path::Path;
lalrpop_mod!(env_file);
mod lexer;
pub use lexer::ParseError;
/// Parses an environment file that is passed as a `&str`.
///
/// Returns an error if if the string is ill-formatted.
/// Read the [crate's](crate) documentation for information about the
/// format that `env-file-reader` supports.
///
/// **Example:**
///
/// ```rust
/// use env_file_reader::read_str;
///
/// const ENV_FILE: &str = "
/// CLIENT_ID=YOUR_CLIENT_ID
/// CLIENT_SECRET=YOUR_CLIENT_SECRET
/// ";
///
/// fn main() -> std::io::Result<()> {
/// let env_variables = read_str(ENV_FILE)?;
///
/// assert_eq!(&env_variables["CLIENT_ID"], "YOUR_CLIENT_ID");
/// assert_eq!(&env_variables["CLIENT_SECRET"], "YOUR_CLIENT_SECRET");
///
/// Ok(())
/// }
/// ```
///
pub fn read_str(s: &str) -> Result<HashMap<String, String>, Error> {
env_file::EnvFileParser::new()
.parse(lexer::Lexer::new(s))
.map_err(|_| Error::new(ErrorKind::InvalidInput, &ParseError))
}
/// Parses the environment file at the specified `path`.
///
/// Returns an error if reading the file was unsuccessful or if the
/// file is ill-formatted.
/// Read the [crate's](crate) documentation for information about the
/// format that `env-file-reader` supports.
///
/// **Example:**
///
/// `examples/.env`:
///
/// ```ini
/// CLIENT_ID=YOUR_CLIENT_ID
/// CLIENT_SECRET=YOUR_CLIENT_SECRET
/// ```
///
/// ```rust
/// use env_file_reader::read_file;
///
/// fn main() -> std::io::Result<()> {
/// let env_variables = read_file("examples/.env")?;
///
/// assert_eq!(&env_variables["CLIENT_ID"], "YOUR_CLIENT_ID");
/// assert_eq!(&env_variables["CLIENT_SECRET"], "YOUR_CLIENT_SECRET");
///
/// Ok(())
/// }
/// ```
///
pub fn read_file<P: AsRef<Path>>(
path: P,
) -> Result<HashMap<String, String>, Error> {
let content = fs::read_to_string(path)?;
read_str(&content)
}
/// Parses multiple environment files at the specified `paths`
/// and constructs a single [HashMap] with the merged environment
/// variables from the files.
///
/// This is a vectorized version of [read_file], where a single
/// output is constructed from calling [read_file] for each file
/// provided as an argument to this function.
/// The order in which the `paths` are defined is maintained, so
/// if an environment file with a higher index exposes the same
/// environment variable as a file with a lower index, the value of
/// the file with the higher index is exposed by the returned
/// [HashMap].
///
/// Returns an error if reading one file was unsuccessful or if one
/// file is ill-formatted.
/// Read the [crate's](crate) documentation for information about the
/// format that `env-file-reader` supports.
///
/// **Example:**
///
/// `examples/.env`:
///
/// ```ini
/// CLIENT_ID=YOUR_CLIENT_ID
/// CLIENT_SECRET=YOUR_CLIENT_SECRET
/// ```
///
/// `examples/.env.utf8`:
///
/// ```ini
/// 🦄=💖
/// 💖=🦄
/// ```
///
/// ```rust
/// use env_file_reader::read_files;
///
/// fn main() -> std::io::Result<()> {
/// let env_variables = read_files(&[
/// "examples/.env",
/// "examples/.env.utf8",
/// ])?;
///
/// assert_eq!(&env_variables["CLIENT_ID"], "YOUR_CLIENT_ID");
/// assert_eq!(&env_variables["CLIENT_SECRET"], "YOUR_CLIENT_SECRET");
/// assert_eq!(&env_variables["🦄"], "💖");
/// assert_eq!(&env_variables["💖"], "🦄");
///
/// Ok(())
/// }
/// ```
///
pub fn read_files<P: AsRef<Path>>(
paths: &[P],
) -> Result<HashMap<String, String>, Error> {
let mut res = HashMap::new();
for path in paths {
res.extend(read_file(path)?);
}
Ok(res)
}
#[cfg(test)]
mod test {
use super::read_str;
#[test]
fn wrong_idents() {
let s = "key_with#=x";
assert!(read_str(s).is_err());
let s = "key_with`quotes`=x";
assert!(read_str(s).is_err());
let s = "key_with=do-not-work=x";
assert!(read_str(s).is_err());
let s = "key with whitespace=x";
assert!(read_str(s).is_err());
}
#[test]
fn comments() {
let s = "
# a comment
key=val # a comment at the end of the line
";
let m = read_str(s).unwrap();
assert_eq!(&m["key"], "val");
}
#[test]
fn empty_value() {
let s = "
key1=
key2=something
key3=";
let m = read_str(s).unwrap();
assert_eq!(&m["key1"], "");
assert_eq!(&m["key2"], "something");
assert_eq!(&m["key3"], "");
}
#[test]
fn empty_string() {
let m = read_str("").unwrap();
assert_eq!(m.len(), 0);
}
}