Compare commits

...

4 Commits

9 changed files with 155 additions and 160 deletions

View File

@ -19,7 +19,7 @@ jobs:
- macos-latest
- windows-latest
rust:
- 1.51.0 # msrv
- 1.59.0 # msrv
- stable
- beta
- nightly

6
Cargo.lock generated
View File

@ -4,13 +4,13 @@ version = 3
[[package]]
name = "enve"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"estring",
]
[[package]]
name = "estring"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ea7e904ef7cb950eee02f9332b6a5c24c33bebeca6c027651b04f6c20658d9"
checksum = "515fc26a9876dfa25822d8cc040c6e4497ff9f5be96f101ee136d6ece160d2cc"

View File

@ -1,16 +1,14 @@
[package]
name = "enve"
version = "0.1.0"
version = "0.2.0"
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
description = "it helps you work with environment variables and convert it to any type using only type annotations"
categories = ["config"]
keywords = ["env", "environment"]
edition = "2018"
edition = "2021"
license = "MIT"
repository = "https://github.com/pleshevskiy/enve"
[package.metadata]
msrv = "1.51.0"
rust-version = "1.59.0"
# https://docs.rs/about
[package.metadata.docs.rs]
@ -20,17 +18,21 @@ all-features = true
[features]
default = []
number = ["estring/number"]
bool = ["estring/bool"]
vec = ["estring/vec"]
low-level = ["estring/low-level"]
structs = ["estring/structs"]
# deprecated
number = []
bool = []
vec = ["structs"]
[dependencies]
estring = "0.1"
estring = "0.2"
[badges]
maintenance = { status = "actively-developed" }
[[example]]
name = "calc"
required-features = ["number", "vec"]
required-features = ["structs"]

View File

