Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
bced02a1fd | |||
2171d6b6a7 | |||
a8c124aabc | |||
7b1746ef83 | |||
04b777e214 | |||
332b14bcd9 | |||
71d6fe42e8 | |||
bc7ab940b0 | |||
9448c26337 | |||
21e6aba270 |
30 changed files with 457 additions and 2382 deletions
3
.vim/coc-settings.json
Normal file
3
.vim/coc-settings.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.cargo.features": "all"
|
||||
}
|
1371
Cargo.lock
generated
1371
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
48
Cargo.toml
48
Cargo.toml
|
@ -1,7 +1,49 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"itconfig",
|
||||
"itconfig-macro",
|
||||
"itconfig-tests",
|
||||
"estring",
|
||||
"enve_mod",
|
||||
]
|
||||
|
||||
[package]
|
||||
name = "enve"
|
||||
version = "1.1.1"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
description = "Easy build a configs from environment variables and use it in globally."
|
||||
categories = ["config", "web-programming"]
|
||||
keywords = ["config", "env", "configuration", "environment", "macro"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/pleshevskiy/enve"
|
||||
homepage = "https://github.com/pleshevskiy/enve"
|
||||
documentation = "https://docs.rs/enve"
|
||||
readme = "../README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = []
|
||||
number = ["estring/number"]
|
||||
bool = ["estring/bool"]
|
||||
vec = ["estring/vec"]
|
||||
|
||||
macro = ["enve_mod"]
|
||||
|
||||
[dependencies]
|
||||
estring = "0.1"
|
||||
enve_mod = { version = "1.1", path = "./enve_mod", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
criterion = "0.3.1"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
# https://docs.rs/about
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[[example]]
|
||||
name = "calc"
|
||||
required-features = ["number", "vec"]
|
||||
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 IceTemple
|
||||
Copyright (c) 2019-2022 pleshevskiy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
56
README.md
56
README.md
|
@ -1,10 +1,10 @@
|
|||
# itconfig
|
||||
# enve
|
||||
|
||||
[![CI](https://github.com/icetemple/itconfig-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/icetemple/itconfig-rs/actions/workflows/ci.yml)
|
||||
[![CI](https://github.com/pleshevskiy/enve/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pleshevskiy/enve/actions/workflows/ci.yml)
|
||||
[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
|
||||
[![Documentation](https://docs.rs/itconfig/badge.svg)](https://docs.rs/itconfig)
|
||||
[![Crates.io](https://img.shields.io/crates/v/itconfig)](https://crates.io/crates/itconfig)
|
||||
![Crates.io](https://img.shields.io/crates/l/itconfig)
|
||||
[![Documentation](https://docs.rs/pleshevskiy/badge.svg)](https://docs.rs/enve)
|
||||
[![Crates.io](https://img.shields.io/crates/v/enve)](https://crates.io/crates/enve)
|
||||
![Crates.io](https://img.shields.io/crates/l/enve)
|
||||
|
||||
Easy build a configs from environment variables and use it in globally.
|
||||
|
||||
|
@ -23,7 +23,7 @@ of it I decided to create my own library.
|
|||
|
||||
The MSRV is 1.39.0
|
||||
|
||||
Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in
|
||||
Add `enve = { version = "1.0", features = ["mod"] }` as a dependency in
|
||||
`Cargo.toml`.
|
||||
|
||||
`Cargo.toml` example:
|
||||
|
@ -35,17 +35,16 @@ version = "0.1.0"
|
|||
authors = ["Me <user@rust-lang.org>"]
|
||||
|
||||
[dependencies]
|
||||
itconfig = { version = "1.0", features = ["macro"] }
|
||||
enve = { version = "1.0", features = ["mod"] }
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
```rust
|
||||
use itconfig::config;
|
||||
use std::env;
|
||||
//use dotenv::dotenv;
|
||||
|
||||
config! {
|
||||
enve::mod! {
|
||||
DEBUG: bool => false,
|
||||
|
||||
#[env_name = "APP_HOST"]
|
||||
|
@ -97,7 +96,6 @@ Macro is an optional feature, disabled by default. You can use this library
|
|||
without macro
|
||||
|
||||
```rust
|
||||
use itconfig::*;
|
||||
use std::env;
|
||||
// use dotenv::dotenv;
|
||||
|
||||
|
@ -106,9 +104,9 @@ fn main() {
|
|||
// or
|
||||
env::set_var("DATABASE_URL", "postgres://127.0.0.1:5432/test");
|
||||
|
||||
let database_url = get_env::<String>("DATABASE_URL").unwrap();
|
||||
let new_profile: bool = get_env_or_default("FEATURE_NEW_PROFILE", false);
|
||||
let articles_per_page: u32 = get_env_or_set_default("ARTICLES_PER_PAGE", 10);
|
||||
let database_url = enve::get::<String>("DATABASE_URL").unwrap();
|
||||
let new_profile: bool = enve::get("FEATURE_NEW_PROFILE").unwrap_or_default();
|
||||
let articles_per_page: u32 = enve::get_or_set_default("ARTICLES_PER_PAGE", 10);
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -120,39 +118,19 @@ cargo test --all-features
|
|||
|
||||
## Available features
|
||||
|
||||
- **default** - ["primitives"]
|
||||
- **macro** - Activates `config!` macros for easy configure web application.
|
||||
- **primitives** - Group for features: `numbers` and `bool`.
|
||||
- **numbers** - Group for features: `int`, `uint` and `float`.
|
||||
- **int** - Group for features: `i8`, `i16`, `i32`, `i64`, `i128` and `isize`.
|
||||
- **uint** - Group for features: `u8`, `u16`, `u32`, `u64`, `u128` and `usize`.
|
||||
- **float** - Group for features: `f32` and `f64`
|
||||
- **i8** - impl EnvString for `i8` type
|
||||
- **i16** - impl EnvString for `i16` type
|
||||
- **i32** - impl EnvString for `i32` type
|
||||
- **i64** - impl EnvString for `i64` type
|
||||
- **i128** - impl EnvString for `i128` type
|
||||
- **isize** - impl EnvString for `isize` type
|
||||
- **u8** - impl EnvString for `u8` type
|
||||
- **u16** - impl EnvString for `u16` type
|
||||
- **u32** - impl EnvString for `u32` type
|
||||
- **u64** - impl EnvString for `u64` type
|
||||
- **u128** - impl EnvString for `u128` type
|
||||
- **usize** - impl EnvString for `usize` type
|
||||
- **f32** - impl EnvString for `f32` type
|
||||
- **f64** - impl EnvString for `f64` type
|
||||
- **bool** - impl EnvString for `bool` type
|
||||
- **json_array** - Add EnvString impl for vector type (uses optional
|
||||
`serde_json` package). ⚠ **_DEPRECATED_**
|
||||
- **number** - Group for features: `int`, `uint` and `float`.
|
||||
- **bool** - impl EnvString for `bool` type `serde_json` package). ⚠
|
||||
**_DEPRECATED_**
|
||||
|
||||
## License
|
||||
|
||||
[MIT] © [Ice Temple](https://github.com/icetemple)
|
||||
[MIT] © [pleshevskiy](https://github.com/pleshevskiy)
|
||||
|
||||
## Contributors
|
||||
|
||||
[pleshevskiy](https://github.com/pleshevskiy) (Dmitriy Pleshevskiy) – creator,
|
||||
maintainer.
|
||||
|
||||
[documentation]: https://docs.rs/itconfig
|
||||
[MIT]: https://github.com/icetemple/itconfig-rs/blob/master/LICENSE
|
||||
[documentation]: https://docs.rs/enve
|
||||
[MIT]: https://github.com/icetemple/enve-rs/blob/master/LICENSE
|
||||
|
|
|
@ -4,14 +4,14 @@ use std::env;
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate itconfig;
|
||||
extern crate enve;
|
||||
|
||||
fn setup_env_var(key: &'static str, initial: String) {
|
||||
env::set_var(key, initial);
|
||||
}
|
||||
|
||||
fn source_get_env() -> u32 {
|
||||
itconfig::get_env::<u32>("TEST").unwrap()
|
||||
enve::get::<u32>("TEST").unwrap()
|
||||
}
|
||||
|
||||
fn lazy_get_env() -> u32 {
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "itconfig-macro"
|
||||
name = "enve_mod"
|
||||
version = "1.1.1"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
description = "Easy build a configs from environment variables and use it in globally."
|
||||
|
@ -7,9 +7,9 @@ categories = ["config", "web-programming"]
|
|||
keywords = ["config", "env", "configuration", "environment", "macro"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/icetemple/itconfig-rs"
|
||||
homepage = "https://github.com/icetemple/itconfig-rs"
|
||||
documentation = "https://docs.rs/itconfig"
|
||||
repository = "https://github.com/pleshevskiy/enve"
|
||||
homepage = "https://github.com/pleshevskiy/enve"
|
||||
documentation = "https://docs.rs/enve"
|
||||
readme = "../README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -22,9 +22,8 @@ quote = "1.0.9"
|
|||
proc-macro2 = "1.0.24"
|
||||
|
||||
[dev-dependencies]
|
||||
itconfig = { path = "../itconfig" }
|
||||
enve = { path = ".." }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "icetemple/itconfig-rs" }
|
||||
maintenance = { status = "passively-maintained" }
|
||||
maintenance = { status = "actively-developed" }
|
15
examples/README.md
Normal file
15
examples/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Examples
|
||||
|
||||
## Calculator
|
||||
|
||||
Fun calculator based on environment variables
|
||||
|
||||
```
|
||||
E=2*2-1-1+5*3-10 cargo run --example calc --all-features
|
||||
```
|
||||
|
||||
Limits:
|
||||
|
||||
- Supports `*`, `+`, `-`
|
||||
- You cannot start from a negative number. `E=-10`. Solution: start from `0`.
|
||||
`E=0-10`.
|
36
examples/calc.rs
Normal file
36
examples/calc.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use enve::estr::SepVec;
|
||||
|
||||
type MinusVec<T> = SepVec<T, '-'>;
|
||||
type PlusVec<T> = SepVec<T, '+'>;
|
||||
type MulVec<T> = SepVec<T, '*'>;
|
||||
|
||||
const HELP_MESSAGE: &str = "
|
||||
USAGE:
|
||||
E=10+10*2+4 cargo run --example calc --all-features
|
||||
";
|
||||
|
||||
fn main() -> Result<(), enve::Error> {
|
||||
let res: f32 = enve::get::<PlusVec<MinusVec<MulVec<f32>>>>("E")
|
||||
.map_err(|err| {
|
||||
match err {
|
||||
enve::Error::NotPresent => eprintln!("The expression was not found"),
|
||||
rest => eprintln!("ERROR: {}", rest),
|
||||
}
|
||||
|
||||
eprintln!("{}", HELP_MESSAGE);
|
||||
std::process::exit(0);
|
||||
})
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|p| {
|
||||
p.iter()
|
||||
.map(|m| m.iter().product::<f32>())
|
||||
.reduce(|acc, v| acc - v)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.sum::<f32>();
|
||||
|
||||
println!("result: {}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
use std::convert::Infallible;
|
||||
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
|
||||
itconfig::config! {
|
||||
hyper {
|
||||
static HOST < (
|
||||
ADDR => "127.0.0.1",
|
||||
":",
|
||||
PORT => 8000,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
|
||||
Ok(Response::new(Body::from("Hello World!")))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
config::init();
|
||||
pretty_env_logger::init();
|
||||
|
||||
// For every connection, we must make a `Service` to handle all
|
||||
// incoming HTTP requests on said connection.
|
||||
let make_svc = make_service_fn(|_conn| {
|
||||
// This is the `Service` that will handle the connection.
|
||||
// `service_fn` is a helper to convert a function that
|
||||
// returns a Response into a `Service`.
|
||||
async { Ok::<_, Infallible>(service_fn(hello)) }
|
||||
});
|
||||
|
||||
let addr = config::hyper::HOST().parse()?;
|
||||
|
||||
let server = Server::bind(&addr).serve(make_svc);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
server.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
# Hyper "hello world"
|
||||
|
||||
```bash
|
||||
cargo run --example hyper
|
||||
```
|
||||
|
||||
# Rocket "hello world"
|
||||
|
||||
```bash
|
||||
cargo run --example roket
|
||||
```
|
|
@ -1,22 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
itconfig::config! {
|
||||
rocket {
|
||||
HOST: String => "localhost",
|
||||
PORT: u16 => 9000,
|
||||
BASE_URL => "/",
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
config::init();
|
||||
|
||||
rocket::build().mount(config::rocket::BASE_URL(), routes![hello])
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "itconfig_tests"
|
||||
version = "0.1.0"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
itconfig = { path = '../itconfig', features = ["macro"] }
|
||||
criterion = "0.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = ["meta_namespace"]
|
||||
meta_namespace = []
|
||||
|
||||
[[bench]]
|
||||
name = "main_benches"
|
||||
harness = false
|
|
@ -1,81 +0,0 @@
|
|||
[package]
|
||||
name = "itconfig"
|
||||
version = "1.1.1"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
description = "Easy build a configs from environment variables and use it in globally."
|
||||
categories = ["config", "web-programming"]
|
||||
keywords = ["config", "env", "configuration", "environment", "macro"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/icetemple/itconfig-rs"
|
||||
homepage = "https://github.com/icetemple/itconfig-rs"
|
||||
documentation = "https://docs.rs/itconfig"
|
||||
readme = "../README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["primitives"]
|
||||
|
||||
macro = ["itconfig-macro"]
|
||||
|
||||
primitives = ["numbers", "bool"]
|
||||
numbers = ["int", "uint", "float"]
|
||||
int = ["i8", "i16", "i32", "i64", "i128", "isize"]
|
||||
uint = ["u8", "u16", "u32", "u64", "u128", "usize"]
|
||||
float = ["f32", "f64"]
|
||||
|
||||
i8 = []
|
||||
i16 = []
|
||||
i32 = []
|
||||
i64 = []
|
||||
i128 = []
|
||||
isize = []
|
||||
|
||||
u8 = []
|
||||
u16 = []
|
||||
u32 = []
|
||||
u64 = []
|
||||
u128 = []
|
||||
usize = []
|
||||
|
||||
f32 = []
|
||||
f64 = []
|
||||
|
||||
bool = []
|
||||
|
||||
# deprecated since 1.1
|
||||
json_array = ["serde_json"]
|
||||
|
||||
[dependencies]
|
||||
serde_json = { version = "1", optional = true }
|
||||
itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
# required for examples
|
||||
rocket = "0.5.0-rc.2"
|
||||
hyper = { version = "0.14.4", features = ["full"] }
|
||||
serde_json = "1.0.62"
|
||||
tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread"] }
|
||||
bytes = "1.0.1"
|
||||
futures-util = { version = "0.3.13", default-features = false }
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
# https://docs.rs/about
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[[example]]
|
||||
name = "hyper"
|
||||
path = "../examples/hyper.rs"
|
||||
required-features = ["macro"]
|
||||
|
||||
[[example]]
|
||||
name = "rocket"
|
||||
path = "../examples/rocket.rs"
|
||||
required-features = ["macro"]
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
/// Wrapper under String type.
|
||||
///
|
||||
/// When we read the environment variable, we automatically convert the value
|
||||
/// to EnvString and then convert it to your expected type.
|
||||
///
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||
pub struct EnvString(String);
|
||||
|
||||
impl<T> From<T> for EnvString
|
||||
where
|
||||
T: ToEnvString,
|
||||
{
|
||||
fn from(val: T) -> Self {
|
||||
val.to_env_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EnvString {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting value to EnvString.
|
||||
///
|
||||
/// This trait automatically implemented for any type which implements the
|
||||
/// [`Display`] trait. As such, `ToEnvString` shouldn't be implemented directly:
|
||||
/// [`Display`] should be implemented instead, and you get the `ToEnvString`
|
||||
/// implementation for free.
|
||||
///
|
||||
/// [`Display`]: std::fmt::Display
|
||||
pub trait ToEnvString {
|
||||
/// Converts the giving value to a `EnvString`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// basic usage
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::{EnvString, ToEnvString};
|
||||
/// let i = 5;
|
||||
/// let five = EnvString::from("5");
|
||||
/// assert_eq!(five, i.to_env_string());
|
||||
/// ```
|
||||
fn to_env_string(&self) -> EnvString;
|
||||
}
|
||||
|
||||
/// Simple and safe type conversions that may fail in a controlled way under
|
||||
/// some circumstances.
|
||||
///
|
||||
/// This trait automatically implemented for all standard primitives. If you
|
||||
/// want to use your custom type in the library you need to implement
|
||||
/// `ToEnvString` and `FromEnvString` manually.
|
||||
pub trait FromEnvString: Sized {
|
||||
/// The type returned in the event of a conversion error.
|
||||
type Err;
|
||||
|
||||
/// Performs the conversion.
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
impl<T> ToEnvString for T
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
#[inline]
|
||||
fn to_env_string(&self) -> EnvString {
|
||||
EnvString(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! from_env_string_numbers_impl {
|
||||
($($ty:ty => $feature:expr),+) => {
|
||||
$(
|
||||
#[cfg(feature = $feature)]
|
||||
impl FromEnvString for $ty {
|
||||
type Err = <$ty as std::str::FromStr>::Err;
|
||||
|
||||
#[inline]
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
||||
s.0.parse::<Self>()
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
from_env_string_numbers_impl![
|
||||
i8 => "i8",
|
||||
i16 => "i16",
|
||||
i32 => "i32",
|
||||
i64 => "i64",
|
||||
i128 => "i128",
|
||||
isize => "isize",
|
||||
u8 => "u8",
|
||||
u16 => "u16",
|
||||
u32 => "u32",
|
||||
u64 => "u64",
|
||||
u128 => "u128",
|
||||
usize => "usize",
|
||||
f32 => "f32",
|
||||
f64 => "f64"
|
||||
];
|
||||
|
||||
#[cfg(feature = "bool")]
|
||||
impl FromEnvString for bool {
|
||||
type Err = ();
|
||||
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"true" | "t" | "yes" | "y" | "on" | "1" => Ok(true),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromEnvString for String {
|
||||
type Err = ();
|
||||
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
||||
Ok(s.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromEnvString for &'static str {
|
||||
type Err = ();
|
||||
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
||||
Ok(Box::leak(s.0.clone().into_boxed_str()))
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// DEPRECATED //
|
||||
//===========================================================================//
|
||||
|
||||
/// Error type for json array implementation
|
||||
#[cfg(feature = "json_array")]
|
||||
#[derive(Debug)]
|
||||
#[deprecated(since = "1.1.0")]
|
||||
pub enum ArrayEnvError {
|
||||
/// Invalid type.
|
||||
InvalidType,
|
||||
|
||||
/// Failed to parse environment variable
|
||||
FailedToParse,
|
||||
}
|
||||
|
||||
#[cfg(feature = "json_array")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> FromEnvString for Vec<T>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
type Err = ArrayEnvError;
|
||||
|
||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str::<Vec<isize>>(s.trim())
|
||||
.map(|vec| vec.iter().map(|v| v.to_string()).collect::<Vec<String>>())
|
||||
.or_else(|_| serde_json::from_str::<Vec<String>>(s.trim()))
|
||||
.map_err(|_| ArrayEnvError::InvalidType)
|
||||
.and_then(|vec| {
|
||||
vec.iter()
|
||||
.map(|v| {
|
||||
FromEnvString::from_env_string(&v.to_env_string())
|
||||
.map_err(|_| ArrayEnvError::FailedToParse)
|
||||
})
|
||||
.collect::<Result<Vec<T>, _>>()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
/// The error type for operations interacting with environment variables
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EnvError {
|
||||
/// The specified environment variable was not present in the current process's environment.
|
||||
MissingVariable(String),
|
||||
|
||||
/// Failed to parse the specified environment variable.
|
||||
FailedToParse(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for EnvError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self {
|
||||
EnvError::MissingVariable(env_name) => {
|
||||
write!(f, r#"Environment variable "{}" is missing"#, env_name)
|
||||
}
|
||||
EnvError::FailedToParse(env_name) => {
|
||||
write!(f, r#"Failed to parse environment variable "{}""#, env_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for EnvError {}
|
|
@ -1,240 +0,0 @@
|
|||
use crate::envstr::*;
|
||||
use crate::error::*;
|
||||
use crate::utils::*;
|
||||
use std::env;
|
||||
|
||||
/// Same as get_env but returns Option enum instead Result
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::maybe_get_env;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// env::set_var("HOST", "https://example.com");
|
||||
///
|
||||
/// let host: Option<&'static str> = maybe_get_env("HOST");
|
||||
/// let not_existence_host: Option<&'static str> = maybe_get_env("NOT_EXISTENCE_HOST");
|
||||
///
|
||||
/// assert_eq!(host, Some("https://example.com"));
|
||||
/// assert_eq!(not_existence_host, None);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn maybe_get_env<T>(env_name: &str) -> Option<T>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_env(env_name).ok()
|
||||
}
|
||||
|
||||
/// This function is similar as `get_env`, but it unwraps result with panic on error.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if environment variable is missing or cannot parse variable to
|
||||
/// expected type
|
||||
///
|
||||
pub fn get_env_or_panic<T>(env_name: &str) -> T
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_env(env_name).unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// Try to read environment variable and parse to expected type. You may to put to argument
|
||||
/// any type with `FromEnvString` trait.
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_env;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// env::set_var("DEBUG", "true");
|
||||
///
|
||||
/// let result: bool = get_env("DEBUG").unwrap();
|
||||
///
|
||||
/// assert_eq!(result, true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_env<T>(env_name: &str) -> Result<T, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_env_or(env_name, |_| {
|
||||
Err(EnvError::MissingVariable(env_name.to_string()))
|
||||
})
|
||||
}
|
||||
|
||||
/// This function is similar as `get_env_or_panic`, but you can pass default value for
|
||||
/// environment variable with `ToEnvString` trait.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if cannot parse variable to expected type
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_env_or_default;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let result: bool = get_env_or_default("TESTING", "true");
|
||||
/// assert_eq!(result, true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_env_or_default<T, D>(env_name: &str, default: D) -> T
|
||||
where
|
||||
T: FromEnvString,
|
||||
D: ToEnvString,
|
||||
{
|
||||
get_env_or(env_name, |_| Ok(default.to_env_string())).unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// This function is similar as `get_env_or_default`, but the default value will be set to environment
|
||||
/// variable, if env variable is missed.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if cannot parse variable to expected type
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_env_or_set_default;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let result: bool = get_env_or_set_default("TESTING", "true");
|
||||
/// assert_eq!(result, true);
|
||||
///
|
||||
/// let var = env::var("TESTING").unwrap();
|
||||
/// assert_eq!(var, "true");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_env_or_set_default<T, D>(env_name: &str, default: D) -> T
|
||||
where
|
||||
T: FromEnvString,
|
||||
D: ToEnvString,
|
||||
{
|
||||
get_env_or(env_name, |_| {
|
||||
let val = default.to_env_string();
|
||||
env::set_var(env_name, val.as_str());
|
||||
Ok(val)
|
||||
})
|
||||
.unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
||||
/// default expression. Callback should return `EnvString` value or `EnvError`
|
||||
pub fn get_env_or<T, F>(env_name: &str, cb: F) -> Result<T, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
F: FnOnce(env::VarError) -> Result<EnvString, EnvError>,
|
||||
{
|
||||
env::var(env_name)
|
||||
.map(|s| s.to_env_string())
|
||||
.or_else(cb)
|
||||
.and_then(|env_str| parse_env_variable(env_name, env_str))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"TEST_CASE_1\" is missing")]
|
||||
fn get_missing_env() {
|
||||
get_env_or_panic::<String>("TEST_CASE_1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_2\"")]
|
||||
fn get_env_with_invalid_value() {
|
||||
let env_name = "TEST_CASE_2";
|
||||
env::set_var(&env_name, "30r");
|
||||
get_env_or_panic::<u32>(env_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_missing_env() {
|
||||
let env_name = String::from("TEST_CASE_3");
|
||||
let env_val = get_env::<String>(&env_name);
|
||||
assert_eq!(env_val, Err(EnvError::MissingVariable(env_name)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_env_with_invalid_value() {
|
||||
let env_name = String::from("TEST_CASE_4");
|
||||
env::set_var(&env_name, "30r");
|
||||
let env_val = get_env::<u32>(&env_name);
|
||||
assert_eq!(env_val, Err(EnvError::FailedToParse(env_name)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_env_successfully() {
|
||||
env::set_var("TEST_CASE_5", "30");
|
||||
let env_var = get_env("TEST_CASE_5");
|
||||
assert_eq!(env_var, Ok(30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_missing_env_with_default_value() {
|
||||
let flag: bool = get_env_or_default("TEST_CASE_6", "true");
|
||||
assert!(flag);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_7\"")]
|
||||
fn get_invalid_env_with_default_value() {
|
||||
env::set_var("TEST_CASE_7", "30r");
|
||||
get_env_or_default::<u32, _>("TEST_CASE_7", 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_8\"")]
|
||||
fn get_env_with_invalid_default_value() {
|
||||
get_env_or_default::<u32, _>("TEST_CASE_8", "30r");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_env_with_default_successfully() {
|
||||
env::set_var("TEST_CASE_9", "10");
|
||||
let env_val: u32 = get_env_or_default("TEST_CASE_9", 30);
|
||||
assert_eq!(env_val, 10)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_missing_env_with_set_default_value() {
|
||||
let flag: bool = get_env_or_set_default("TEST_CASE_10", "true");
|
||||
assert!(flag);
|
||||
|
||||
let env_var = env::var("TEST_CASE_10");
|
||||
assert_eq!(env_var, Ok(String::from("true")))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_optional_env() {
|
||||
env::set_var("TEST_CASE_11", "something");
|
||||
let something: Option<&'static str> = maybe_get_env("TEST_CASE_11");
|
||||
assert_eq!(something, Some("something"));
|
||||
|
||||
let nothing: Option<&'static str> = maybe_get_env("TEST_CASE_11_NONE");
|
||||
assert_eq!(nothing, None);
|
||||
}
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
use crate::envstr::*;
|
||||
use crate::error::*;
|
||||
use crate::utils::*;
|
||||
use std::env;
|
||||
|
||||
/// Same as get_vec_env but returns Option enum instead Result
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::*;
|
||||
/// use std::env;
|
||||
///
|
||||
/// #[derive(Debug, PartialEq, Eq)]
|
||||
/// enum PaymentPlatform {
|
||||
/// PayPal,
|
||||
/// Stripe,
|
||||
/// SomethingElse,
|
||||
/// }
|
||||
///
|
||||
/// impl FromEnvString for PaymentPlatform {
|
||||
/// type Err = &'static str;
|
||||
///
|
||||
/// fn from_env_string(envstr: &EnvString) -> Result<Self, Self::Err> {
|
||||
/// match envstr.to_lowercase().as_str() {
|
||||
/// "paypal" => Ok(Self::PayPal),
|
||||
/// "stripe" => Ok(Self::Stripe),
|
||||
/// "smth" => Ok(Self::SomethingElse),
|
||||
/// _ => Err("Unsupported payment platform"),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// fn main () {
|
||||
/// env::set_var("PAYMENT_PLATFORMS", "paypal,stripe");
|
||||
///
|
||||
/// let payment_platforms: Option<Vec<PaymentPlatform>> = maybe_get_vec_env("PAYMENT_PLATFORMS", ",");
|
||||
/// assert_eq!(
|
||||
/// payment_platforms,
|
||||
/// Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe])
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn maybe_get_vec_env<T>(env_name: &str, sep: &'static str) -> Option<Vec<T>>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_vec_env(env_name, sep).ok()
|
||||
}
|
||||
|
||||
/// This function is similar as `get_vec_env`, but it unwraps result with panic on error.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if environment variable is missing or cannot parse variable to
|
||||
/// expected type
|
||||
///
|
||||
pub fn get_vec_env_or_panic<T>(env_name: &str, sep: &'static str) -> Vec<T>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_vec_env(env_name, sep).unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// Try to read environment variable, split by separator and parse each item to expected
|
||||
/// type.
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_vec_env;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// env::set_var("DEBUG", "true");
|
||||
///
|
||||
/// let result: Vec<bool> = get_vec_env("DEBUG", ",").unwrap();
|
||||
///
|
||||
/// assert_eq!(result, vec![true]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_vec_env<T>(env_name: &str, sep: &str) -> Result<Vec<T>, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
get_vec_env_or(env_name, sep, |_| {
|
||||
Err(EnvError::MissingVariable(env_name.to_string()))
|
||||
})
|
||||
}
|
||||
|
||||
/// This function is similar as `get_vec_env_or_panic`, but you can pass default value for
|
||||
/// environment variable with `ToEnvString` trait.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if cannot parse variable to expected type
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_vec_env_or_default;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let result: Vec<bool> = get_vec_env_or_default("TESTING", ",", vec!["true"]);
|
||||
/// assert_eq!(result, vec![true]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_vec_env_or_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
||||
where
|
||||
T: FromEnvString,
|
||||
D: ToEnvString,
|
||||
{
|
||||
get_vec_env_or(env_name, sep, |_| Ok(vec_to_env_strings(default))).unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// This function is similar as `get_vec_env_or_default`, but the default value will be set to environment
|
||||
/// variable, if env variable is missed.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
/// Application will panic if cannot parse variable to expected type
|
||||
///
|
||||
/// Example
|
||||
/// -------
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate itconfig;
|
||||
/// # use itconfig::get_vec_env_or_set_default;
|
||||
/// use std::env;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let result: Vec<bool> = get_vec_env_or_set_default("TESTING", ",", vec!["true"]);
|
||||
/// assert_eq!(result, vec![true]);
|
||||
///
|
||||
/// let var = env::var("TESTING").unwrap();
|
||||
/// assert_eq!(var, "true");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn get_vec_env_or_set_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
||||
where
|
||||
T: FromEnvString,
|
||||
D: ToEnvString,
|
||||
{
|
||||
get_vec_env_or(env_name, sep, |_| {
|
||||
let default_env_strings = vec_to_env_strings(default);
|
||||
let env_val = join(&default_env_strings, sep);
|
||||
env::set_var(env_name, env_val.as_str());
|
||||
Ok(default_env_strings)
|
||||
})
|
||||
.unwrap_or_else(make_panic)
|
||||
}
|
||||
|
||||
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
||||
/// default expression. Callback should return `EnvString` value or `EnvError`
|
||||
pub fn get_vec_env_or<T, F>(env_name: &str, sep: &str, cb: F) -> Result<Vec<T>, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
F: FnOnce(env::VarError) -> Result<Vec<EnvString>, EnvError>,
|
||||
{
|
||||
env::var(env_name)
|
||||
.map(|s| {
|
||||
s.split(sep)
|
||||
.into_iter()
|
||||
.map(|item| item.to_env_string())
|
||||
.collect()
|
||||
})
|
||||
.or_else(cb)
|
||||
.and_then(|items| {
|
||||
items
|
||||
.into_iter()
|
||||
.map(|env_str| parse_env_variable(env_name, env_str))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SEP: &str = ",";
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"TEST_CASE_VEC_1\" is missing")]
|
||||
fn get_missing_vec_env() {
|
||||
let _: Vec<&'static str> = get_vec_env_or_panic("TEST_CASE_VEC_1", SEP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_2\"")]
|
||||
fn get_vec_env_with_invalid_value() {
|
||||
let env_name = "TEST_CASE_VEC_2";
|
||||
env::set_var(&env_name, "30r");
|
||||
let _: Vec<u32> = get_vec_env_or_panic(env_name, SEP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_missing_vec_env() {
|
||||
let env_name = String::from("TEST_CASE_VEC_3");
|
||||
let env_val = get_vec_env::<String>(&env_name, SEP);
|
||||
assert_eq!(env_val, Err(EnvError::MissingVariable(env_name)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_vec_env_with_invalid_value() {
|
||||
let env_name = String::from("TEST_CASE_VEC_4");
|
||||
env::set_var(&env_name, "30r");
|
||||
let env_val = get_vec_env::<u32>(&env_name, SEP);
|
||||
assert_eq!(env_val, Err(EnvError::FailedToParse(env_name)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_result_of_vec_env_successfully() {
|
||||
env::set_var("TEST_CASE_VEC_5", "30");
|
||||
let env_var = get_vec_env("TEST_CASE_VEC_5", SEP);
|
||||
assert_eq!(env_var, Ok(vec![30]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_missing_vec_env_with_default_value() {
|
||||
let flag: Vec<bool> = get_vec_env_or_default("TEST_CASE_VEC_6", SEP, vec!["true"]);
|
||||
assert_eq!(flag, vec![true]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_7\"")]
|
||||
fn get_invalid_vec_env_with_default_value() {
|
||||
env::set_var("TEST_CASE_VEC_7", "30r");
|
||||
get_vec_env_or_default::<u32, _>("TEST_CASE_VEC_7", SEP, vec![30]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_8\"")]
|
||||
fn get_vec_env_with_invalid_default_value() {
|
||||
get_vec_env_or_default::<u32, _>("TEST_CASE_VEC_8", SEP, vec!["30r"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_vec_env_with_default_successfully() {
|
||||
env::set_var("TEST_CASE_VEC_9", "10");
|
||||
let env_val: Vec<u32> = get_vec_env_or_default("TEST_CASE_VEC_9", SEP, vec![30]);
|
||||
assert_eq!(env_val, vec![10])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_missing_vec_env_with_set_default_value() {
|
||||
let flag: Vec<bool> = get_vec_env_or_set_default("TEST_CASE_VEC_10", SEP, vec!["true"]);
|
||||
assert_eq!(flag, vec![true]);
|
||||
|
||||
let env_var = env::var("TEST_CASE_VEC_10");
|
||||
assert_eq!(env_var, Ok(String::from("true")))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_optional_vec_env() {
|
||||
env::set_var("TEST_CASE_VEC_11", "something");
|
||||
let something: Option<Vec<&'static str>> = maybe_get_vec_env("TEST_CASE_VEC_11", SEP);
|
||||
assert_eq!(something, Some(vec!["something"]));
|
||||
|
||||
let nothing: Option<Vec<&'static str>> = maybe_get_vec_env("TEST_CASE_VEC_11_NONE", SEP);
|
||||
assert_eq!(nothing, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_custom_type_from_vec_env() {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum PaymentPlatform {
|
||||
PayPal,
|
||||
Stripe,
|
||||
SomethingElse,
|
||||
}
|
||||
|
||||
impl FromEnvString for PaymentPlatform {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_env_string(envstr: &EnvString) -> Result<Self, Self::Err> {
|
||||
match envstr.to_lowercase().as_str() {
|
||||
"paypal" => Ok(Self::PayPal),
|
||||
"stripe" => Ok(Self::Stripe),
|
||||
"smth" => Ok(Self::SomethingElse),
|
||||
_ => Err("Unsupported payment platform"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env::set_var("TEST_CASE_VEC_12", "paypal,stripe");
|
||||
let something: Option<Vec<PaymentPlatform>> = maybe_get_vec_env("TEST_CASE_VEC_12", SEP);
|
||||
assert_eq!(
|
||||
something,
|
||||
Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe])
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
use crate::{EnvError, EnvString, FromEnvString, ToEnvString};
|
||||
|
||||
pub(crate) fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
FromEnvString::from_env_string(&env_str)
|
||||
.map_err(|_| EnvError::FailedToParse(env_name.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn make_panic<T>(e: EnvError) -> T {
|
||||
panic!("{}", e)
|
||||
}
|
||||
|
||||
pub(crate) fn join(env_strings: &[EnvString], sep: &str) -> String {
|
||||
env_strings
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(String::new(), |mut res, (i, item)| {
|
||||
if i > 0 {
|
||||
res.push_str(sep);
|
||||
}
|
||||
res.push_str(item);
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn vec_to_env_strings<T>(values: Vec<T>) -> Vec<EnvString>
|
||||
where
|
||||
T: ToEnvString,
|
||||
{
|
||||
values.into_iter().map(EnvString::from).collect()
|
||||
}
|
238
src/core.rs
Normal file
238
src/core.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
use crate::error::Error;
|
||||
use estring::EString;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub fn get_or_set_default<R>(env_name: &str, default: R) -> Result<R, Error>
|
||||
where
|
||||
R: TryFrom<EString> + std::fmt::Display,
|
||||
{
|
||||
get::<R>(env_name).or_else(|err| match err {
|
||||
Error::NotPresent => sset(env_name, &default).parse().map_err(Error::from),
|
||||
_ => Err(err),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get<R>(env_name: &str) -> Result<R, Error>
|
||||
where
|
||||
R: TryFrom<EString>,
|
||||
{
|
||||
sget(env_name).and_then(|v| v.parse().map_err(Error::from))
|
||||
}
|
||||
|
||||
pub fn sget(env_name: &str) -> Result<EString, Error> {
|
||||
std::env::var(env_name)
|
||||
.map_err(Error::from)
|
||||
.map(EString::from)
|
||||
}
|
||||
|
||||
pub fn sset<V>(env_name: &str, value: V) -> EString
|
||||
where
|
||||
V: std::fmt::Display,
|
||||
{
|
||||
let val = value.to_string();
|
||||
std::env::set_var(env_name, &val);
|
||||
val.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
struct TestCase<const N: u8>;
|
||||
|
||||
impl<const N: u8> std::fmt::Display for TestCase<N> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "test_case_{}", N)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_env_variable_to_process() {
|
||||
let en = TestCase::<0>.to_string();
|
||||
sset(&en, "hello");
|
||||
match std::env::var(&en) {
|
||||
Ok(var) => assert_eq!(&var, "hello"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_variable() {
|
||||
let en = TestCase::<1>.to_string();
|
||||
std::env::set_var(&en, "hello");
|
||||
match get::<&str>(&en) {
|
||||
Ok(res) => assert_eq!(res, "hello"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_throw_no_present_error() {
|
||||
let en = TestCase::<2>.to_string();
|
||||
match get::<&str>(&en) {
|
||||
Err(Error::NotPresent) => {}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_set_default_if_var_is_no_present() {
|
||||
let en = TestCase::<3>.to_string();
|
||||
let orig = "hello";
|
||||
match get_or_set_default(&en, orig) {
|
||||
Ok(res) => {
|
||||
assert_eq!(res, orig);
|
||||
assert_eq!(std::env::var(&en).unwrap(), orig);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[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!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bool")]
|
||||
mod boolean {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_parse_bool_variable() {
|
||||
let en = TestCase::<7>.to_string();
|
||||
|
||||
[
|
||||
("1", true),
|
||||
("y", true),
|
||||
("yes", true),
|
||||
("true", true),
|
||||
("t", true),
|
||||
("on", true),
|
||||
("false", false),
|
||||
("f", false),
|
||||
("0", false),
|
||||
]
|
||||
.iter()
|
||||
.for_each(|(val, expected)| {
|
||||
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!(),
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vec")]
|
||||
mod vector {
|
||||
use super::*;
|
||||
use crate::estr::{CommaVec, SemiVec, SepVec};
|
||||
|
||||
#[test]
|
||||
fn should_return_var_as_vector() {
|
||||
let en = TestCase::<8>.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]),
|
||||
_ => 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!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[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!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[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_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!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
53
src/error.rs
Normal file
53
src/error.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use std::env::VarError;
|
||||
use std::error;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt;
|
||||
|
||||
/// The error type for operations interacting with environment variables
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The specified environment variable was not present in the current process's environment.
|
||||
NotPresent,
|
||||
|
||||
/// Failed to parse the specified environment variable.
|
||||
Parse(String),
|
||||
|
||||
/// The specified environment variable was found, but it did not contain
|
||||
/// valid unicode data. The found data is returned as a payload of this
|
||||
/// variant.
|
||||
Invalid(OsString),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Error::*;
|
||||
match &self {
|
||||
NotPresent => f.write_str("The specified env variable was not present"),
|
||||
Invalid(inner) => write!(
|
||||
f,
|
||||
"The specified env variable was found, but it did not valid: '{:?}'",
|
||||
inner,
|
||||
),
|
||||
Parse(env_name) => {
|
||||
write!(f, r#"Failed to parse environment variable "{}""#, env_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
impl From<VarError> for Error {
|
||||
fn from(err: VarError) -> Self {
|
||||
match err {
|
||||
VarError::NotPresent => Error::NotPresent,
|
||||
VarError::NotUnicode(inner) => Error::Invalid(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<estring::ParseError> for Error {
|
||||
fn from(err: estring::ParseError) -> Self {
|
||||
Error::Parse(err.clone())
|
||||
}
|
||||
}
|
6
src/estr.rs
Normal file
6
src/estr.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
#[cfg(feature = "vec")]
|
||||
pub mod vec;
|
||||
#[cfg(feature = "vec")]
|
||||
pub use vec::{CommaVec, SemiVec};
|
||||
|
||||
pub use estring::core::*;
|
7
src/estr/vec.rs
Normal file
7
src/estr/vec.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use estring::SepVec;
|
||||
|
||||
const COMMA: char = ',';
|
||||
const SEMI: char = ';';
|
||||
|
||||
pub type CommaVec<T> = SepVec<T, COMMA>;
|
||||
pub type SemiVec<T> = SepVec<T, SEMI>;
|
|
@ -1,4 +1,4 @@
|
|||
//! # itconfig
|
||||
//! # enve
|
||||
//!
|
||||
//! Simple configuration with macro for rust application.
|
||||
//!
|
||||
|
@ -16,7 +16,7 @@
|
|||
//!
|
||||
//! These macros require a Rust compiler version 1.31 or newer.
|
||||
//!
|
||||
//! Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`.
|
||||
//! Add `enve = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`.
|
||||
//!
|
||||
//!
|
||||
//! `Cargo.toml` example:
|
||||
|
@ -28,14 +28,14 @@
|
|||
//! authors = ["Me <user@rust-lang.org>"]
|
||||
//!
|
||||
//! [dependencies]
|
||||
//! itconfig = { version = "1.0", features = ["macro"] }
|
||||
//! enve = { version = "1.0", features = ["macro"] }
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! ## Basic usage
|
||||
//!
|
||||
//! ```rust
|
||||
//! use itconfig::config;
|
||||
//! use enve::config;
|
||||
//! use std::env;
|
||||
//! //use dotenv::dotenv;
|
||||
//!
|
||||
|
@ -90,7 +90,7 @@
|
|||
//! Macro is an optional feature, disabled by default. You can use this library without macro.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use itconfig::*;
|
||||
//! use enve::*;
|
||||
//! use std::env;
|
||||
//! // use dotenv::dotenv;
|
||||
//!
|
||||
|
@ -142,24 +142,22 @@
|
|||
unused_imports,
|
||||
unused_qualifications
|
||||
)]
|
||||
#![warn(missing_docs)]
|
||||
// Clippy lints
|
||||
#![deny(clippy::all)]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
mod envstr;
|
||||
mod core;
|
||||
mod error;
|
||||
mod get_env;
|
||||
mod get_vec_env;
|
||||
pub(crate) mod utils;
|
||||
pub mod estr;
|
||||
|
||||
pub use self::envstr::*;
|
||||
pub use self::error::*;
|
||||
pub use self::get_env::*;
|
||||
pub use self::get_vec_env::*;
|
||||
pub use self::core::*;
|
||||
pub use self::core::{get, get_or_set_default, sget, sset};
|
||||
pub use self::error::Error;
|
||||
|
||||
#[cfg(feature = "macro")]
|
||||
extern crate itconfig_macro;
|
||||
extern crate enve_mod;
|
||||
#[cfg(feature = "macro")]
|
||||
pub use itconfig_macro::*;
|
||||
pub use enve_mod::*;
|
Reference in a new issue