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