@ -3,11 +3,11 @@
[![Crates.io](https://img.shields.io/crates/v/enve?style=flat-square)](https://crates.io/crates/enve)
[![docs.rs](https://img.shields.io/docsrs/enve?style=flat-square)](https://docs.rs/enve)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/pleshevskiy/enve/CI?label=tests&logo=github&style=flat-square)](https://github.com/pleshevskiy/enve/actions/workflows/ci.yml)
![The MSRV](https://img.shields.io/badge/MSRV-1.51.0-red.svg)
![The MSRV](https://img.shields.io/badge/MSRV-1.59.0-red.svg)
```toml
[dependencies]
enve = "0.1"
enve = "0.2"
```
`enve` helps you work with environment variables and convert it to **any type**

View File

@ -1,6 +1,5 @@
use crate::error::Error;
use estring::EString;
use std::convert::TryFrom;
use estring::{EString, ParseFragment, ToEString};
/// Fetches the environment variable `key` from the current process. It set value `default`
/// if environment variable `key` ins'n set. Then this function tries to parse ``EString`` to
@ -28,10 +27,10 @@ use std::convert::TryFrom;
#[allow(clippy::needless_pass_by_value)]
pub fn get_or_set_default<R>(env_name: &str, default: R) -> Result<R, Error>
where
R: TryFrom<EString> + std::fmt::Display,
R: ParseFragment + ToEString,
{
get::<R>(env_name).or_else(|err| match err {
Error::NotPresent => sset(env_name, &default).parse().map_err(Error::from),
Error::NotPresent => sset(env_name, default).parse().map_err(Error::from),
_ => Err(err),
})
}
@ -63,7 +62,7 @@ where
/// ```
pub fn get<R>(key: &str) -> Result<R, Error>
where
R: TryFrom<EString>,
R: ParseFragment,
{
sget(key).and_then(|v| v.parse().map_err(Error::from))
}
@ -108,18 +107,20 @@ pub fn sget(key: &str) -> Result<EString, Error> {
/// let estr = enve::sset("KEY", "10");
/// assert_eq!(estr.to_string(), String::from("10"));
/// ```
#[allow(clippy::needless_pass_by_value)]
pub fn sset<V>(key: &str, value: V) -> EString
where
V: std::fmt::Display,
V: ToEString,
{
let val = value.to_string();
std::env::set_var(key, &val);
val.into()
let es = value.to_estring();
std::env::set_var(key, &*es);
es
}
#[cfg(test)]
mod tests {
use super::*;
use crate::estr::{CommaVec, SemiVec, SepVec};
struct TestCase<const N: u8>;
@ -171,150 +172,134 @@ mod tests {
};
}
#[cfg(feature = "number")]
mod numbers {
use super::*;
#[test]
fn should_return_parsed_num() {
let en = TestCase::<4>.to_string();
std::env::set_var(&en, "-10");
match get::<i32>(&en) {
Ok(res) => assert_eq!(res, -10),
_ => unreachable!(),
};
}
#[test]
fn should_throw_parse_error() {
let en = TestCase::<5>.to_string();
std::env::set_var(&en, "-10");
match get::<u32>(&en) {
Err(Error::Parse(orig)) => {
assert_eq!(orig, String::from("-10"));
}
_ => unreachable!(),
};
}
#[test]
fn should_set_default_num_if_var_is_no_present() {
let en = TestCase::<6>.to_string();
let orig = 10;
match get_or_set_default(&en, orig) {
Ok(res) => {
assert_eq!(res, orig);
assert_eq!(std::env::var(&en).unwrap(), "10");
}
_ => unreachable!(),
};
}
#[test]
fn should_return_parsed_num() {
let en = TestCase::<4>.to_string();
std::env::set_var(&en, "-10");
match get::<i32>(&en) {
Ok(res) => assert_eq!(res, -10),
_ => unreachable!(),
};
}
#[cfg(feature = "bool")]
mod boolean {
use super::*;
#[test]
fn should_parse_bool_variable() {
let en = TestCase::<7>.to_string();
let test_cases = [
("1", true),
("y", true),
("yes", true),
("true", true),
("t", true),
("on", true),
("false", false),
("f", false),
("0", false),
];
for (val, expected) in &test_cases {
let mut en = en.clone();
en.push_str(val.as_ref());
std::env::set_var(&en, val);
match get::<bool>(&en) {
Ok(res) => assert_eq!(res, *expected),
_ => unreachable!(),
};
#[test]
fn should_throw_parse_error() {
let en = TestCase::<5>.to_string();
std::env::set_var(&en, "-10");
match get::<u32>(&en) {
Err(Error::Parse(orig)) => {
assert_eq!(orig, EString::from("-10"));
}
}
_ => unreachable!(),
};
}
#[cfg(feature = "vec")]
mod vector {
use super::*;
use crate::estr::{CommaVec, SemiVec, SepVec};
#[test]
fn should_set_default_num_if_var_is_no_present() {
let en = TestCase::<6>.to_string();
let orig = 10;
match get_or_set_default(&en, orig) {
Ok(res) => {
assert_eq!(res, orig);
assert_eq!(std::env::var(&en).unwrap(), "10");
}
_ => unreachable!(),
};
}
#[test]
fn should_return_var_as_vector() {
let en = TestCase::<8>.to_string();
#[test]
fn should_parse_bool_variable() {
let en = TestCase::<7>.to_string();
std::env::set_var(&en, "1,2,3,4,5");
match get::<CommaVec<i32>>(&en) {
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
let test_cases = [
("1", true),
("y", true),
("yes", true),
("true", true),
("t", true),
("on", true),
("false", false),
("f", false),
("0", false),
];
for (val, expected) in test_cases {
let mut en = en.clone();
en.push_str(val.as_ref());
std::env::set_var(&en, val);
match get::<bool>(&en) {
Ok(res) => assert_eq!(res, expected),
_ => unreachable!(),
};
}
}
#[test]
fn should_trim_identations_before_parsing() {
let en = TestCase::<9>.to_string();
#[test]
fn should_return_var_as_vector() {
let en = TestCase::<8>.to_string();
let input = "
std::env::set_var(&en, "1,2,3,4,5");
match get::<CommaVec<i32>>(&en) {
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
_ => unreachable!(),
};
}
#[test]
fn should_trim_identations_before_parsing() {
let en = TestCase::<9>.to_string();
let input = "
1 , 2, 3,
4,5";
std::env::set_var(&en, input);
match get::<CommaVec<i32>>(&en) {
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
_ => unreachable!(),
};
}
std::env::set_var(&en, input);
match get::<CommaVec<i32>>(&en) {
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
_ => unreachable!(),
};
}
#[test]
fn should_return_vector_of_vectors() {
let en = TestCase::<10>.to_string();
#[test]
fn should_return_vector_of_vectors() {
let en = TestCase::<10>.to_string();
std::env::set_var(&en, "1,2; 3,4,5; 6,7");
match get::<SemiVec<CommaVec<i32>>>(&en) {
Ok(res) => assert_eq!(
res,
SemiVec::from(vec![
CommaVec::from(vec![1, 2]),
CommaVec::from(vec![3, 4, 5]),
CommaVec::from(vec![6, 7])
])
),
_ => unreachable!(),
};
}
std::env::set_var(&en, "1,2; 3,4,5; 6,7");
match get::<SemiVec<CommaVec<i32>>>(&en) {
Ok(res) => assert_eq!(
res,
SemiVec::from(vec![
CommaVec::from(vec![1, 2]),
CommaVec::from(vec![3, 4, 5]),
CommaVec::from(vec![6, 7])
])
),
_ => unreachable!(),
};
}
#[test]
fn should_throw_parse_vec_error() {
let en = TestCase::<11>.to_string();
std::env::set_var(&en, "1,2,3,4,5");
match get::<SepVec<i32, '+'>>(&en) {
Err(Error::Parse(orig)) => {
assert_eq!(orig, String::from("1,2,3,4,5"));
}
_ => unreachable!(),
};
}
#[test]
fn should_throw_parse_vec_error() {
let en = TestCase::<11>.to_string();
std::env::set_var(&en, "1,2,3,4,5");
match get::<SepVec<i32, '+'>>(&en) {
Err(Error::Parse(orig)) => {
assert_eq!(orig, EString::from("1,2,3,4,5"));
}
_ => unreachable!(),
};
}
#[test]
fn should_set_default_vector_if_var_is_no_present() {
let en = TestCase::<12>.to_string();
let orig = CommaVec::from(vec![1, 2, 3, 4]);
match get_or_set_default(&en, orig.clone()) {
Ok(res) => {
assert_eq!(res, orig);
assert_eq!(std::env::var(&en).unwrap(), "1,2,3,4");
}
_ => unreachable!(),
};
}
#[test]
fn should_set_default_vector_if_var_is_no_present() {
let en = TestCase::<12>.to_string();
let orig = CommaVec::from(vec![1, 2, 3, 4]);
match get_or_set_default(&en, orig.clone()) {
Ok(res) => {
assert_eq!(res, orig);
assert_eq!(std::env::var(&en).unwrap(), "1,2,3,4");
}
_ => unreachable!(),
};
}
}

View File

@ -3,6 +3,8 @@ use std::error;
use std::ffi::OsString;
use std::fmt;
use estring::EString;
/// The error type for operations interacting with environment variables
#[derive(Debug)]
pub enum Error {
@ -10,7 +12,7 @@ pub enum Error {
NotPresent,
/// Failed to parse the specified environment variable.
Parse(String),
Parse(EString),
/// The specified environment variable was found, but it did not contain
/// valid unicode data. The found data is returned as a payload of this
@ -45,8 +47,8 @@ impl From<VarError> for Error {
}
}
impl From<estring::ParseError> for Error {
fn from(err: estring::ParseError) -> Self {
Error::Parse(err.clone())
impl From<estring::Error> for Error {
fn from(err: estring::Error) -> Self {
Error::Parse(err.0)
}
}

View File

@ -1,6 +1,10 @@
#[cfg(feature = "vec")]
mod vec;
#[cfg(feature = "vec")]
pub use vec::{CommaVec, SemiVec};
#[cfg(feature = "structs")]
mod structs;
#[cfg(feature = "structs")]
pub use structs::{CommaVec, SemiVec};
pub use estring::core::*;
pub use estring::core::EString;
#[cfg(feature = "low-level")]
pub use estring::low::*;
#[cfg(feature = "structs")]
pub use estring::structs::*;

View File

@ -62,3 +62,5 @@ mod estr;
pub use crate::core::{get, get_or_set_default, sget, sset};
pub use error::Error;
pub use estr::*;
pub use estring;