1use crate::error::{Result, SklearsError};
21
22pub mod stable {
31 pub use crate::traits::{
32 Estimator, Fit, FitPredict, FitTransform, PartialFit, Predict, Transform,
33 };
34
35 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
50pub 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
76pub mod deprecated {
81 #[deprecated(since = "0.1.0", note = "Use the new Plugin system instead")]
84 pub fn old_plugin_system() {
85 }
87}
88
89pub trait StableApi {
98 const STABLE_SINCE: &'static str;
100
101 const HAS_EXPERIMENTAL_FEATURES: bool = false;
103}
104
105pub trait ExperimentalApi {
110 const INTRODUCED_IN: &'static str;
112
113 const STABILIZATION_TARGET: Option<&'static str> = None;
115
116 const KNOWN_LIMITATIONS: &'static [&'static str] = &[];
118}
119
120pub struct ApiVersionInfo {
126 pub core_version: &'static str,
128 pub min_supported_version: &'static str,
130 pub breaking_changes: &'static [BreakingChange],
132}
133
134#[derive(Debug, Clone)]
136pub struct BreakingChange {
137 pub version: &'static str,
139 pub description: &'static str,
141 pub migration: Option<&'static str>,
143}
144
145pub fn api_version_info() -> ApiVersionInfo {
147 ApiVersionInfo {
148 core_version: "0.1.0",
149 min_supported_version: "0.1.0",
150 breaking_changes: &[
151 ],
153 }
154}
155
156#[derive(Debug, Clone)]
162pub struct PublicApiConfig {
163 pub enable_experimental: bool,
165 pub warn_deprecated: bool,
167 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
181pub struct PublicApiConfigBuilder {
183 config: PublicApiConfig,
184}
185
186impl PublicApiConfigBuilder {
187 pub fn new() -> Self {
189 Self {
190 config: PublicApiConfig::default(),
191 }
192 }
193
194 pub fn enable_experimental(mut self, enable: bool) -> Self {
196 self.config.enable_experimental = enable;
197 self
198 }
199
200 pub fn warn_deprecated(mut self, warn: bool) -> Self {
202 self.config.warn_deprecated = warn;
203 self
204 }
205
206 pub fn strict_compatibility(mut self, strict: bool) -> Self {
208 self.config.strict_compatibility = strict;
209 self
210 }
211
212 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
224impl<T: crate::traits::Estimator<crate::traits::Untrained>> StableApi for T {
230 const STABLE_SINCE: &'static str = "0.1.0";
231}
232
233impl<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
254impl 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
274pub fn is_api_stable<T: StableApi>() -> bool {
290 true }
292
293pub fn is_api_experimental<T: ExperimentalApi>() -> bool {
295 true }
297
298pub fn get_api_stability<T>() -> ApiStability
300where
301 T: 'static,
302{
303 let _type_id = std::any::TypeId::of::<T>();
304
305 ApiStability::Unknown
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq)]
312pub enum ApiStability {
313 Stable,
315 Experimental,
317 Deprecated,
319 Unknown,
321}
322
323pub 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
336pub 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#[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 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 assert!(
396 validate_experimental_usage::<crate::plugin::PluginRegistry>(&config_disabled).is_err()
397 );
398
399 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}