Skip to content

Commit 816c027

Browse files
committed
Add lazy alternative to readme.
1 parent 757b458 commit 816c027

File tree

3 files changed

+220
-7
lines changed

3 files changed

+220
-7
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,11 @@ const FOO: u32 = 0;
209209
const FOO: u32 = option_env!("FOO").and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
210210
```
211211

212-
Until that is possible, I'm not aware of an alternative approach.
212+
Until that is possible, the only other alternative I'm aware of would be to use a lazy approach, which defers parsing until runtime.
213+
214+
```rust
215+
use std::sync::LazyLock;
216+
static FOO: LazyLock<u32> = LazyLock::new(|| {
217+
option_env!("FOO").and_then(|foo| foo.parse().ok()).unwrap_or(0)
218+
});
219+
```

const_env_impl/README.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# const_env
2+
3+
- [Motivation](#motivation)
4+
- [Crate Features](#crate-features)
5+
- [Usage](#usage)
6+
- [Supported Types](#supported-types)
7+
- [Limitations](#known-limitations)
8+
- [Alternatives](#alternatives)
9+
10+
## Motivation
11+
12+
**Your goal**: You want to define various constants for your code at compile time based on environment variables.
13+
14+
**Your problem**: Its only possible to do this for `&'static str` constants in today's Rust.
15+
16+
```rust
17+
const MY_STR: &'static str = env!("SOME_ENV");
18+
```
19+
20+
You cannot similarly initialize other types such as constant `u32`s, `bool`s, or
21+
other primitive types. See [issues](https://github.com/rust-lang/rfcs/issues/1907) and
22+
[workarounds](https://stackoverflow.com/questions/37526598/overriding-constant-via-compiler-option).
23+
Eventually you will be able to do so once support for running `parse` and `unwrap` in
24+
`const fn` lands, but for now this crate offers an easy workaround that you can use today.
25+
26+
## Crate Features
27+
28+
| Feature name | Enabled by default? | Requires nightly? | Description |
29+
|---|---|---|---|
30+
| `tracked` | No | Yes | Use the unstable [proc_macro_tracked_env](https://github.com/rust-lang/rust/issues/74690) feature to inform the build system about the used environment variables. |
31+
32+
## Usage
33+
34+
Add the dependency. If your crate uses nightly, enable the `tracked` feature for better
35+
build dependency tracking.
36+
37+
```toml
38+
# If using a stable compiler:
39+
[dependencies]
40+
const_env = "0.1"
41+
42+
# If using a nightly compiler:
43+
[dependencies]
44+
const_env = { version = "0.1", features = ["tracked"] }
45+
```
46+
47+
At the top of your file import the `env_item!` and/or `env_lit!` macros.
48+
49+
```rust
50+
use const_env::{env_item, env_lit};
51+
```
52+
53+
Use the macro to override the value of constants based on environment variables at build time.
54+
55+
```rust
56+
#[env_item]
57+
const FOO: u32 = 123;
58+
59+
const BAR: bool = env_lit!("BAR", false);
60+
61+
// This test will PASS if invoked as `FOO=456 BAR=true cargo test`
62+
#[test]
63+
fn test() {
64+
assert_eq!(456, FOO);
65+
assert_eq!(true, BAR);
66+
}
67+
```
68+
69+
By default, the `env_item` macro looks for an environment variable with the same name as the constant that it is attached to.
70+
71+
```rust
72+
// Use `FOO=true cargo build` to configure the value.
73+
#[env_item]
74+
const FOO: bool = false;
75+
```
76+
77+
But you can specify a name explicitly too.
78+
79+
```rust
80+
// Use `BAR=true cargo build` to configure the value.
81+
#[env_item("BAR")]
82+
const FOO: bool = false;
83+
```
84+
85+
The expression that you assign in your source acts as a default in case the environment variable does not exist.
86+
87+
```rust
88+
// If env var FOO is not set then the FOO constant will have the default value of '🦀'.
89+
#[env_item]
90+
const FOO: char = '🦀';
91+
```
92+
93+
Both `const` and `static` declarations are supported.
94+
95+
```rust
96+
// Both of these may be set by `FOO=abc BAZ=def cargo build`.
97+
#[env_item]
98+
const FOO: &'static str = "hello";
99+
#[env_item("BAZ")]
100+
static BAR: &'static [u8] = b"world";
101+
```
102+
103+
## Supported Types
104+
105+
Strings!
106+
107+
```rust
108+
#[env_item]
109+
const FOO: &'static str = "hello";
110+
111+
// example: `FOO=abc cargo build`
112+
// results in:
113+
const FOO: &'static str = "abc";
114+
```
115+
116+
Byte strings!
117+
118+
```rust
119+
#[env_item]
120+
const FOO: &'static [u8] = b"hello";
121+
122+
// example: `FOO=world cargo build`
123+
// results in:
124+
const FOO: &'static [u8] = b"world";
125+
```
126+
127+
Bytes!
128+
```rust
129+
#[env_item]
130+
const FOO: u8 = b'⚙';
131+
132+
// example: `FOO=🦀 cargo build`
133+
// results in:
134+
const FOO: u8 = b'🦀';
135+
```
136+
137+
Characters!
138+
139+
```rust
140+
#[env_item]
141+
const FOO: char = '⚙';
142+
143+
// example: `FOO=🦀 cargo build`
144+
// results in:
145+
const FOO: car = '🦀';
146+
```
147+
148+
Integers of all shapes and sizes!
149+
150+
```rust
151+
#[env_item]
152+
const FOO: u32 = 123;
153+
#[env_item]
154+
const BAR: i64 = 456;
155+
#[env_item]
156+
const BAZ: usize = 0;
157+
158+
// example: `FOO=321 BAR=-456 BAZ=1usize cargo build`
159+
// results in:
160+
const FOO: u32 = 321;
161+
const BAR: i64 = -456;
162+
const BAZ: usize = 1usize;
163+
```
164+
165+
Floats of all shapes and sizes!
166+
167+
```rust
168+
#[env_item]
169+
const FOO: f32 = 123.0;
170+
#[env_item]
171+
const BAR: f64 = 456.0;
172+
#[env_item]
173+
const BAZ: f32 = 0.0;
174+
175+
// example: `FOO=321.0 BAR=-456.0 BAZ=1f32 cargo build`
176+
// results in:
177+
const FOO: f32 = 321.0;
178+
const BAR: f64 = -456.0;
179+
const BAZ: f32 = 1f32;
180+
```
181+
182+
Booleans!
183+
184+
```rust
185+
#[env_item]
186+
const FOO: bool = false;
187+
188+
// example: `FOO=true cargo build`
189+
// results in:
190+
const FOO: bool = true;
191+
```
192+
193+
## Known Limitations
194+
195+
- Only top-level `const` and `static` declarations are supported.
196+
197+
## Alternatives
198+
199+
An immediately available alternative would be to write a `build.rs` script which looks at the env vars and generates code based on them. This is conceptually similar to how this crate works, except that this crate uses a procedural macro instead of a build script.
200+
201+
Longer term, this crate will hopefully become completely superceded by being able to write normal Rust code once compile-time const evaluation of [FromStr](https://github.com/rust-lang/rust/issues/143773) and [various Option methods](https://github.com/rust-lang/rust/issues/143956). That will allow you to modify your code like so:
202+
203+
```rust
204+
// Current code using this library.
205+
#[env_item]
206+
const FOO: u32 = 0;
207+
208+
// New code, no library needed.
209+
const FOO: u32 = option_env!("FOO").and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
210+
```
211+
212+
Until that is possible, I'm not aware of an alternative approach.

const_env_tests/src/main.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::sync::LazyLock;
2-
31
use const_env::{env_item, env_lit};
42

53
const USIZE_ARRAY: [usize; 3] = env_lit!("INT_ARRAY", [1, 2, 3]);
@@ -70,10 +68,6 @@ static ORIGIN: Vec2<f64> = Vec2 { x: 0., y: 0. };
7068

7169
static ORIGIN_LIT: Vec2<f32> = env_lit!("ORIGIN", Vec2 { x: 0., y: 0.});
7270

73-
static FOO: LazyLock<u32> = LazyLock::new(|| {
74-
option_env!("FOO").and_then(|foo| foo.parse().ok()).unwrap_or(0)
75-
});
76-
7771
fn main() {
7872
assert_eq!([10, 11, 12], USIZE_ARRAY);
7973
assert_eq!(["bar"], STRING_ARRAY);

0 commit comments

Comments
 (0)