kataglyphis_rustprojecttemplate/
config.rs

1// src/config.rs — Centralised environment-variable configuration.
2//
3// All `KATAGLYPHIS_*` env vars are read here so that the configuration surface
4// is discoverable and documented in a single place.
5//
6// Each accessor caches the parsed value in a `OnceLock` so that the env var is
7// read at most once per process lifetime.
8//
9// The `env_cached!` macro eliminates the repetitive `OnceLock` + `get_or_init`
10// boilerplate that every accessor previously duplicated.
11
12use std::sync::OnceLock;
13
14use log::warn;
15
16// ── Helper macro ───────────────────────────────────────────────────
17
18/// Define an env-var accessor that parses once and caches in a `OnceLock`.
19///
20/// # Variants
21///
22/// ```ignore
23/// // Value type (returned by copy), public by default:
24/// env_cached!(fn_name -> Type, { || init_expr });
25///
26/// // Value type with explicit visibility:
27/// env_cached!(pub(crate) fn_name -> Type, { || init_expr });
28///
29/// // Reference type (returned by &'static ref):
30/// env_cached!(ref fn_name -> Type, { || init_expr });
31/// ```
32macro_rules! env_cached {
33    // Copy variant — returns `T` by value (requires `T: Copy`).
34    ($(#[$attr:meta])* fn $name:ident -> $ty:ty, $init:expr) => {
35        $(#[$attr])*
36        pub fn $name() -> $ty {
37            static CACHE: OnceLock<$ty> = OnceLock::new();
38            *CACHE.get_or_init($init)
39        }
40    };
41    // Copy variant with explicit visibility.
42    ($(#[$attr:meta])* $vis:vis fn $name:ident -> $ty:ty, $init:expr) => {
43        $(#[$attr])*
44        $vis fn $name() -> $ty {
45            static CACHE: OnceLock<$ty> = OnceLock::new();
46            *CACHE.get_or_init($init)
47        }
48    };
49    // Reference variant — returns `&'static T` to avoid cloning.
50    ($(#[$attr:meta])* ref fn $name:ident -> $ty:ty, $init:expr) => {
51        $(#[$attr])*
52        pub fn $name() -> &'static $ty {
53            static CACHE: OnceLock<$ty> = OnceLock::new();
54            CACHE.get_or_init($init)
55        }
56    };
57}
58
59// ── Preprocessing ──────────────────────────────────────────────────
60
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
62#[cfg_attr(not(onnx), allow(dead_code))]
63pub(crate) enum PreprocessMode {
64    Letterbox,
65    #[default]
66    Stretch,
67}
68
69env_cached!(
70    /// `KATAGLYPHIS_PREPROCESS`: `"letterbox"` | `"stretch"` (default `"stretch"`).
71    #[cfg_attr(not(onnx), allow(dead_code))]
72    pub(crate) fn preprocess_mode -> PreprocessMode, || {
73        let raw = std::env::var("KATAGLYPHIS_PREPROCESS").unwrap_or_else(|_| "stretch".to_string());
74        match raw.trim().to_ascii_lowercase().as_str() {
75            "letterbox" | "boxed" | "pad" => PreprocessMode::Letterbox,
76            "stretch" | "resize" => PreprocessMode::Stretch,
77            other => {
78                warn!("Unknown preprocess mode '{other}', defaulting to stretch");
79                PreprocessMode::Stretch
80            }
81        }
82    }
83);
84
85env_cached!(
86    /// `KATAGLYPHIS_SWAP_XY`: set to `"1"` to swap X/Y in YOLO output coords.
87    fn swap_xy_enabled -> bool, || {
88        std::env::var("KATAGLYPHIS_SWAP_XY").ok().as_deref() == Some("1")
89    }
90);
91
92// ── ONNX backend selection ─────────────────────────────────────────
93
94env_cached!(
95    /// `KATAGLYPHIS_ONNX_BACKEND`: `"ort"` / `"onnxruntime"` | `"tract"` | unset (auto).
96    ///
97    /// Returns `None` when the variable is unset, signalling automatic backend
98    /// selection.  The returned reference is `&'static` so callers can use
99    /// `.as_deref()` without allocation.
100    ref fn onnx_backend -> Option<String>, || {
101        std::env::var("KATAGLYPHIS_ONNX_BACKEND")
102            .ok()
103            .map(|v| v.to_lowercase())
104    }
105);
106
107env_cached!(
108    /// `KATAGLYPHIS_ORT_DEVICE`: `"cpu"` (default) | `"cuda"` | `"auto"`.
109    ///
110    /// Returns a `&'static String` to avoid cloning on every call.
111    ref fn ort_device -> String, || {
112        std::env::var("KATAGLYPHIS_ORT_DEVICE")
113            .unwrap_or_else(|_| "cpu".to_string())
114            .to_ascii_lowercase()
115    }
116);
117
118// ── Inference / GUI ────────────────────────────────────────────────
119
120env_cached!(
121    /// `KATAGLYPHIS_ONNX_MODEL`: override path to the ONNX model file.
122    ///
123    /// Returns a `&'static Option<String>` to avoid cloning on every call.
124    ref fn onnx_model_override -> Option<String>, || {
125        std::env::var("KATAGLYPHIS_ONNX_MODEL").ok()
126    }
127);
128
129env_cached!(
130    /// `KATAGLYPHIS_SCORE_THRESHOLD`: detection confidence threshold (default `0.5`).
131    fn score_threshold -> f32, || {
132        std::env::var("KATAGLYPHIS_SCORE_THRESHOLD")
133            .ok()
134            .and_then(|v| v.parse::<f32>().ok())
135            .unwrap_or(0.5)
136    }
137);
138
139env_cached!(
140    /// `KATAGLYPHIS_INFER_EVERY_MS`: minimum interval between inference requests (default `0`).
141    fn infer_every_ms -> u64, || {
142        std::env::var("KATAGLYPHIS_INFER_EVERY_MS")
143            .ok()
144            .and_then(|v| v.parse::<u64>().ok())
145            .unwrap_or(0)
146    }
147);
148
149// ── Logging ────────────────────────────────────────────────────────
150
151env_cached!(
152    /// `KATAGLYPHIS_LOG_LEVEL`: `error` | `warn` | `info` (default) | `debug` | `trace`.
153    fn log_level -> log::LevelFilter, || {
154        std::env::var("KATAGLYPHIS_LOG_LEVEL")
155            .ok()
156            .and_then(|v| match v.to_ascii_lowercase().as_str() {
157                "error" => Some(log::LevelFilter::Error),
158                "warn" | "warning" => Some(log::LevelFilter::Warn),
159                "info" => Some(log::LevelFilter::Info),
160                "debug" => Some(log::LevelFilter::Debug),
161                "trace" => Some(log::LevelFilter::Trace),
162                _ => None,
163            })
164            .unwrap_or(log::LevelFilter::Info)
165    }
166);