Skip to main content

sklears_core/
features.rs

1/// Feature flag management and compile-time configuration
2///
3/// This module provides utilities for managing feature flags and compile-time
4/// configuration options. It enables fine-grained control over which parts
5/// of the library are included in the final binary.
6use std::collections::HashMap;
7
8/// Compile-time feature detection
9pub struct Features;
10
11impl Features {
12    /// Check if standard library support is enabled
13    pub const fn has_std() -> bool {
14        cfg!(feature = "std")
15    }
16
17    /// Check if no_std mode is enabled
18    pub const fn has_no_std() -> bool {
19        cfg!(feature = "no_std")
20    }
21
22    /// Check if serde support is enabled
23    pub const fn has_serde() -> bool {
24        cfg!(feature = "serde")
25    }
26
27    /// Check if binary serialization is enabled
28    pub const fn has_binary() -> bool {
29        cfg!(feature = "binary")
30    }
31
32    /// Check if Arrow integration is enabled
33    pub const fn has_arrow() -> bool {
34        cfg!(feature = "arrow")
35    }
36
37    /// Check if SIMD support is enabled
38    pub const fn has_simd() -> bool {
39        cfg!(feature = "simd")
40    }
41
42    /// Check if parallel processing is enabled
43    pub const fn has_parallel() -> bool {
44        cfg!(feature = "parallel")
45    }
46
47    /// Check if memory mapping is enabled
48    pub const fn has_mmap() -> bool {
49        cfg!(feature = "mmap")
50    }
51
52    /// Check if async support is enabled
53    pub const fn has_async() -> bool {
54        cfg!(feature = "async_support")
55    }
56
57    /// Check if streaming support is enabled
58    pub const fn has_streaming() -> bool {
59        cfg!(feature = "streaming")
60    }
61
62    /// Check if GPU support is enabled
63    pub const fn has_gpu() -> bool {
64        cfg!(feature = "gpu_support")
65    }
66
67    /// Check if distributed computing is enabled
68    pub const fn has_distributed() -> bool {
69        cfg!(feature = "distributed")
70    }
71
72    /// Algorithm category checks
73    pub const fn has_linear_models() -> bool {
74        cfg!(feature = "linear_models")
75    }
76
77    pub const fn has_tree_models() -> bool {
78        cfg!(feature = "tree_models")
79    }
80
81    pub const fn has_neural_networks() -> bool {
82        cfg!(feature = "neural_networks")
83    }
84
85    pub const fn has_clustering() -> bool {
86        cfg!(feature = "clustering")
87    }
88
89    pub const fn has_dimensionality_reduction() -> bool {
90        cfg!(feature = "dimensionality_reduction")
91    }
92
93    pub const fn has_ensemble_methods() -> bool {
94        cfg!(feature = "ensemble_methods")
95    }
96
97    /// Utility feature checks
98    pub const fn has_validation() -> bool {
99        cfg!(feature = "validation")
100    }
101
102    pub const fn has_metrics() -> bool {
103        cfg!(feature = "metrics")
104    }
105
106    pub const fn has_preprocessing() -> bool {
107        cfg!(feature = "preprocessing")
108    }
109
110    pub const fn has_model_selection() -> bool {
111        cfg!(feature = "model_selection")
112    }
113
114    /// Development feature checks
115    pub const fn has_debug_assertions() -> bool {
116        cfg!(feature = "debug_assertions")
117    }
118
119    pub const fn has_profiling() -> bool {
120        cfg!(feature = "profiling")
121    }
122
123    pub const fn has_benchmarking() -> bool {
124        // Benchmarking feature not currently implemented
125        false
126    }
127
128    /// Get all enabled features as a HashMap
129    pub fn enabled_features() -> HashMap<&'static str, bool> {
130        let mut features = HashMap::new();
131
132        // Core features
133        features.insert("std", Self::has_std());
134        features.insert("no_std", Self::has_no_std());
135
136        // Serialization features
137        features.insert("serde", Self::has_serde());
138        features.insert("binary", Self::has_binary());
139
140        // Data format support
141        features.insert("arrow", Self::has_arrow());
142
143        // Performance features
144        features.insert("simd", Self::has_simd());
145        features.insert("parallel", Self::has_parallel());
146        features.insert("mmap", Self::has_mmap());
147
148        // Algorithm categories
149        features.insert("linear_models", Self::has_linear_models());
150        features.insert("tree_models", Self::has_tree_models());
151        features.insert("neural_networks", Self::has_neural_networks());
152        features.insert("clustering", Self::has_clustering());
153        features.insert(
154            "dimensionality_reduction",
155            Self::has_dimensionality_reduction(),
156        );
157        features.insert("ensemble_methods", Self::has_ensemble_methods());
158
159        // Advanced features
160        features.insert("async_support", Self::has_async());
161        features.insert("streaming", Self::has_streaming());
162        features.insert("gpu_support", Self::has_gpu());
163        features.insert("distributed", Self::has_distributed());
164
165        // Utility features
166        features.insert("validation", Self::has_validation());
167        features.insert("metrics", Self::has_metrics());
168        features.insert("preprocessing", Self::has_preprocessing());
169        features.insert("model_selection", Self::has_model_selection());
170
171        // Development features
172        features.insert("debug_assertions", Self::has_debug_assertions());
173        features.insert("profiling", Self::has_profiling());
174        features.insert("benchmarking", Self::has_benchmarking());
175
176        features
177    }
178
179    /// Print all enabled features (useful for debugging)
180    pub fn print_enabled_features() {
181        println!("Enabled features:");
182        for (feature, enabled) in Self::enabled_features() {
183            if enabled {
184                println!("  - {feature}");
185            }
186        }
187    }
188
189    /// Get a summary of enabled feature categories
190    pub fn feature_summary() -> FeatureSummary {
191        FeatureSummary {
192            core: CoreFeatures {
193                std: Self::has_std(),
194                no_std: Self::has_no_std(),
195            },
196            serialization: SerializationFeatures {
197                serde: Self::has_serde(),
198                binary: Self::has_binary(),
199            },
200            data_formats: DataFormatFeatures {
201                arrow: Self::has_arrow(),
202            },
203            performance: PerformanceFeatures {
204                simd: Self::has_simd(),
205                parallel: Self::has_parallel(),
206                mmap: Self::has_mmap(),
207            },
208            algorithms: AlgorithmFeatures {
209                linear_models: Self::has_linear_models(),
210                tree_models: Self::has_tree_models(),
211                neural_networks: Self::has_neural_networks(),
212                clustering: Self::has_clustering(),
213                dimensionality_reduction: Self::has_dimensionality_reduction(),
214                ensemble_methods: Self::has_ensemble_methods(),
215            },
216            advanced: AdvancedFeatures {
217                async_support: Self::has_async(),
218                streaming: Self::has_streaming(),
219                gpu_support: Self::has_gpu(),
220                distributed: Self::has_distributed(),
221            },
222            utilities: UtilityFeatures {
223                validation: Self::has_validation(),
224                metrics: Self::has_metrics(),
225                preprocessing: Self::has_preprocessing(),
226                model_selection: Self::has_model_selection(),
227            },
228            development: DevelopmentFeatures {
229                debug_assertions: Self::has_debug_assertions(),
230                profiling: Self::has_profiling(),
231                benchmarking: Self::has_benchmarking(),
232            },
233        }
234    }
235}
236
237/// Summary of all feature categories
238#[derive(Debug, Clone)]
239pub struct FeatureSummary {
240    pub core: CoreFeatures,
241    pub serialization: SerializationFeatures,
242    pub data_formats: DataFormatFeatures,
243    pub performance: PerformanceFeatures,
244    pub algorithms: AlgorithmFeatures,
245    pub advanced: AdvancedFeatures,
246    pub utilities: UtilityFeatures,
247    pub development: DevelopmentFeatures,
248}
249
250/// Core library features
251#[derive(Debug, Clone)]
252pub struct CoreFeatures {
253    pub std: bool,
254    pub no_std: bool,
255}
256
257/// Serialization-related features
258#[derive(Debug, Clone)]
259pub struct SerializationFeatures {
260    pub serde: bool,
261    pub binary: bool,
262}
263
264/// Data format support features
265#[derive(Debug, Clone)]
266pub struct DataFormatFeatures {
267    pub arrow: bool,
268}
269
270/// Performance optimization features
271#[derive(Debug, Clone)]
272pub struct PerformanceFeatures {
273    pub simd: bool,
274    pub parallel: bool,
275    pub mmap: bool,
276}
277
278/// Algorithm category features
279#[derive(Debug, Clone)]
280pub struct AlgorithmFeatures {
281    pub linear_models: bool,
282    pub tree_models: bool,
283    pub neural_networks: bool,
284    pub clustering: bool,
285    pub dimensionality_reduction: bool,
286    pub ensemble_methods: bool,
287}
288
289impl AlgorithmFeatures {
290    /// Check if any algorithm category is enabled
291    pub fn any_enabled(&self) -> bool {
292        self.linear_models
293            || self.tree_models
294            || self.neural_networks
295            || self.clustering
296            || self.dimensionality_reduction
297            || self.ensemble_methods
298    }
299
300    /// Count how many algorithm categories are enabled
301    pub fn count_enabled(&self) -> usize {
302        [
303            self.linear_models,
304            self.tree_models,
305            self.neural_networks,
306            self.clustering,
307            self.dimensionality_reduction,
308            self.ensemble_methods,
309        ]
310        .iter()
311        .filter(|&&enabled| enabled)
312        .count()
313    }
314
315    /// Get list of enabled algorithm categories
316    pub fn enabled_categories(&self) -> Vec<&'static str> {
317        let mut categories = Vec::new();
318        if self.linear_models {
319            categories.push("linear_models");
320        }
321        if self.tree_models {
322            categories.push("tree_models");
323        }
324        if self.neural_networks {
325            categories.push("neural_networks");
326        }
327        if self.clustering {
328            categories.push("clustering");
329        }
330        if self.dimensionality_reduction {
331            categories.push("dimensionality_reduction");
332        }
333        if self.ensemble_methods {
334            categories.push("ensemble_methods");
335        }
336        categories
337    }
338}
339
340/// Advanced features
341#[derive(Debug, Clone)]
342pub struct AdvancedFeatures {
343    pub async_support: bool,
344    pub streaming: bool,
345    pub gpu_support: bool,
346    pub distributed: bool,
347}
348
349/// Utility features
350#[derive(Debug, Clone)]
351pub struct UtilityFeatures {
352    pub validation: bool,
353    pub metrics: bool,
354    pub preprocessing: bool,
355    pub model_selection: bool,
356}
357
358/// Development and debugging features
359#[derive(Debug, Clone)]
360pub struct DevelopmentFeatures {
361    pub debug_assertions: bool,
362    pub profiling: bool,
363    pub benchmarking: bool,
364}
365
366/// Compile-time feature validation
367pub mod validation {
368    use super::Features;
369
370    /// Check for conflicting feature combinations
371    pub const fn validate_features() -> Result<(), &'static str> {
372        // Check for conflicting std/no_std features
373        // Note: When using --all-features, both std and no_std get enabled
374        // In this case, we default to std as it's the more permissive option
375        if Features::has_std() && Features::has_no_std() {
376            // This is expected when using --all-features, so we allow it
377            // std takes precedence over no_std in this case
378        }
379
380        // Check dependencies - Note: These dependencies should be handled by Cargo.toml
381        // but we keep validation for explicit feature conflict detection
382        if Features::has_binary() && !Features::has_serde() {
383            return Err("'binary' feature requires 'serde' feature");
384        }
385
386        // Note: streaming and distributed should automatically enable async_support via Cargo.toml
387        // These checks are for explicit verification only
388        if Features::has_streaming() && !Features::has_async() {
389            return Err("'streaming' feature requires 'async_support' feature");
390        }
391
392        if Features::has_distributed() && !Features::has_async() {
393            return Err("'distributed' feature requires 'async_support' feature");
394        }
395
396        // Ensure at least std or no_std is enabled
397        if !Features::has_std() && !Features::has_no_std() {
398            // Default to std if neither is explicitly specified
399            // This is handled by the default feature set
400        }
401
402        Ok(())
403    }
404
405    /// Validate features at compile time using const assertions
406    pub const fn assert_valid_features() {
407        match validate_features() {
408            Ok(()) => {}
409            Err(_) => panic!("Invalid feature combination detected"),
410        }
411    }
412}
413
414/// Runtime feature configuration
415pub struct FeatureConfig {
416    enabled_features: HashMap<String, bool>,
417}
418
419impl FeatureConfig {
420    /// Create a new feature configuration from current compile-time features
421    pub fn from_compile_time() -> Self {
422        let enabled = Features::enabled_features()
423            .into_iter()
424            .map(|(k, v)| (k.to_string(), v))
425            .collect();
426
427        Self {
428            enabled_features: enabled,
429        }
430    }
431
432    /// Check if a feature is enabled
433    pub fn is_enabled(&self, feature: &str) -> bool {
434        self.enabled_features.get(feature).copied().unwrap_or(false)
435    }
436
437    /// Get all enabled features
438    pub fn enabled_features(&self) -> Vec<&str> {
439        self.enabled_features
440            .iter()
441            .filter_map(|(k, &v)| if v { Some(k.as_str()) } else { None })
442            .collect()
443    }
444
445    /// Get configuration as JSON string (requires serde feature)
446    #[cfg(feature = "serde")]
447    pub fn to_json(&self) -> Result<String, serde_json::Error> {
448        serde_json::to_string_pretty(&self.enabled_features)
449    }
450
451    /// Create configuration from JSON string (requires serde feature)
452    #[cfg(feature = "serde")]
453    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
454        let enabled_features = serde_json::from_str(json)?;
455        Ok(Self { enabled_features })
456    }
457}
458
459/// Macro for conditional compilation based on feature flags
460#[macro_export]
461macro_rules! cfg_feature {
462    ($feature:literal, $code:block) => {
463        #[cfg(feature = $feature)]
464        $code
465    };
466    ($feature:literal, $code:block, else $else_code:block) => {
467        #[cfg(feature = $feature)]
468        $code
469        #[cfg(not(feature = $feature))]
470        $else_code
471    };
472}
473
474/// Macro for conditional type definitions based on features
475#[macro_export]
476macro_rules! cfg_type {
477    ($feature:literal, $type_def:item) => {
478        #[cfg(feature = $feature)]
479        $type_def
480    };
481}
482
483/// Macro for feature-gated function implementations
484#[macro_export]
485macro_rules! cfg_impl {
486    ($feature:literal, impl $trait_name:ident for $type:ty { $($item:item)* }) => {
487        #[cfg(feature = $feature)]
488        impl $trait_name for $type {
489            $($item)*
490        }
491    };
492}
493
494#[allow(non_snake_case)]
495#[cfg(test)]
496mod tests {
497    use super::*;
498
499    #[test]
500    fn test_feature_detection() {
501        // Test that feature detection works
502        let summary = Features::feature_summary();
503
504        // Core features should have at least one enabled
505        assert!(summary.core.std || summary.core.no_std);
506
507        // Print enabled features for debugging
508        Features::print_enabled_features();
509    }
510
511    #[test]
512    fn test_feature_config() {
513        let config = FeatureConfig::from_compile_time();
514
515        // Should have some features enabled
516        assert!(!config.enabled_features().is_empty());
517
518        // Test specific feature checking
519        let has_std = config.is_enabled("std");
520        assert_eq!(has_std, Features::has_std());
521    }
522
523    #[test]
524    fn test_algorithm_features() {
525        let algo_features = Features::feature_summary().algorithms;
526
527        // Test counting and listing
528        let count = algo_features.count_enabled();
529        let categories = algo_features.enabled_categories();
530
531        assert_eq!(count, categories.len());
532    }
533
534    #[test]
535    fn test_feature_validation() {
536        // This should not panic if features are correctly configured
537        validation::assert_valid_features();
538
539        // Test validation function
540        assert!(validation::validate_features().is_ok());
541    }
542
543    #[cfg(feature = "serde")]
544    #[test]
545    fn test_feature_serialization() {
546        let config = FeatureConfig::from_compile_time();
547
548        // Test JSON serialization
549        let json = config.to_json().unwrap_or_default();
550        assert!(!json.is_empty());
551
552        // Test round-trip
553        let config2 = FeatureConfig::from_json(&json).expect("valid JSON");
554        assert_eq!(
555            config.enabled_features().len(),
556            config2.enabled_features().len()
557        );
558    }
559
560    #[test]
561    fn test_feature_macros() {
562        // Test cfg_feature macro
563        cfg_feature!("std", {
564            println!("std feature is enabled");
565        });
566
567        // Test else branch with a defined but likely disabled feature
568        cfg_feature!("gpu_support", {
569            println!("gpu_support feature is enabled");
570        }, else {
571            println!("gpu_support is not enabled (expected in most builds)");
572        });
573    }
574}