Skip to main content

oxihuman_core/
proc_context.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Processing context: key/value state bag for pipeline stages.
6
7use std::collections::HashMap;
8
9/// Value held in the processing context.
10#[derive(Debug, Clone)]
11pub enum CtxVal {
12    Int(i64),
13    Float(f64),
14    Bool(bool),
15    Text(String),
16}
17
18/// A context carrying state through a processing pipeline.
19#[derive(Debug, Default)]
20pub struct ProcContext {
21    entries: HashMap<String, CtxVal>,
22    stage: u32,
23    errors: Vec<String>,
24    warnings: Vec<String>,
25    done: bool,
26}
27
28#[allow(dead_code)]
29impl ProcContext {
30    pub fn new() -> Self {
31        ProcContext::default()
32    }
33
34    pub fn set_int(&mut self, key: &str, v: i64) {
35        self.entries.insert(key.to_string(), CtxVal::Int(v));
36    }
37
38    pub fn set_float(&mut self, key: &str, v: f64) {
39        self.entries.insert(key.to_string(), CtxVal::Float(v));
40    }
41
42    pub fn set_bool(&mut self, key: &str, v: bool) {
43        self.entries.insert(key.to_string(), CtxVal::Bool(v));
44    }
45
46    pub fn set_text(&mut self, key: &str, v: &str) {
47        self.entries
48            .insert(key.to_string(), CtxVal::Text(v.to_string()));
49    }
50
51    pub fn get_int(&self, key: &str) -> Option<i64> {
52        match self.entries.get(key) {
53            Some(CtxVal::Int(v)) => Some(*v),
54            _ => None,
55        }
56    }
57
58    pub fn get_float(&self, key: &str) -> Option<f64> {
59        match self.entries.get(key) {
60            Some(CtxVal::Float(v)) => Some(*v),
61            _ => None,
62        }
63    }
64
65    pub fn get_bool(&self, key: &str) -> Option<bool> {
66        match self.entries.get(key) {
67            Some(CtxVal::Bool(v)) => Some(*v),
68            _ => None,
69        }
70    }
71
72    pub fn get_text(&self, key: &str) -> Option<&str> {
73        match self.entries.get(key) {
74            Some(CtxVal::Text(v)) => Some(v.as_str()),
75            _ => None,
76        }
77    }
78
79    pub fn has(&self, key: &str) -> bool {
80        self.entries.contains_key(key)
81    }
82
83    pub fn remove(&mut self, key: &str) -> bool {
84        self.entries.remove(key).is_some()
85    }
86
87    pub fn stage(&self) -> u32 {
88        self.stage
89    }
90
91    pub fn advance_stage(&mut self) {
92        self.stage += 1;
93    }
94
95    pub fn add_error(&mut self, msg: &str) {
96        self.errors.push(msg.to_string());
97    }
98
99    pub fn add_warning(&mut self, msg: &str) {
100        self.warnings.push(msg.to_string());
101    }
102
103    pub fn has_errors(&self) -> bool {
104        !self.errors.is_empty()
105    }
106
107    pub fn error_count(&self) -> usize {
108        self.errors.len()
109    }
110
111    pub fn warning_count(&self) -> usize {
112        self.warnings.len()
113    }
114
115    pub fn mark_done(&mut self) {
116        self.done = true;
117    }
118
119    pub fn is_done(&self) -> bool {
120        self.done
121    }
122
123    pub fn reset(&mut self) {
124        self.entries.clear();
125        self.errors.clear();
126        self.warnings.clear();
127        self.stage = 0;
128        self.done = false;
129    }
130
131    pub fn entry_count(&self) -> usize {
132        self.entries.len()
133    }
134}
135
136pub fn new_proc_context() -> ProcContext {
137    ProcContext::new()
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn set_and_get_int() {
146        let mut ctx = new_proc_context();
147        ctx.set_int("count", 42);
148        assert_eq!(ctx.get_int("count"), Some(42));
149    }
150
151    #[test]
152    fn set_and_get_float() {
153        let mut ctx = new_proc_context();
154        ctx.set_float("ratio", std::f64::consts::PI);
155        assert!(ctx.get_float("ratio").expect("should succeed") > std::f64::consts::E);
156    }
157
158    #[test]
159    fn set_and_get_bool() {
160        let mut ctx = new_proc_context();
161        ctx.set_bool("ready", true);
162        assert_eq!(ctx.get_bool("ready"), Some(true));
163    }
164
165    #[test]
166    fn set_and_get_text() {
167        let mut ctx = new_proc_context();
168        ctx.set_text("name", "oxihuman");
169        assert_eq!(ctx.get_text("name"), Some("oxihuman"));
170    }
171
172    #[test]
173    fn has_and_remove() {
174        let mut ctx = new_proc_context();
175        ctx.set_int("x", 1);
176        assert!(ctx.has("x"));
177        assert!(ctx.remove("x"));
178        assert!(!ctx.has("x"));
179    }
180
181    #[test]
182    fn stage_advance() {
183        let mut ctx = new_proc_context();
184        assert_eq!(ctx.stage(), 0);
185        ctx.advance_stage();
186        ctx.advance_stage();
187        assert_eq!(ctx.stage(), 2);
188    }
189
190    #[test]
191    fn errors_and_warnings() {
192        let mut ctx = new_proc_context();
193        ctx.add_error("bad");
194        ctx.add_warning("meh");
195        assert!(ctx.has_errors());
196        assert_eq!(ctx.error_count(), 1);
197        assert_eq!(ctx.warning_count(), 1);
198    }
199
200    #[test]
201    fn done_flag() {
202        let mut ctx = new_proc_context();
203        assert!(!ctx.is_done());
204        ctx.mark_done();
205        assert!(ctx.is_done());
206    }
207
208    #[test]
209    fn reset_clears_all() {
210        let mut ctx = new_proc_context();
211        ctx.set_int("k", 1);
212        ctx.add_error("e");
213        ctx.advance_stage();
214        ctx.mark_done();
215        ctx.reset();
216        assert_eq!(ctx.entry_count(), 0);
217        assert!(!ctx.has_errors());
218        assert_eq!(ctx.stage(), 0);
219        assert!(!ctx.is_done());
220    }
221}