use std::sync::LazyLock;
use tracing::warn;
pub(crate) const DEFAULT_MAX_STEPS: usize = 10_000;
pub(crate) const DEFAULT_MAX_CHAIN_SIZE: usize = 500;
pub(crate) const DEFAULT_MAX_HISTORICAL_PRICES: usize = 100_000;
pub(crate) static MAX_STEPS: LazyLock<usize> =
LazyLock::new(|| parse_limit(std::env::var("OCS_MAX_STEPS").ok(), DEFAULT_MAX_STEPS));
pub(crate) static MAX_CHAIN_SIZE: LazyLock<usize> = LazyLock::new(|| {
parse_limit(
std::env::var("OCS_MAX_CHAIN_SIZE").ok(),
DEFAULT_MAX_CHAIN_SIZE,
)
});
pub(crate) static MAX_HISTORICAL_PRICES: LazyLock<usize> = LazyLock::new(|| {
parse_limit(
std::env::var("OCS_MAX_HISTORICAL_PRICES").ok(),
DEFAULT_MAX_HISTORICAL_PRICES,
)
});
#[must_use]
pub(crate) fn parse_limit(raw: Option<String>, default: usize) -> usize {
match raw {
None => default,
Some(value) => match value.trim().parse::<usize>() {
Ok(parsed) if parsed >= 1 => parsed,
_ => {
warn!(
raw = %value,
default,
"invalid limit value; falling back to default"
);
default
}
},
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_limit_unset_uses_default() {
assert_eq!(parse_limit(None, 10), 10);
}
#[test]
fn test_parse_limit_valid_value_is_used() {
assert_eq!(parse_limit(Some("42".to_string()), 10), 42);
}
#[test]
fn test_parse_limit_trims_whitespace() {
assert_eq!(parse_limit(Some(" 25 ".to_string()), 10), 25);
}
#[test]
fn test_parse_limit_non_numeric_falls_back() {
assert_eq!(parse_limit(Some("not-a-number".to_string()), 10), 10);
}
#[test]
fn test_parse_limit_zero_falls_back() {
assert_eq!(parse_limit(Some("0".to_string()), 10), 10);
}
#[test]
fn test_parse_limit_negative_falls_back() {
assert_eq!(parse_limit(Some("-5".to_string()), 10), 10);
}
#[test]
fn test_default_limits_match_documentation() {
assert_eq!(*MAX_STEPS, DEFAULT_MAX_STEPS);
assert_eq!(*MAX_STEPS, 10_000);
assert_eq!(*MAX_CHAIN_SIZE, DEFAULT_MAX_CHAIN_SIZE);
assert_eq!(*MAX_CHAIN_SIZE, 500);
assert_eq!(*MAX_HISTORICAL_PRICES, DEFAULT_MAX_HISTORICAL_PRICES);
assert_eq!(*MAX_HISTORICAL_PRICES, 100_000);
}
}