sklears_core/
public.rs

1/// Public API definitions and stability guarantees
2///
3/// This module clearly defines the public API surface of sklears-core.
4/// All items in this module are covered by semantic versioning guarantees.
5///
6/// # Stability Guarantees
7///
8/// - **Stable APIs**: Items marked as stable will not have breaking changes
9///   without a major version bump
10/// - **Experimental APIs**: Items marked as experimental may change in minor versions
11/// - **Deprecated APIs**: Items marked as deprecated will be removed in the next major version
12///
13/// # API Organization
14///
15/// The public API is organized into logical groups:
16/// - Core traits and types
17/// - Utility functions
18/// - Configuration types
19/// - Error handling
20use crate::error::{Result, SklearsError};
21
22// =============================================================================
23// Stable Public APIs
24// =============================================================================
25
26/// Re-export of stable core traits
27///
28/// These traits form the foundation of the sklears ecosystem and are
29/// guaranteed to remain stable across versions.
30pub mod stable {
31    pub use crate::traits::{
32        Estimator, Fit, FitPredict, FitTransform, PartialFit, Predict, Transform,
33    };
34
35    // Additional traits will be added here as they are implemented
36
37    pub use crate::types::{
38        Array1, Array2, ArrayView1, ArrayView2, ArrayViewMut1, ArrayViewMut2, FeatureCount,
39        Features, Float, FloatBounds, Int, IntBounds, Labels, Numeric, Predictions, Probabilities,
40        Probability, SampleCount, Target,
41    };
42
43    pub use crate::error::{ErrorChain, ErrorContext, Result, SklearsError};
44
45    pub use crate::validation::{Validate, ValidationContext, ValidationRule};
46
47    pub use crate::dataset::{load_iris, make_blobs, make_regression, Dataset};
48}
49
50/// Experimental APIs that may change
51///
52/// These APIs are newer and may undergo breaking changes in minor versions.
53/// Use with caution in production code.
54pub mod experimental {
55    pub use crate::async_traits::*;
56    pub use crate::traits::gat_traits::*;
57    pub use crate::traits::specialized::*;
58    pub use crate::traits::streaming::*;
59
60    pub use crate::plugin::{
61        AlgorithmPlugin, ClusteringPlugin, Plugin, PluginConfig, PluginMetadata, PluginRegistry,
62        TransformerPlugin,
63    };
64
65    pub use crate::parallel::{
66        ParallelConfig, ParallelCrossValidation, ParallelFit, ParallelPredict, ParallelTransform,
67    };
68
69    #[cfg(feature = "simd")]
70    pub use crate::simd::{SimdArrayOps, SimdOps};
71
72    #[cfg(feature = "arrow")]
73    pub use crate::arrow::{ArrowDataset, ColumnStats};
74}
75
76/// Deprecated APIs scheduled for removal
77///
78/// These APIs are deprecated and will be removed in the next major version.
79/// Migration paths are provided where applicable.
80pub mod deprecated {
81    // No deprecated APIs yet, but this provides a clear place to put them
82
83    #[deprecated(since = "0.1.0", note = "Use the new Plugin system instead")]
84    pub fn old_plugin_system() {
85        // Placeholder for deprecated functionality
86    }
87}
88
89// =============================================================================
90// Public API Markers
91// =============================================================================
92
93/// Marker trait for stable APIs
94///
95/// Types implementing this trait are guaranteed to have stable public APIs
96/// that follow semantic versioning.
97pub trait StableApi {
98    /// API version this type was stabilized in
99    const STABLE_SINCE: &'static str;
100
101    /// Whether this API has any experimental features
102    const HAS_EXPERIMENTAL_FEATURES: bool = false;
103}
104
105/// Marker trait for experimental APIs
106///
107/// Types implementing this trait are experimental and may change
108/// without following strict semantic versioning.
109pub trait ExperimentalApi {
110    /// API version this type was introduced in
111    const INTRODUCED_IN: &'static str;
112
113    /// Expected version when this API will be stabilized
114    const STABILIZATION_TARGET: Option<&'static str> = None;
115
116    /// Known limitations or issues with this API
117    const KNOWN_LIMITATIONS: &'static [&'static str] = &[];
118}
119
120// =============================================================================
121// API Version Information
122// =============================================================================
123
124/// Information about API versions and compatibility
125pub struct ApiVersionInfo {
126    /// Current version of the core API
127    pub core_version: &'static str,
128    /// Minimum supported version for compatibility
129    pub min_supported_version: &'static str,
130    /// List of breaking changes since the minimum version
131    pub breaking_changes: &'static [BreakingChange],
132}
133
134/// Information about a breaking change
135#[derive(Debug, Clone)]
136pub struct BreakingChange {
137    /// Version where the breaking change was introduced
138    pub version: &'static str,
139    /// Description of the change
140    pub description: &'static str,
141    /// Migration guide or workaround
142    pub migration: Option<&'static str>,
143}
144
145/// Get current API version information
146pub fn api_version_info() -> ApiVersionInfo {
147    ApiVersionInfo {
148        core_version: "0.1.0",
149        min_supported_version: "0.1.0",
150        breaking_changes: &[
151            // Future breaking changes will be documented here
152        ],
153    }
154}
155
156// =============================================================================
157// Public Configuration Types
158// =============================================================================
159
160/// Configuration for public APIs
161#[derive(Debug, Clone)]
162pub struct PublicApiConfig {
163    /// Enable experimental features
164    pub enable_experimental: bool,
165    /// Enable deprecated API warnings
166    pub warn_deprecated: bool,
167    /// Strict compatibility mode
168    pub strict_compatibility: bool,
169}
170
171impl Default for PublicApiConfig {
172    fn default() -> Self {
173        Self {
174            enable_experimental: false,
175            warn_deprecated: true,
176            strict_compatibility: true,
177        }
178    }
179}
180
181/// Builder for public API configuration
182pub struct PublicApiConfigBuilder {
183    config: PublicApiConfig,
184}
185
186impl PublicApiConfigBuilder {
187    /// Create a new configuration builder
188    pub fn new() -> Self {
189        Self {
190            config: PublicApiConfig::default(),
191        }
192    }
193
194    /// Enable experimental features
195    pub fn enable_experimental(mut self, enable: bool) -> Self {
196        self.config.enable_experimental = enable;
197        self
198    }
199
200    /// Enable deprecated API warnings
201    pub fn warn_deprecated(mut self, warn: bool) -> Self {
202        self.config.warn_deprecated = warn;
203        self
204    }
205
206    /// Enable strict compatibility mode
207    pub fn strict_compatibility(mut self, strict: bool) -> Self {
208        self.config.strict_compatibility = strict;
209        self
210    }
211
212    /// Build the configuration
213    pub fn build(self) -> PublicApiConfig {
214        self.config
215    }
216}
217
218impl Default for PublicApiConfigBuilder {
219    fn default() -> Self {
220        Self::new()
221    }
222}
223
224// =============================================================================
225// API Stability Implementations
226// =============================================================================
227
228// Implement StableApi for core types
229impl<T: crate::traits::Estimator<crate::traits::Untrained>> StableApi for T {
230    const STABLE_SINCE: &'static str = "0.1.0";
231}
232
233// Note: Cannot implement StableApi for Fit trait without specifying associated types
234// impl<X, Y> StableApi for dyn crate::traits::Fit<X, Y> {
235//     const STABLE_SINCE: &'static str = "0.1.0";
236// }
237
238impl<X, Output> StableApi for dyn crate::traits::Predict<X, Output> {
239    const STABLE_SINCE: &'static str = "0.1.0";
240}
241
242impl<X, Output> StableApi for dyn crate::traits::Transform<X, Output> {
243    const STABLE_SINCE: &'static str = "0.1.0";
244}
245
246impl StableApi for crate::error::SklearsError {
247    const STABLE_SINCE: &'static str = "0.1.0";
248}
249
250impl StableApi for crate::dataset::Dataset {
251    const STABLE_SINCE: &'static str = "0.1.0";
252}
253
254// Implement ExperimentalApi for newer features
255impl ExperimentalApi for crate::plugin::PluginRegistry {
256    const INTRODUCED_IN: &'static str = "0.1.0";
257    const STABILIZATION_TARGET: Option<&'static str> = Some("0.2.0");
258    const KNOWN_LIMITATIONS: &'static [&'static str] = &[
259        "Plugin unloading may not clean up all resources",
260        "Dynamic loading requires platform-specific libraries",
261    ];
262}
263
264#[cfg(feature = "simd")]
265impl ExperimentalApi for crate::simd::SimdOps {
266    const INTRODUCED_IN: &'static str = "0.1.0";
267    const STABILIZATION_TARGET: Option<&'static str> = Some("0.3.0");
268    const KNOWN_LIMITATIONS: &'static [&'static str] = &[
269        "SIMD operations may not be available on all platforms",
270        "Performance benefits vary by CPU architecture",
271    ];
272}
273
274// Note: Cannot implement ExperimentalApi for AsyncFit trait without specifying associated types
275// impl<X, Y> ExperimentalApi for dyn crate::traits::async_traits::AsyncFit<X, Y> {
276//     const INTRODUCED_IN: &'static str = "0.1.0";
277//     const STABILIZATION_TARGET: Option<&'static str> = Some("0.2.0");
278//     const KNOWN_LIMITATIONS: &'static [&'static str] = &[
279//         "Async runtime dependencies may conflict with user code",
280//         "Error handling in async contexts needs improvement",
281//     ];
282// }
283
284// =============================================================================
285// Public Utility Functions
286// =============================================================================
287
288/// Check if an API is stable
289pub fn is_api_stable<T: StableApi>() -> bool {
290    true // All types implementing StableApi are stable by definition
291}
292
293/// Check if an API is experimental
294pub fn is_api_experimental<T: ExperimentalApi>() -> bool {
295    true // All types implementing ExperimentalApi are experimental by definition
296}
297
298/// Get stability information for a type
299pub fn get_api_stability<T>() -> ApiStability
300where
301    T: 'static,
302{
303    let _type_id = std::any::TypeId::of::<T>();
304
305    // This is a simplified implementation
306    // In practice, you'd maintain a registry of type stability information
307    ApiStability::Unknown
308}
309
310/// API stability classification
311#[derive(Debug, Clone, Copy, PartialEq, Eq)]
312pub enum ApiStability {
313    /// API is stable and follows semantic versioning
314    Stable,
315    /// API is experimental and may change
316    Experimental,
317    /// API is deprecated and will be removed
318    Deprecated,
319    /// API stability is unknown
320    Unknown,
321}
322
323/// Validate that experimental features are enabled when needed
324pub fn validate_experimental_usage<T: ExperimentalApi>(config: &PublicApiConfig) -> Result<()> {
325    if !config.enable_experimental {
326        return Err(SklearsError::InvalidOperation(format!(
327            "Experimental API {} requires enable_experimental = true. \
328                 This API was introduced in version {} and may change without notice.",
329            std::any::type_name::<T>(),
330            T::INTRODUCED_IN
331        )));
332    }
333    Ok(())
334}
335
336/// Emit deprecation warning if configured
337pub fn warn_if_deprecated<T>(config: &PublicApiConfig, api_name: &str) {
338    if config.warn_deprecated {
339        eprintln!(
340            "Warning: API {api_name} is deprecated and will be removed in a future version. \
341             Please update your code to use the recommended alternative."
342        );
343    }
344}
345
346// =============================================================================
347// Tests
348// =============================================================================
349
350#[allow(non_snake_case)]
351#[cfg(test)]
352mod tests {
353    use super::*;
354
355    #[test]
356    fn test_api_version_info() {
357        let info = api_version_info();
358        assert_eq!(info.core_version, "0.1.0");
359        assert_eq!(info.min_supported_version, "0.1.0");
360    }
361
362    #[test]
363    fn test_public_api_config() {
364        let config = PublicApiConfigBuilder::new()
365            .enable_experimental(true)
366            .warn_deprecated(false)
367            .strict_compatibility(false)
368            .build();
369
370        assert!(config.enable_experimental);
371        assert!(!config.warn_deprecated);
372        assert!(!config.strict_compatibility);
373    }
374
375    #[test]
376    fn test_api_stability_traits() {
377        // Test that we can determine API stability at compile time
378        assert!(is_api_stable::<crate::error::SklearsError>());
379        assert!(is_api_experimental::<crate::plugin::PluginRegistry>());
380    }
381
382    #[test]
383    fn test_experimental_validation() {
384        let config_disabled = PublicApiConfig {
385            enable_experimental: false,
386            ..Default::default()
387        };
388
389        let config_enabled = PublicApiConfig {
390            enable_experimental: true,
391            ..Default::default()
392        };
393
394        // This should fail with experimental disabled
395        assert!(
396            validate_experimental_usage::<crate::plugin::PluginRegistry>(&config_disabled).is_err()
397        );
398
399        // This should succeed with experimental enabled
400        assert!(
401            validate_experimental_usage::<crate::plugin::PluginRegistry>(&config_enabled).is_ok()
402        );
403    }
404
405    #[test]
406    fn test_api_stability_enum() {
407        assert_eq!(ApiStability::Stable, ApiStability::Stable);
408        assert_ne!(ApiStability::Stable, ApiStability::Experimental);
409    }
410}