Skip to main content

dynamo_runtime/config/
env_config.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Shared support for lazily resolved scalar environment configuration.
5
6use std::{env::VarError, str::FromStr};
7
8/// Parse an environment variable or return `default` when it is unset or invalid.
9#[doc(hidden)]
10pub fn parse_or_default<T>(name: &str, default: T) -> T
11where
12    T: FromStr,
13    T::Err: std::fmt::Display,
14{
15    match std::env::var(name) {
16        Ok(value) => match value.parse() {
17            Ok(parsed) => parsed,
18            Err(error) => {
19                tracing::warn!(
20                    env_var = name,
21                    value,
22                    %error,
23                    "invalid environment variable, using default"
24                );
25                default
26            }
27        },
28        Err(VarError::NotPresent) => default,
29        Err(VarError::NotUnicode(value)) => {
30            tracing::warn!(
31                env_var = name,
32                ?value,
33                "environment variable is not valid Unicode, using default"
34            );
35            default
36        }
37    }
38}
39
40/// Declare a function that parses and caches one scalar environment variable.
41#[macro_export]
42macro_rules! env_config {
43    (
44        $(#[$attr:meta])*
45        $visibility:vis fn $name:ident() -> $value_type:ty =
46            $env_var:path, default = $default:expr;
47    ) => {
48        $(#[$attr])*
49        $visibility fn $name() -> $value_type {
50            static VALUE: ::std::sync::OnceLock<$value_type> = ::std::sync::OnceLock::new();
51            VALUE.get_or_init(|| {
52                $crate::config::env_config::parse_or_default($env_var, $default)
53            }).clone()
54        }
55    };
56}
57
58#[cfg(test)]
59mod tests {
60    const TEST_ENV: &str = "DYN_TEST_ENV_CONFIG_PARSE_OR_DEFAULT";
61    const INVALID_TEST_ENV: &str = "DYN_TEST_ENV_CONFIG_INVALID";
62
63    crate::env_config! {
64        fn configured_value() -> usize = TEST_ENV, default = 17;
65    }
66
67    crate::env_config! {
68        fn configured_string() -> String = TEST_ENV, default = "fallback".to_string();
69    }
70
71    #[test]
72    fn uses_parsed_environment_value() {
73        temp_env::with_var(TEST_ENV, Some("42"), || {
74            assert_eq!(configured_value(), 42);
75        });
76    }
77
78    #[test]
79    fn invalid_value_uses_default() {
80        temp_env::with_var(INVALID_TEST_ENV, Some("invalid"), || {
81            assert_eq!(super::parse_or_default::<usize>(INVALID_TEST_ENV, 17), 17);
82        });
83    }
84
85    #[test]
86    fn supports_non_copy_values() {
87        temp_env::with_var(TEST_ENV, Some("configured"), || {
88            assert_eq!(configured_string(), "configured");
89        });
90    }
91}