1use torsh_core::error::{Result, TorshError};
4
5pub fn validate_probability(prob: f32, name: &str) -> Result<()> {
11 if !(0.0..=1.0).contains(&prob) {
12 return Err(TorshError::InvalidArgument(format!(
13 "{name} must be between 0 and 1, got {prob}"
14 )));
15 }
16 Ok(())
17}
18
19pub fn validate_range<T: PartialOrd + std::fmt::Debug>(range: (T, T), name: &str) -> Result<()> {
21 if range.0 > range.1 {
22 return Err(TorshError::InvalidArgument(format!(
23 "Invalid {name} range: {range:?}"
24 )));
25 }
26 Ok(())
27}
28
29pub fn validate_positive<T: PartialOrd + Default + std::fmt::Debug>(
31 value: T,
32 name: &str,
33) -> Result<()> {
34 if value <= T::default() {
35 return Err(TorshError::InvalidArgument(format!(
36 "{name} must be positive, got {value:?}"
37 )));
38 }
39 Ok(())
40}
41
42pub fn validate_same_length<T, U>(vec1: &[T], vec2: &[U], name1: &str, name2: &str) -> Result<()> {
44 if vec1.len() != vec2.len() {
45 return Err(TorshError::InvalidArgument(format!(
46 "{} and {} must have the same length, got {} and {}",
47 name1,
48 name2,
49 vec1.len(),
50 vec2.len()
51 )));
52 }
53 Ok(())
54}
55
56pub fn validate_not_empty<T>(vec: &[T], name: &str) -> Result<()> {
58 if vec.is_empty() {
59 return Err(TorshError::InvalidArgument(format!(
60 "{name} cannot be empty"
61 )));
62 }
63 Ok(())
64}
65
66pub mod errors {
68 use torsh_core::error::TorshError;
69
70 pub fn invalid_index(index: usize, size: usize) -> TorshError {
72 TorshError::IndexError { index, size }
73 }
74
75 pub fn invalid_argument(msg: impl Into<String>) -> TorshError {
77 TorshError::InvalidArgument(msg.into())
78 }
79
80 pub fn config_error(msg: impl Into<String>) -> TorshError {
82 TorshError::InvalidArgument(msg.into())
83 }
84
85 pub fn empty_batch() -> TorshError {
87 TorshError::InvalidArgument("Cannot process empty batch".to_string())
88 }
89
90 pub fn shape_mismatch(expected: &[usize], got: &[usize]) -> TorshError {
92 TorshError::ShapeMismatch {
93 expected: expected.to_vec(),
94 got: got.to_vec(),
95 }
96 }
97
98 pub fn file_not_found(path: &std::path::Path) -> TorshError {
100 TorshError::InvalidArgument(format!("File not found: {}", path.display()))
101 }
102
103 pub fn invalid_format(expected: &str, got: &str) -> TorshError {
105 TorshError::InvalidArgument(format!("Expected {expected} format, got {got}"))
106 }
107}
108
109#[macro_export]
111macro_rules! validated_constructor {
112 ($name:ident, $field:ident: f32, probability) => {
114 impl $name {
115 pub fn new($field: f32) -> Result<Self> {
117 $crate::utils::validate_probability($field, stringify!($field))?;
118 Ok(Self { $field })
119 }
120 }
121 };
122
123 ($name:ident, $field:ident: ($t1:ty, $t2:ty), range) => {
125 impl $name {
126 pub fn new($field: ($t1, $t2)) -> Result<Self> {
128 $crate::utils::validate_range($field, stringify!($field))?;
129 Ok(Self { $field })
130 }
131 }
132 };
133
134 ($name:ident, $field:ident: $t:ty, positive) => {
136 impl $name {
137 pub fn new($field: $t) -> Result<Self> {
139 $crate::utils::validate_positive($field, stringify!($field))?;
140 Ok(Self { $field })
141 }
142 }
143 };
144
145 ($name:ident, size: ($t1:ty, $t2:ty)) => {
147 impl $name {
148 pub fn new(size: ($t1, $t2)) -> Self {
150 Self { size }
151 }
152 }
153 };
154
155 ($name:ident, $($field:ident: $t:ty),+, validate = $validator:expr) => {
157 impl $name {
158 pub fn new($($field: $t),+) -> Result<Self> {
160 $validator(&$($field),+)?;
161 Ok(Self { $($field),+ })
162 }
163 }
164 };
165}
166
167#[macro_export]
169macro_rules! builder_pattern {
170 ($name:ident, $($field:ident: $t:ty),+) => {
171 };
181}
182
183#[macro_export]
185macro_rules! simple_random_transform {
186 ($name:ident, $input:ty, $output:ty, $prob_field:ident, $transform_fn:expr) => {
187 impl $crate::transforms::Transform<$input> for $name {
188 type Output = $output;
189
190 fn transform(&self, input: $input) -> Result<Self::Output> {
191 use scirs2_core::random::{Random, Rng};
193 let mut rng = Random::seed(42);
194
195 if rng.random::<f32>() < self.$prob_field {
196 $transform_fn(input, &mut rng)
197 } else {
198 Ok(input)
199 }
200 }
201
202 fn is_deterministic(&self) -> bool {
203 false
204 }
205 }
206 };
207}
208
209pub fn validate_dataset_path(path: &std::path::Path, name: &str) -> Result<()> {
211 if !path.exists() {
212 return Err(TorshError::InvalidArgument(format!(
213 "{} path does not exist: {}",
214 name,
215 path.display()
216 )));
217 }
218 Ok(())
219}
220
221pub fn validate_file_extension(path: &std::path::Path, extensions: &[&str]) -> Result<()> {
223 if let Some(ext) = path.extension() {
224 let ext_str = ext.to_string_lossy().to_lowercase();
225 if extensions.iter().any(|&e| e == ext_str) {
226 return Ok(());
227 }
228 }
229
230 Err(TorshError::InvalidArgument(format!(
231 "File must have one of these extensions: {:?}, got: {}",
232 extensions,
233 path.display()
234 )))
235}
236
237pub fn validate_tensor_shape(shape: &[usize], expected_dims: usize, name: &str) -> Result<()> {
239 if shape.len() != expected_dims {
240 return Err(TorshError::InvalidArgument(format!(
241 "{} tensor must have {} dimensions, got {}",
242 name,
243 expected_dims,
244 shape.len()
245 )));
246 }
247 Ok(())
248}
249
250pub fn create_size_tuple(width: usize, height: usize) -> Result<(usize, usize)> {
252 validate_positive(width, "width")?;
253 validate_positive(height, "height")?;
254 Ok((width, height))
255}
256
257pub trait Resettable {
259 fn reset(&mut self);
261}
262
263pub trait Configurable<T> {
264 fn configure(&mut self, config: T) -> Result<()>;
266}
267
268pub trait Cacheable {
269 fn clear_cache(&mut self);
271
272 fn cache_hit_rate(&self) -> Option<f32> {
274 None
275 }
276}
277
278pub struct ProgressTracker {
280 current: usize,
281 total: usize,
282 last_reported: f32,
283 report_interval: f32,
284}
285
286impl ProgressTracker {
287 pub fn new(total: usize) -> Self {
289 Self {
290 current: 0,
291 total,
292 last_reported: 0.0,
293 report_interval: 0.1, }
295 }
296
297 pub fn update(&mut self) -> bool {
299 self.current += 1;
300 let progress = self.current as f32 / self.total as f32;
301
302 if progress - self.last_reported >= self.report_interval {
303 self.last_reported = progress;
304 true
305 } else {
306 false
307 }
308 }
309
310 pub fn percentage(&self) -> f32 {
312 (self.current as f32 / self.total as f32) * 100.0
313 }
314
315 pub fn is_complete(&self) -> bool {
317 self.current >= self.total
318 }
319}
320
321pub mod performance {
323 use std::collections::VecDeque;
324 use std::time::{Duration, Instant};
325
326 pub struct Timer {
328 start: Instant,
329 measurements: VecDeque<Duration>,
330 max_samples: usize,
331 }
332
333 impl Timer {
334 pub fn new() -> Self {
336 Self {
337 start: Instant::now(),
338 measurements: VecDeque::new(),
339 max_samples: 100,
340 }
341 }
342
343 pub fn start(&mut self) {
345 self.start = Instant::now();
346 }
347
348 pub fn stop(&mut self) -> Duration {
350 let duration = self.start.elapsed();
351
352 if self.measurements.len() >= self.max_samples {
353 self.measurements.pop_front();
354 }
355 self.measurements.push_back(duration);
356
357 duration
358 }
359
360 pub fn average(&self) -> Option<Duration> {
362 if self.measurements.is_empty() {
363 None
364 } else {
365 let total: Duration = self.measurements.iter().sum();
366 Some(total / self.measurements.len() as u32)
367 }
368 }
369
370 pub fn throughput(&self, items: usize) -> Option<f64> {
372 self.average().map(|avg| items as f64 / avg.as_secs_f64())
373 }
374 }
375
376 impl Default for Timer {
377 fn default() -> Self {
378 Self::new()
379 }
380 }
381}
382
383pub mod memory {
385 use std::collections::HashMap;
386 use std::sync::{Arc, Mutex};
387
388 pub struct MemoryPool<T> {
390 available: Vec<Vec<T>>,
391 capacity: usize,
392 default_size: usize,
393 }
394
395 impl<T: Clone + Default> MemoryPool<T> {
396 pub fn new(capacity: usize, default_size: usize) -> Self {
398 Self {
399 available: Vec::with_capacity(capacity),
400 capacity,
401 default_size,
402 }
403 }
404
405 pub fn get(&mut self) -> Vec<T> {
407 self.available
408 .pop()
409 .unwrap_or_else(|| Vec::with_capacity(self.default_size))
410 }
411
412 pub fn put(&mut self, mut buffer: Vec<T>) {
414 if self.available.len() < self.capacity {
415 buffer.clear();
416 self.available.push(buffer);
417 }
418 }
419
420 pub fn size(&self) -> usize {
422 self.available.len()
423 }
424 }
425
426 pub type SharedMemoryPool<T> = Arc<Mutex<MemoryPool<T>>>;
428
429 pub struct MemoryTracker {
431 allocations: HashMap<String, usize>,
432 peak_memory: usize,
433 current_memory: usize,
434 }
435
436 impl MemoryTracker {
437 pub fn new() -> Self {
439 Self {
440 allocations: HashMap::new(),
441 peak_memory: 0,
442 current_memory: 0,
443 }
444 }
445
446 pub fn allocate(&mut self, name: &str, size: usize) {
448 self.current_memory += size;
449 self.peak_memory = self.peak_memory.max(self.current_memory);
450 *self.allocations.entry(name.to_string()).or_insert(0) += size;
451 }
452
453 pub fn deallocate(&mut self, name: &str, size: usize) {
455 self.current_memory = self.current_memory.saturating_sub(size);
456 if let Some(total) = self.allocations.get_mut(name) {
457 *total = total.saturating_sub(size);
458 }
459 }
460
461 pub fn current_usage(&self) -> usize {
463 self.current_memory
464 }
465
466 pub fn peak_usage(&self) -> usize {
468 self.peak_memory
469 }
470
471 pub fn breakdown(&self) -> &HashMap<String, usize> {
473 &self.allocations
474 }
475 }
476
477 impl Default for MemoryTracker {
478 fn default() -> Self {
479 Self::new()
480 }
481 }
482}
483
484pub mod batch {
486 use scirs2_core::parallel_ops::*;
488 use std::sync::mpsc;
489 use std::thread;
490
491 pub fn parallel_batch_process<T, R, F>(data: Vec<T>, batch_size: usize, processor: F) -> Vec<R>
493 where
494 T: Send + Sync,
495 R: Send,
496 F: Fn(&[T]) -> R + Send + Sync,
497 {
498 data.par_chunks(batch_size).map(processor).collect()
499 }
500
501 pub struct AsyncBatchProcessor<T, R> {
503 sender: mpsc::Sender<Vec<T>>,
504 receiver: mpsc::Receiver<R>,
505 _handle: thread::JoinHandle<()>,
506 }
507
508 impl<T, R> AsyncBatchProcessor<T, R>
509 where
510 T: Send + 'static,
511 R: Send + 'static,
512 {
513 pub fn new<F>(batch_size: usize, processor: F) -> Self
515 where
516 F: Fn(Vec<T>) -> R + Send + 'static,
517 {
518 let (input_sender, input_receiver) = mpsc::channel();
519 let (output_sender, output_receiver) = mpsc::channel();
520
521 let handle = thread::spawn(move || {
522 let mut buffer = Vec::with_capacity(batch_size);
523
524 while let Ok(mut data) = input_receiver.recv() {
525 buffer.append(&mut data);
526
527 while buffer.len() >= batch_size {
528 let batch = buffer.drain(..batch_size).collect();
529 let result = processor(batch);
530 if output_sender.send(result).is_err() {
531 break;
532 }
533 }
534 }
535
536 if !buffer.is_empty() {
538 let result = processor(buffer);
539 let _ = output_sender.send(result);
540 }
541 });
542
543 Self {
544 sender: input_sender,
545 receiver: output_receiver,
546 _handle: handle,
547 }
548 }
549
550 pub fn send(&self, data: Vec<T>) -> Result<(), mpsc::SendError<Vec<T>>> {
552 self.sender.send(data)
553 }
554
555 pub fn recv(&self) -> Result<R, mpsc::RecvError> {
557 self.receiver.recv()
558 }
559
560 pub fn try_recv(&self) -> Result<R, mpsc::TryRecvError> {
562 self.receiver.try_recv()
563 }
564 }
565}
566
567pub mod concurrent {
569 use parking_lot::{Mutex, RwLock};
570 use std::collections::HashMap;
571 use std::sync::Arc;
572
573 pub struct ConcurrentCache<K, V>
575 where
576 K: Eq + std::hash::Hash,
577 {
578 data: Arc<RwLock<HashMap<K, V>>>,
579 max_size: usize,
580 }
581
582 impl<K, V> ConcurrentCache<K, V>
583 where
584 K: Eq + std::hash::Hash + Clone,
585 V: Clone,
586 {
587 pub fn new(max_size: usize) -> Self {
589 Self {
590 data: Arc::new(RwLock::new(HashMap::new())),
591 max_size,
592 }
593 }
594
595 pub fn get(&self, key: &K) -> Option<V> {
597 self.data.read().get(key).cloned()
598 }
599
600 pub fn insert(&self, key: K, value: V) {
602 let mut data = self.data.write();
603
604 if data.len() >= self.max_size && !data.contains_key(&key) {
606 if let Some(first_key) = data.keys().next().cloned() {
607 data.remove(&first_key);
608 }
609 }
610
611 data.insert(key, value);
612 }
613
614 pub fn remove(&self, key: &K) -> Option<V> {
616 self.data.write().remove(key)
617 }
618
619 pub fn clear(&self) {
621 self.data.write().clear();
622 }
623
624 pub fn len(&self) -> usize {
626 self.data.read().len()
627 }
628
629 pub fn is_empty(&self) -> bool {
631 self.data.read().is_empty()
632 }
633 }
634
635 pub struct StatisticsCollector {
637 data: Arc<Mutex<Vec<f64>>>,
638 max_samples: usize,
639 }
640
641 impl StatisticsCollector {
642 pub fn new(max_samples: usize) -> Self {
644 Self {
645 data: Arc::new(Mutex::new(Vec::new())),
646 max_samples,
647 }
648 }
649
650 pub fn add_sample(&self, value: f64) {
652 let mut data = self.data.lock();
653 if data.len() >= self.max_samples {
654 data.remove(0);
655 }
656 data.push(value);
657 }
658
659 pub fn mean(&self) -> Option<f64> {
661 let data = self.data.lock();
662 if data.is_empty() {
663 None
664 } else {
665 Some(data.iter().sum::<f64>() / data.len() as f64)
666 }
667 }
668
669 pub fn std_dev(&self) -> Option<f64> {
671 let data = self.data.lock();
672 if data.len() < 2 {
673 None
674 } else {
675 let mean = data.iter().sum::<f64>() / data.len() as f64;
676 let variance =
677 data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (data.len() - 1) as f64;
678 Some(variance.sqrt())
679 }
680 }
681
682 pub fn min_max(&self) -> Option<(f64, f64)> {
684 let data = self.data.lock();
685 if data.is_empty() {
686 None
687 } else {
688 let min = data.iter().fold(f64::INFINITY, |a, &b| a.min(b));
689 let max = data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
690 Some((min, max))
691 }
692 }
693
694 pub fn count(&self) -> usize {
696 self.data.lock().len()
697 }
698 }
699}
700
701pub mod config {
703 use std::collections::HashMap;
704 use std::env;
705 use std::path::Path;
706
707 #[cfg(feature = "serialize")]
708 use std::fs;
709 use torsh_core::error::{Result, TorshError};
710
711 #[cfg(feature = "serialize")]
712 use serde::{Deserialize, Serialize};
713
714 pub struct ConfigManager {
716 values: HashMap<String, ConfigValue>,
717 env_prefix: String,
718 }
719
720 #[derive(Debug, Clone, PartialEq)]
722 #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
723 pub enum ConfigValue {
724 String(String),
725 Integer(i64),
726 Float(f64),
727 Boolean(bool),
728 Array(Vec<ConfigValue>),
729 Object(HashMap<String, ConfigValue>),
730 }
731
732 impl ConfigValue {
733 pub fn as_string(&self) -> Option<&str> {
735 match self {
736 ConfigValue::String(s) => Some(s),
737 _ => None,
738 }
739 }
740
741 pub fn as_i64(&self) -> Option<i64> {
743 match self {
744 ConfigValue::Integer(i) => Some(*i),
745 _ => None,
746 }
747 }
748
749 pub fn as_f64(&self) -> Option<f64> {
751 match self {
752 ConfigValue::Float(f) => Some(*f),
753 ConfigValue::Integer(i) => Some(*i as f64),
754 _ => None,
755 }
756 }
757
758 pub fn as_bool(&self) -> Option<bool> {
760 match self {
761 ConfigValue::Boolean(b) => Some(*b),
762 _ => None,
763 }
764 }
765
766 pub fn as_array(&self) -> Option<&Vec<ConfigValue>> {
768 match self {
769 ConfigValue::Array(arr) => Some(arr),
770 _ => None,
771 }
772 }
773
774 pub fn as_object(&self) -> Option<&HashMap<String, ConfigValue>> {
776 match self {
777 ConfigValue::Object(obj) => Some(obj),
778 _ => None,
779 }
780 }
781 }
782
783 impl ConfigManager {
784 pub fn new() -> Self {
786 Self {
787 values: HashMap::new(),
788 env_prefix: "TORSH_".to_string(),
789 }
790 }
791
792 pub fn with_env_prefix(prefix: &str) -> Self {
794 Self {
795 values: HashMap::new(),
796 env_prefix: prefix.to_string(),
797 }
798 }
799
800 #[cfg(feature = "serialize")]
802 pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
803 let content = fs::read_to_string(path).map_err(|e| {
804 TorshError::InvalidArgument(format!("Failed to read config file: {}", e))
805 })?;
806
807 let json_value: serde_json::Value = serde_json::from_str(&content)
808 .map_err(|e| TorshError::InvalidArgument(format!("Failed to parse JSON: {}", e)))?;
809
810 self.load_from_json_value("", &json_value);
811 Ok(())
812 }
813
814 #[cfg(not(feature = "serialize"))]
816 pub fn load_from_file<P: AsRef<Path>>(&mut self, _path: P) -> Result<()> {
817 Err(TorshError::InvalidArgument(
818 "JSON loading requires 'serialize' feature. Enable with --features serialize"
819 .to_string(),
820 ))
821 }
822
823 pub fn load_from_env(&mut self) {
825 for (key, value) in env::vars() {
826 if key.starts_with(&self.env_prefix) {
827 let config_key = key
828 .strip_prefix(&self.env_prefix)
829 .expect("prefix exists as checked in starts_with")
830 .to_lowercase();
831 self.set_from_string(&config_key, &value);
832 }
833 }
834 }
835
836 pub fn set(&mut self, key: &str, value: ConfigValue) {
838 self.values.insert(key.to_string(), value);
839 }
840
841 pub fn get(&self, key: &str) -> Option<&ConfigValue> {
843 self.values.get(key)
844 }
845
846 pub fn get_string(&self, key: &str, default: &str) -> String {
848 self.get(key)
849 .and_then(|v| v.as_string())
850 .unwrap_or(default)
851 .to_string()
852 }
853
854 pub fn get_i64(&self, key: &str, default: i64) -> i64 {
856 self.get(key).and_then(|v| v.as_i64()).unwrap_or(default)
857 }
858
859 pub fn get_f64(&self, key: &str, default: f64) -> f64 {
861 self.get(key).and_then(|v| v.as_f64()).unwrap_or(default)
862 }
863
864 pub fn get_bool(&self, key: &str, default: bool) -> bool {
866 self.get(key).and_then(|v| v.as_bool()).unwrap_or(default)
867 }
868
869 pub fn contains_key(&self, key: &str) -> bool {
871 self.values.contains_key(key)
872 }
873
874 pub fn keys(&self) -> Vec<&String> {
876 self.values.keys().collect()
877 }
878
879 pub fn clear(&mut self) {
881 self.values.clear();
882 }
883
884 pub fn merge(&mut self, other: &ConfigManager) {
886 for (key, value) in &other.values {
887 self.values.insert(key.clone(), value.clone());
888 }
889 }
890
891 fn set_from_string(&mut self, key: &str, value: &str) {
893 if let Ok(b) = value.parse::<bool>() {
895 self.set(key, ConfigValue::Boolean(b));
896 } else if let Ok(i) = value.parse::<i64>() {
897 self.set(key, ConfigValue::Integer(i));
898 } else if let Ok(f) = value.parse::<f64>() {
899 self.set(key, ConfigValue::Float(f));
900 } else {
901 self.set(key, ConfigValue::String(value.to_string()));
902 }
903 }
904
905 #[cfg(feature = "serialize")]
907 fn load_from_json_value(&mut self, prefix: &str, value: &serde_json::Value) {
908 match value {
909 serde_json::Value::String(s) => {
910 self.set(prefix, ConfigValue::String(s.clone()));
911 }
912 serde_json::Value::Number(n) => {
913 if let Some(i) = n.as_i64() {
914 self.set(prefix, ConfigValue::Integer(i));
915 } else if let Some(f) = n.as_f64() {
916 self.set(prefix, ConfigValue::Float(f));
917 }
918 }
919 serde_json::Value::Bool(b) => {
920 self.set(prefix, ConfigValue::Boolean(*b));
921 }
922 serde_json::Value::Array(arr) => {
923 let config_arr: Vec<ConfigValue> = arr
924 .iter()
925 .map(|v| self.json_value_to_config_value(v))
926 .collect();
927 self.set(prefix, ConfigValue::Array(config_arr));
928 }
929 serde_json::Value::Object(obj) => {
930 for (key, val) in obj {
931 let new_key = if prefix.is_empty() {
932 key.clone()
933 } else {
934 format!("{}.{}", prefix, key)
935 };
936 self.load_from_json_value(&new_key, val);
937 }
938 }
939 serde_json::Value::Null => {}
940 }
941 }
942
943 #[cfg(feature = "serialize")]
945 fn json_value_to_config_value(&self, value: &serde_json::Value) -> ConfigValue {
946 match value {
947 serde_json::Value::String(s) => ConfigValue::String(s.clone()),
948 serde_json::Value::Number(n) => {
949 if let Some(i) = n.as_i64() {
950 ConfigValue::Integer(i)
951 } else if let Some(f) = n.as_f64() {
952 ConfigValue::Float(f)
953 } else {
954 ConfigValue::String(n.to_string())
955 }
956 }
957 serde_json::Value::Bool(b) => ConfigValue::Boolean(*b),
958 serde_json::Value::Array(arr) => {
959 let config_arr: Vec<ConfigValue> = arr
960 .iter()
961 .map(|v| self.json_value_to_config_value(v))
962 .collect();
963 ConfigValue::Array(config_arr)
964 }
965 serde_json::Value::Object(obj) => {
966 let mut config_obj = HashMap::new();
967 for (key, val) in obj {
968 config_obj.insert(key.clone(), self.json_value_to_config_value(val));
969 }
970 ConfigValue::Object(config_obj)
971 }
972 serde_json::Value::Null => ConfigValue::String("null".to_string()),
973 }
974 }
975 }
976
977 impl Default for ConfigManager {
978 fn default() -> Self {
979 Self::new()
980 }
981 }
982
983 pub struct ConfigBuilder {
985 manager: ConfigManager,
986 }
987
988 impl ConfigBuilder {
989 pub fn new() -> Self {
991 Self {
992 manager: ConfigManager::new(),
993 }
994 }
995
996 pub fn env_prefix(mut self, prefix: &str) -> Self {
998 self.manager.env_prefix = prefix.to_string();
999 self
1000 }
1001
1002 #[cfg(feature = "serialize")]
1004 pub fn file<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
1005 self.manager.load_from_file(path)?;
1006 Ok(self)
1007 }
1008
1009 #[cfg(not(feature = "serialize"))]
1011 pub fn file<P: AsRef<Path>>(self, _path: P) -> Result<Self> {
1012 Err(TorshError::InvalidArgument(
1013 "JSON file loading requires 'serialize' feature. Enable with --features serialize"
1014 .to_string(),
1015 ))
1016 }
1017
1018 pub fn env(mut self) -> Self {
1020 self.manager.load_from_env();
1021 self
1022 }
1023
1024 pub fn set(mut self, key: &str, value: ConfigValue) -> Self {
1026 self.manager.set(key, value);
1027 self
1028 }
1029
1030 pub fn set_string(mut self, key: &str, value: &str) -> Self {
1032 self.manager
1033 .set(key, ConfigValue::String(value.to_string()));
1034 self
1035 }
1036
1037 pub fn set_i64(mut self, key: &str, value: i64) -> Self {
1039 self.manager.set(key, ConfigValue::Integer(value));
1040 self
1041 }
1042
1043 pub fn set_f64(mut self, key: &str, value: f64) -> Self {
1045 self.manager.set(key, ConfigValue::Float(value));
1046 self
1047 }
1048
1049 pub fn set_bool(mut self, key: &str, value: bool) -> Self {
1051 self.manager.set(key, ConfigValue::Boolean(value));
1052 self
1053 }
1054
1055 pub fn build(self) -> ConfigManager {
1057 self.manager
1058 }
1059 }
1060
1061 impl Default for ConfigBuilder {
1062 fn default() -> Self {
1063 Self::new()
1064 }
1065 }
1066}
1067
1068pub use builder_pattern;
1070pub use simple_random_transform;
1071pub use validated_constructor;
1072
1073#[cfg(test)]
1074mod tests {
1075 use super::*;
1076 use std::thread;
1077 use std::time::Duration;
1078
1079 #[test]
1080 fn test_validate_probability() {
1081 assert!(validate_probability(0.5, "test").is_ok());
1082 assert!(validate_probability(0.0, "test").is_ok());
1083 assert!(validate_probability(1.0, "test").is_ok());
1084 assert!(validate_probability(-0.1, "test").is_err());
1085 assert!(validate_probability(1.1, "test").is_err());
1086 }
1087
1088 #[test]
1089 fn test_validate_range() {
1090 assert!(validate_range((0.0, 1.0), "test").is_ok());
1091 assert!(validate_range((1.0, 1.0), "test").is_ok());
1092 assert!(validate_range((1.0, 0.0), "test").is_err());
1093 }
1094
1095 #[test]
1096 fn test_validate_positive() {
1097 assert!(validate_positive(1, "test").is_ok());
1098 assert!(validate_positive(0, "test").is_err());
1099 assert!(validate_positive(-1, "test").is_err());
1100 }
1101
1102 #[test]
1103 fn test_progress_tracker() {
1104 let mut tracker = ProgressTracker::new(10);
1105 assert!(!tracker.is_complete());
1106
1107 assert!(tracker.update()); assert!(tracker.update()); for _ in 0..8 {
1112 tracker.update();
1113 }
1114 assert!(tracker.is_complete());
1115 }
1116
1117 #[test]
1118 fn test_performance_timer() {
1119 let mut timer = performance::Timer::new();
1120 timer.start();
1121 thread::sleep(Duration::from_millis(10));
1122 let duration = timer.stop();
1123
1124 assert!(duration >= Duration::from_millis(10));
1125 assert!(timer.average().is_some());
1126 assert!(timer.throughput(100).is_some());
1127 }
1128
1129 #[test]
1130 fn test_memory_pool() {
1131 let mut pool = memory::MemoryPool::<u8>::new(5, 1024);
1132 assert_eq!(pool.size(), 0);
1133
1134 let buffer1 = pool.get();
1135 assert_eq!(buffer1.capacity(), 1024);
1136
1137 pool.put(buffer1);
1138 assert_eq!(pool.size(), 1);
1139
1140 let buffer2 = pool.get();
1141 assert_eq!(pool.size(), 0);
1142 assert_eq!(buffer2.len(), 0); }
1144
1145 #[test]
1146 fn test_memory_tracker() {
1147 let mut tracker = memory::MemoryTracker::new();
1148 assert_eq!(tracker.current_usage(), 0);
1149
1150 tracker.allocate("test", 1024);
1151 assert_eq!(tracker.current_usage(), 1024);
1152 assert_eq!(tracker.peak_usage(), 1024);
1153
1154 tracker.deallocate("test", 512);
1155 assert_eq!(tracker.current_usage(), 512);
1156 assert_eq!(tracker.peak_usage(), 1024); }
1158
1159 #[test]
1160 fn test_parallel_batch_process() {
1161 let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1162 let results = batch::parallel_batch_process(data, 3, |chunk| chunk.iter().sum::<i32>());
1163
1164 assert_eq!(results.len(), 4); assert_eq!(results[0], 6); assert_eq!(results[1], 15); assert_eq!(results[2], 24); assert_eq!(results[3], 10); }
1170
1171 #[test]
1172 fn test_concurrent_cache() {
1173 let cache = concurrent::ConcurrentCache::new(2);
1174
1175 cache.insert("key1", "value1");
1176 cache.insert("key2", "value2");
1177 assert_eq!(cache.len(), 2);
1178
1179 assert_eq!(cache.get(&"key1"), Some("value1"));
1180 assert_eq!(cache.get(&"key2"), Some("value2"));
1181 assert_eq!(cache.get(&"key3"), None);
1182
1183 cache.insert("key3", "value3");
1185 assert_eq!(cache.len(), 2); }
1187
1188 #[test]
1189 fn test_statistics_collector() {
1190 let collector = concurrent::StatisticsCollector::new(100);
1191 assert_eq!(collector.count(), 0);
1192
1193 collector.add_sample(1.0);
1194 collector.add_sample(2.0);
1195 collector.add_sample(3.0);
1196
1197 assert_eq!(collector.count(), 3);
1198 assert_eq!(collector.mean(), Some(2.0));
1199 assert_eq!(collector.min_max(), Some((1.0, 3.0)));
1200 assert!(collector.std_dev().is_some());
1201 }
1202
1203 #[test]
1204 fn test_config_manager() {
1205 let mut config = config::ConfigManager::new();
1206
1207 config.set(
1209 "string_key",
1210 config::ConfigValue::String("test_value".to_string()),
1211 );
1212 config.set("int_key", config::ConfigValue::Integer(42));
1213 config.set("float_key", config::ConfigValue::Float(3.14));
1214 config.set("bool_key", config::ConfigValue::Boolean(true));
1215
1216 assert_eq!(config.get_string("string_key", "default"), "test_value");
1217 assert_eq!(config.get_i64("int_key", 0), 42);
1218 assert_eq!(config.get_f64("float_key", 0.0), 3.14);
1219 assert!(config.get_bool("bool_key", false));
1220
1221 assert_eq!(config.get_string("missing_key", "default"), "default");
1223 assert_eq!(config.get_i64("missing_key", 123), 123);
1224
1225 assert!(config.contains_key("string_key"));
1227 assert!(!config.contains_key("missing_key"));
1228
1229 let keys = config.keys();
1231 assert!(keys.len() >= 4);
1232
1233 config.clear();
1235 assert_eq!(config.keys().len(), 0);
1236 }
1237
1238 #[test]
1239 fn test_config_value_conversions() {
1240 let string_val = config::ConfigValue::String("test".to_string());
1241 let int_val = config::ConfigValue::Integer(42);
1242 let float_val = config::ConfigValue::Float(3.14);
1243 let bool_val = config::ConfigValue::Boolean(true);
1244
1245 assert_eq!(string_val.as_string(), Some("test"));
1246 assert_eq!(int_val.as_i64(), Some(42));
1247 assert_eq!(float_val.as_f64(), Some(3.14));
1248 assert_eq!(bool_val.as_bool(), Some(true));
1249
1250 assert_eq!(string_val.as_i64(), None);
1252 assert_eq!(int_val.as_string(), None);
1253
1254 assert_eq!(int_val.as_f64(), Some(42.0));
1256 }
1257
1258 #[test]
1259 fn test_config_builder() {
1260 let config = config::ConfigBuilder::new()
1261 .env_prefix("TEST_")
1262 .set_string("app_name", "torsh-data")
1263 .set_i64("version", 1)
1264 .set_f64("threshold", 0.5)
1265 .set_bool("debug", true)
1266 .build();
1267
1268 assert_eq!(config.get_string("app_name", ""), "torsh-data");
1269 assert_eq!(config.get_i64("version", 0), 1);
1270 assert_eq!(config.get_f64("threshold", 0.0), 0.5);
1271 assert!(config.get_bool("debug", false));
1272 }
1273
1274 #[test]
1275 fn test_config_merge() {
1276 let mut config1 = config::ConfigManager::new();
1277 config1.set("key1", config::ConfigValue::String("value1".to_string()));
1278 config1.set("key2", config::ConfigValue::Integer(42));
1279
1280 let mut config2 = config::ConfigManager::new();
1281 config2.set("key2", config::ConfigValue::Integer(100)); config2.set("key3", config::ConfigValue::Boolean(true)); config1.merge(&config2);
1285
1286 assert_eq!(config1.get_string("key1", ""), "value1");
1287 assert_eq!(config1.get_i64("key2", 0), 100); assert!(config1.get_bool("key3", false));
1289 }
1290}