adze_concurrency_env_contract_core/
lib.rs1#![forbid(unsafe_op_in_unsafe_fn)]
4#![deny(missing_docs)]
5#![cfg_attr(feature = "strict_api", deny(unreachable_pub))]
6#![cfg_attr(not(feature = "strict_api"), warn(unreachable_pub))]
7#![cfg_attr(feature = "strict_docs", deny(missing_docs))]
8#![cfg_attr(not(feature = "strict_docs"), allow(missing_docs))]
9
10use std::env;
11
12pub use adze_concurrency_env_vars_core::{
13 DEFAULT_RAYON_NUM_THREADS, DEFAULT_TOKIO_WORKER_THREADS, RAYON_NUM_THREADS_ENV,
14 TOKIO_WORKER_THREADS_ENV,
15};
16pub use adze_concurrency_parse_core::parse_positive_usize_or_default;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct ConcurrencyCaps {
21 pub rayon_threads: usize,
23 pub tokio_worker_threads: usize,
25}
26
27impl ConcurrencyCaps {
28 #[must_use]
30 pub fn from_env() -> Self {
31 Self::from_lookup(|name| env::var(name).ok())
32 }
33
34 #[must_use]
39 pub fn from_lookup<F>(mut lookup: F) -> Self
40 where
41 F: FnMut(&str) -> Option<String>,
42 {
43 Self {
44 rayon_threads: parse_positive_usize_or_default(
45 lookup(RAYON_NUM_THREADS_ENV).as_deref(),
46 DEFAULT_RAYON_NUM_THREADS,
47 ),
48 tokio_worker_threads: parse_positive_usize_or_default(
49 lookup(TOKIO_WORKER_THREADS_ENV).as_deref(),
50 DEFAULT_TOKIO_WORKER_THREADS,
51 ),
52 }
53 }
54}
55
56impl Default for ConcurrencyCaps {
57 fn default() -> Self {
58 Self {
59 rayon_threads: DEFAULT_RAYON_NUM_THREADS,
60 tokio_worker_threads: DEFAULT_TOKIO_WORKER_THREADS,
61 }
62 }
63}
64
65#[must_use]
67pub fn current_caps() -> ConcurrencyCaps {
68 ConcurrencyCaps::from_env()
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn default_caps_use_expected_constants() {
77 let caps = ConcurrencyCaps::default();
78 assert_eq!(caps.rayon_threads, DEFAULT_RAYON_NUM_THREADS);
79 assert_eq!(caps.tokio_worker_threads, DEFAULT_TOKIO_WORKER_THREADS);
80 }
81
82 #[test]
83 fn from_lookup_returns_defaults_when_none() {
84 let caps = ConcurrencyCaps::from_lookup(|_| None);
85 assert_eq!(caps, ConcurrencyCaps::default());
86 }
87
88 #[test]
89 fn from_lookup_parses_valid_values() {
90 let caps = ConcurrencyCaps::from_lookup(|name| match name {
91 "RAYON_NUM_THREADS" => Some("16".to_string()),
92 "TOKIO_WORKER_THREADS" => Some("8".to_string()),
93 _ => None,
94 });
95 assert_eq!(caps.rayon_threads, 16);
96 assert_eq!(caps.tokio_worker_threads, 8);
97 }
98
99 #[test]
100 fn from_lookup_falls_back_on_zero() {
101 let caps = ConcurrencyCaps::from_lookup(|_| Some("0".to_string()));
102 assert_eq!(caps, ConcurrencyCaps::default());
103 }
104
105 #[test]
106 fn from_lookup_falls_back_on_invalid() {
107 let caps = ConcurrencyCaps::from_lookup(|_| Some("not_a_number".to_string()));
108 assert_eq!(caps, ConcurrencyCaps::default());
109 }
110
111 #[test]
112 fn clone_and_eq() {
113 let caps = ConcurrencyCaps::from_lookup(|_| Some("3".to_string()));
114 let cloned = caps;
115 assert_eq!(caps, cloned);
116 }
117
118 #[test]
119 fn debug_format_is_readable() {
120 let caps = ConcurrencyCaps::default();
121 let dbg = format!("{caps:?}");
122 assert!(dbg.contains("rayon_threads"));
123 assert!(dbg.contains("tokio_worker_threads"));
124 }
125
126 #[test]
127 fn env_var_constants_match_expected_names() {
128 assert_eq!(RAYON_NUM_THREADS_ENV, "RAYON_NUM_THREADS");
129 assert_eq!(TOKIO_WORKER_THREADS_ENV, "TOKIO_WORKER_THREADS");
130 }
131}