sklears_core/plugin/
discovery_marketplace.rs

1//! Plugin Discovery and Marketplace
2//!
3//! This module provides comprehensive plugin discovery and marketplace functionality
4//! for the sklears plugin system. It enables finding, installing, and managing
5//! plugins from remote repositories and community marketplaces.
6
7use super::core_traits::Plugin;
8use super::types_config::{PluginCapability, PluginCategory, PluginMetadata};
9use super::validation::{PluginManifest, PluginValidator, ValidationReport};
10use crate::error::{Result, SklearsError};
11use std::cmp::Ordering;
12use std::collections::HashMap;
13
14/// Plugin discovery service for remote repositories
15///
16/// The PluginDiscoveryService enables automatic discovery and installation of plugins
17/// from configured remote repositories. It provides caching, search functionality,
18/// and network-based plugin management.
19///
20/// # Features
21///
22/// - Multi-repository plugin discovery
23/// - Intelligent caching and index management
24/// - Advanced search capabilities with relevance scoring
25/// - Automatic plugin validation and installation
26/// - Network resilience and error handling
27///
28/// # Examples
29///
30/// ```rust,ignore
31/// use sklears_core::plugin::PluginDiscoveryService;
32///
33/// async fn discover_plugins() -> Result<(), Box<dyn std::error::Error>> {
34///     let discovery = PluginDiscoveryService::new();
35///
36///     // Discover all available plugins
37///     let plugins = discovery.discover_all().await?;
38///     println!("Found {} plugins", plugins.len());
39///
40///     // Search for specific plugins
41///     let query = SearchQuery {
42///         text: "linear regression".to_string(),
43///         category: Some(PluginCategory::Algorithm),
44///         limit: Some(10),
45///     };
46///     let results = discovery.search(&query).await?;
47///
48///     // Install a plugin
49///     if let Some(result) = results.first() {
50///         let install_result = discovery.install_plugin(&result.plugin_id, None).await?;
51///         println!("Installed plugin at: {}", install_result.install_path);
52///     }
53///
54///     Ok(())
55/// }
56/// ```
57#[derive(Debug)]
58pub struct PluginDiscoveryService {
59    /// Remote repositories for plugin discovery
60    repositories: Vec<PluginRepository>,
61    /// Local cache for repository data
62    cache: PluginCache,
63    /// Network client for remote operations
64    client: NetworkClient,
65    /// Search index for fast plugin lookups
66    search_index: SearchIndex,
67}
68
69impl PluginDiscoveryService {
70    /// Create a new discovery service
71    ///
72    /// Initializes the service with default official and community repositories.
73    /// Additional repositories can be added after creation.
74    ///
75    /// # Examples
76    ///
77    /// ```rust,ignore
78    /// use sklears_core::plugin::PluginDiscoveryService;
79    ///
80    /// let discovery = PluginDiscoveryService::new();
81    /// ```
82    pub fn new() -> Self {
83        Self {
84            repositories: vec![PluginRepository::official(), PluginRepository::community()],
85            cache: PluginCache::new(),
86            client: NetworkClient::new(),
87            search_index: SearchIndex::new(),
88        }
89    }
90
91    /// Add a custom repository
92    ///
93    /// Adds a new repository to the discovery service for plugin lookup.
94    ///
95    /// # Arguments
96    ///
97    /// * `repository` - The repository to add
98    ///
99    /// # Examples
100    ///
101    /// ```rust,ignore
102    /// use sklears_core::plugin::{PluginDiscoveryService, PluginRepository};
103    ///
104    /// let mut discovery = PluginDiscoveryService::new();
105    /// let custom_repo = PluginRepository {
106    ///     name: "Company Internal".to_string(),
107    ///     url: "https://internal.company.com/plugins".to_string(),
108    ///     verified: true,
109    ///     priority: 5,
110    /// };
111    /// discovery.add_repository(custom_repo);
112    /// ```
113    pub fn add_repository(&mut self, repository: PluginRepository) {
114        self.repositories.push(repository);
115        // Sort by priority (higher priority first)
116        self.repositories
117            .sort_by(|a, b| b.priority.cmp(&a.priority));
118    }
119
120    /// Discover plugins from all repositories
121    ///
122    /// Scans all configured repositories for available plugins and returns
123    /// their manifests. Results are cached for improved performance.
124    ///
125    /// # Returns
126    ///
127    /// A vector of plugin manifests from all repositories, or an error if
128    /// no repositories are accessible.
129    ///
130    /// # Examples
131    ///
132    /// ```rust,ignore
133    /// use sklears_core::plugin::PluginDiscoveryService;
134    ///
135    /// async fn discover_all() -> Result<(), Box<dyn std::error::Error>> {
136    ///     let discovery = PluginDiscoveryService::new();
137    ///     let all_plugins = discovery.discover_all().await?;
138    ///
139    ///     for manifest in &all_plugins {
140    ///         println!("Found plugin: {} v{}",
141    ///                  manifest.metadata.name,
142    ///                  manifest.metadata.version);
143    ///     }
144    ///     Ok(())
145    /// }
146    /// ```
147    pub async fn discover_all(&self) -> Result<Vec<PluginManifest>> {
148        let mut all_plugins = Vec::new();
149        let mut discovered_from_any = false;
150
151        for repository in &self.repositories {
152            match self.discover_from_repository(repository).await {
153                Ok(mut plugins) => {
154                    all_plugins.append(&mut plugins);
155                    discovered_from_any = true;
156                }
157                Err(e) => {
158                    eprintln!(
159                        "Failed to discover from repository {}: {}",
160                        repository.name, e
161                    );
162                }
163            }
164        }
165
166        if !discovered_from_any && !self.repositories.is_empty() {
167            return Err(SklearsError::InvalidOperation(
168                "Failed to discover plugins from any repository".to_string(),
169            ));
170        }
171
172        // Remove duplicates based on plugin name and version
173        all_plugins.sort_by(|a, b| {
174            a.metadata
175                .name
176                .cmp(&b.metadata.name)
177                .then_with(|| a.metadata.version.cmp(&b.metadata.version))
178        });
179        all_plugins.dedup_by(|a, b| {
180            a.metadata.name == b.metadata.name && a.metadata.version == b.metadata.version
181        });
182
183        Ok(all_plugins)
184    }
185
186    /// Discover plugins from a specific repository
187    ///
188    /// Fetches plugins from a single repository, utilizing caching to avoid
189    /// unnecessary network requests.
190    ///
191    /// # Arguments
192    ///
193    /// * `repository` - The repository to query
194    ///
195    /// # Returns
196    ///
197    /// A vector of plugin manifests from the repository, or an error if
198    /// the repository is inaccessible.
199    pub async fn discover_from_repository(
200        &self,
201        repository: &PluginRepository,
202    ) -> Result<Vec<PluginManifest>> {
203        // Check cache first
204        if let Some(cached) = self.cache.get_repository_plugins(&repository.url) {
205            if !cached.is_expired() {
206                return Ok(cached.plugins);
207            }
208        }
209
210        // Fetch from remote
211        let plugins = self
212            .client
213            .fetch_repository_plugins(repository)
214            .await
215            .map_err(|e| {
216                SklearsError::InvalidOperation(format!(
217                    "Failed to fetch plugins from {}: {}",
218                    repository.name, e
219                ))
220            })?;
221
222        // Update cache
223        self.cache
224            .store_repository_plugins(&repository.url, &plugins);
225
226        // Update search index
227        self.search_index.index_plugins(&plugins);
228
229        Ok(plugins)
230    }
231
232    /// Search plugins by query
233    ///
234    /// Performs intelligent search across all known plugins using text matching,
235    /// category filtering, and relevance scoring. Combines local index results
236    /// with live repository searches for comprehensive results.
237    ///
238    /// # Arguments
239    ///
240    /// * `query` - The search query with filters and options
241    ///
242    /// # Returns
243    ///
244    /// A vector of search results sorted by relevance and popularity.
245    ///
246    /// # Examples
247    ///
248    /// ```rust,ignore
249    /// use sklears_core::plugin::{PluginDiscoveryService, SearchQuery, PluginCategory};
250    ///
251    /// async fn search_plugins() -> Result<(), Box<dyn std::error::Error>> {
252    ///     let discovery = PluginDiscoveryService::new();
253    ///
254    ///     let query = SearchQuery {
255    ///         text: "classification".to_string(),
256    ///         category: Some(PluginCategory::Algorithm),
257    ///         capabilities: vec![PluginCapability::Parallel],
258    ///         limit: Some(20),
259    ///         min_rating: Some(4.0),
260    ///     };
261    ///
262    ///     let results = discovery.search(&query).await?;
263    ///     for result in results {
264    ///         println!("{}: {} (score: {:.2})",
265    ///                  result.plugin_id,
266    ///                  result.description,
267    ///                  result.relevance_score);
268    ///     }
269    ///     Ok(())
270    /// }
271    /// ```
272    pub async fn search(&self, query: &SearchQuery) -> Result<Vec<PluginSearchResult>> {
273        // First search local index
274        let mut results = self.search_index.search(query)?;
275
276        // If not enough results, search repositories
277        let target_count = query.limit.unwrap_or(10);
278        if results.len() < target_count {
279            for repository in &self.repositories {
280                if let Ok(remote_results) = self.client.search_repository(repository, query).await {
281                    results.extend(remote_results);
282                    if results.len() >= target_count * 2 {
283                        break; // Enough results to sort and filter
284                    }
285                }
286            }
287        }
288
289        // Apply filtering
290        if let Some(category) = &query.category {
291            results.retain(|r| r.category == *category);
292        }
293
294        if !query.capabilities.is_empty() {
295            results.retain(|r| {
296                query
297                    .capabilities
298                    .iter()
299                    .all(|cap| r.capabilities.contains(cap))
300            });
301        }
302
303        if let Some(min_rating) = query.min_rating {
304            results.retain(|r| r.rating >= min_rating);
305        }
306
307        // Sort by relevance and popularity
308        results.sort_by(|a, b| {
309            b.relevance_score
310                .partial_cmp(&a.relevance_score)
311                .unwrap_or(Ordering::Equal)
312                .then_with(|| {
313                    b.popularity_score
314                        .partial_cmp(&a.popularity_score)
315                        .unwrap_or(Ordering::Equal)
316                })
317        });
318
319        Ok(results.into_iter().take(target_count).collect())
320    }
321
322    /// Download and install plugin
323    ///
324    /// Downloads a plugin from repositories, validates it comprehensively,
325    /// and installs it locally for use.
326    ///
327    /// # Arguments
328    ///
329    /// * `plugin_id` - The unique identifier of the plugin to install
330    /// * `version` - Optional specific version to install (latest if None)
331    ///
332    /// # Returns
333    ///
334    /// Installation result with manifest, path, and validation report.
335    ///
336    /// # Errors
337    ///
338    /// Returns an error if the plugin is not found, validation fails,
339    /// or installation encounters issues.
340    ///
341    /// # Examples
342    ///
343    /// ```rust,ignore
344    /// use sklears_core::plugin::PluginDiscoveryService;
345    ///
346    /// async fn install_plugin() -> Result<(), Box<dyn std::error::Error>> {
347    ///     let discovery = PluginDiscoveryService::new();
348    ///
349    ///     let result = discovery.install_plugin("linear_regression", Some("2.1.0")).await?;
350    ///
351    ///     println!("Plugin installed at: {}", result.install_path);
352    ///     if !result.validation_report.warnings.is_empty() {
353    ///         println!("Warnings: {:?}", result.validation_report.warnings);
354    ///     }
355    ///     Ok(())
356    /// }
357    /// ```
358    pub async fn install_plugin(
359        &self,
360        plugin_id: &str,
361        version: Option<&str>,
362    ) -> Result<PluginInstallResult> {
363        // Find plugin in repositories
364        let manifest = self.find_plugin_manifest(plugin_id, version).await?;
365
366        // Validate plugin comprehensively
367        let validator = PluginValidator::new();
368        let dummy_plugin = DummyPlugin::new();
369        let validation_report = validator.validate_comprehensive(&*dummy_plugin, &manifest)?;
370
371        if validation_report.has_errors() {
372            return Err(SklearsError::InvalidOperation(format!(
373                "Plugin validation failed: {} errors found",
374                validation_report.errors.len()
375            )));
376        }
377
378        // Download plugin
379        let plugin_data = self.client.download_plugin(&manifest).await.map_err(|e| {
380            SklearsError::InvalidOperation(format!("Failed to download plugin: {}", e))
381        })?;
382
383        // Verify download integrity
384        let computed_hash = self.compute_content_hash(&plugin_data);
385        if computed_hash != manifest.content_hash {
386            return Err(SklearsError::InvalidOperation(
387                "Plugin content hash verification failed".to_string(),
388            ));
389        }
390
391        // Install locally
392        let install_path = self.install_plugin_locally(&manifest, &plugin_data)?;
393
394        Ok(PluginInstallResult {
395            manifest,
396            install_path,
397            validation_report,
398        })
399    }
400
401    /// Find plugin manifest in repositories
402    ///
403    /// Searches through all configured repositories to find a plugin manifest
404    /// matching the specified ID and optional version.
405    async fn find_plugin_manifest(
406        &self,
407        plugin_id: &str,
408        version: Option<&str>,
409    ) -> Result<PluginManifest> {
410        let mut last_error = None;
411
412        for repository in &self.repositories {
413            match self
414                .client
415                .get_plugin_manifest(repository, plugin_id, version)
416                .await
417            {
418                Ok(manifest) => return Ok(manifest),
419                Err(e) => last_error = Some(e),
420            }
421        }
422
423        Err(SklearsError::InvalidOperation(format!(
424            "Plugin '{}' not found in any repository. Last error: {:?}",
425            plugin_id, last_error
426        )))
427    }
428
429    /// Install plugin locally
430    ///
431    /// Handles the local installation of a downloaded plugin, including
432    /// file extraction, permission setup, and registration preparation.
433    fn install_plugin_locally(&self, manifest: &PluginManifest, data: &[u8]) -> Result<String> {
434        // Create plugin directory
435        let plugin_dir = format!("/tmp/plugins/{}", manifest.metadata.name);
436        std::fs::create_dir_all(&plugin_dir).map_err(|e| {
437            SklearsError::InvalidOperation(format!("Failed to create plugin directory: {}", e))
438        })?;
439
440        // Write plugin data
441        let plugin_file = format!("{}/plugin.so", plugin_dir);
442        std::fs::write(&plugin_file, data).map_err(|e| {
443            SklearsError::InvalidOperation(format!("Failed to write plugin file: {}", e))
444        })?;
445
446        // Set appropriate permissions (readable and executable)
447        #[cfg(unix)]
448        {
449            use std::os::unix::fs::PermissionsExt;
450            std::fs::set_permissions(&plugin_file, std::fs::Permissions::from_mode(0o755))
451                .map_err(|e| {
452                    SklearsError::InvalidOperation(format!("Failed to set permissions: {}", e))
453                })?;
454        }
455
456        // Write manifest for future reference
457        let manifest_file = format!("{}/manifest.json", plugin_dir);
458        let manifest_json = serde_json::to_string_pretty(manifest).map_err(|e| {
459            SklearsError::InvalidOperation(format!("Failed to serialize manifest: {}", e))
460        })?;
461        std::fs::write(manifest_file, manifest_json).map_err(|e| {
462            SklearsError::InvalidOperation(format!("Failed to write manifest: {}", e))
463        })?;
464
465        Ok(plugin_file)
466    }
467
468    /// Compute content hash for verification
469    fn compute_content_hash(&self, data: &[u8]) -> String {
470        // Simple hash implementation - in production, use SHA-256 or similar
471        use std::collections::hash_map::DefaultHasher;
472        use std::hash::{Hash, Hasher};
473
474        let mut hasher = DefaultHasher::new();
475        data.hash(&mut hasher);
476        format!("{:x}", hasher.finish())
477    }
478
479    /// Get repository statistics
480    ///
481    /// Returns statistics about configured repositories and their status.
482    pub async fn get_repository_stats(&self) -> Vec<RepositoryStats> {
483        let mut stats = Vec::new();
484
485        for repository in &self.repositories {
486            let plugin_count = match self.discover_from_repository(repository).await {
487                Ok(plugins) => plugins.len(),
488                Err(_) => 0,
489            };
490
491            stats.push(RepositoryStats {
492                name: repository.name.clone(),
493                url: repository.url.clone(),
494                plugin_count,
495                verified: repository.verified,
496                last_updated: std::time::SystemTime::now(), // Placeholder
497            });
498        }
499
500        stats
501    }
502}
503
504impl Default for PluginDiscoveryService {
505    fn default() -> Self {
506        Self::new()
507    }
508}
509
510/// Community plugin marketplace
511///
512/// The PluginMarketplace provides a comprehensive platform for plugin discovery,
513/// rating, reviewing, and analytics. It combines the discovery service with
514/// community features to create a full marketplace experience.
515///
516/// # Features
517///
518/// - Featured plugin recommendations
519/// - Community ratings and reviews
520/// - Download tracking and analytics
521/// - Plugin popularity scoring
522/// - Trend analysis and reporting
523///
524/// # Examples
525///
526/// ```rust,ignore
527/// use sklears_core::plugin::PluginMarketplace;
528///
529/// async fn marketplace_demo() -> Result<(), Box<dyn std::error::Error>> {
530///     let marketplace = PluginMarketplace::new();
531///
532///     // Get featured plugins
533///     let featured = marketplace.get_featured_plugins().await?;
534///     println!("Featured plugins: {}", featured.len());
535///
536///     // Rate a plugin
537///     marketplace.rate_plugin("linear_regression", "user123", 4.5).await?;
538///
539///     // Get plugin statistics
540///     let stats = marketplace.get_plugin_stats("linear_regression").await?;
541///     println!("Average rating: {:.1}", stats.average_rating);
542///
543///     Ok(())
544/// }
545/// ```
546#[derive(Debug)]
547pub struct PluginMarketplace {
548    /// Discovery service for plugin management
549    discovery: PluginDiscoveryService,
550    /// Rating system for community feedback
551    rating_system: RatingSystem,
552    /// Review system for detailed feedback
553    review_system: ReviewSystem,
554    /// Download tracking for popularity metrics
555    download_tracker: DownloadTracker,
556    /// Analytics engine for trend analysis
557    analytics: PluginAnalytics,
558}
559
560impl PluginMarketplace {
561    /// Create a new marketplace
562    ///
563    /// Initializes all marketplace components including discovery, ratings,
564    /// reviews, and analytics systems.
565    ///
566    /// # Examples
567    ///
568    /// ```rust
569    /// use sklears_core::plugin::PluginMarketplace;
570    ///
571    /// let marketplace = PluginMarketplace::new();
572    /// ```
573    pub fn new() -> Self {
574        Self {
575            discovery: PluginDiscoveryService::new(),
576            rating_system: RatingSystem::new(),
577            review_system: ReviewSystem::new(),
578            download_tracker: DownloadTracker::new(),
579            analytics: PluginAnalytics::new(),
580        }
581    }
582
583    /// Get featured plugins
584    ///
585    /// Returns a curated list of high-quality plugins based on ratings,
586    /// download counts, and community engagement metrics.
587    ///
588    /// # Returns
589    ///
590    /// A vector of featured plugins sorted by feature score.
591    ///
592    /// # Examples
593    ///
594    /// ```rust,ignore
595    /// use sklears_core::plugin::PluginMarketplace;
596    ///
597    /// async fn show_featured() -> Result<(), Box<dyn std::error::Error>> {
598    ///     let marketplace = PluginMarketplace::new();
599    ///     let featured = marketplace.get_featured_plugins().await?;
600    ///
601    ///     for plugin in featured {
602    ///         println!("{}: {:.1} stars, {} downloads",
603    ///                  plugin.manifest.metadata.name,
604    ///                  plugin.rating,
605    ///                  plugin.download_count);
606    ///     }
607    ///     Ok(())
608    /// }
609    /// ```
610    pub async fn get_featured_plugins(&self) -> Result<Vec<FeaturedPlugin>> {
611        let plugins = self.discovery.discover_all().await?;
612
613        let mut featured = Vec::new();
614        for plugin_manifest in plugins {
615            let plugin_id = plugin_manifest.metadata.name.clone();
616
617            // Get community metrics
618            let rating = self.rating_system.get_average_rating(&plugin_id).await?;
619            let download_count = self.download_tracker.get_download_count(&plugin_id).await?;
620            let review_count = self.review_system.get_review_count(&plugin_id).await?;
621
622            // Calculate feature score
623            let feature_score = self.calculate_feature_score(rating, download_count, review_count);
624
625            // Only include high-quality plugins
626            if feature_score > 7.0 {
627                featured.push(FeaturedPlugin {
628                    manifest: plugin_manifest,
629                    rating,
630                    download_count,
631                    review_count,
632                    feature_score,
633                    trend_direction: self
634                        .analytics
635                        .get_trend_direction(&plugin_id)
636                        .await
637                        .unwrap_or(TrendDirection::Stable),
638                });
639            }
640        }
641
642        // Sort by feature score (descending)
643        featured.sort_by(|a, b| {
644            b.feature_score
645                .partial_cmp(&a.feature_score)
646                .unwrap_or(Ordering::Equal)
647        });
648
649        Ok(featured.into_iter().take(10).collect())
650    }
651
652    /// Submit plugin rating
653    ///
654    /// Allows users to rate plugins on a 1-5 scale. Ratings are used for
655    /// featured plugin selection and recommendation algorithms.
656    ///
657    /// # Arguments
658    ///
659    /// * `plugin_id` - The plugin to rate
660    /// * `user_id` - The user submitting the rating
661    /// * `rating` - Rating value (1.0 to 5.0)
662    ///
663    /// # Errors
664    ///
665    /// Returns an error if the rating is outside the valid range.
666    ///
667    /// # Examples
668    ///
669    /// ```rust,ignore
670    /// use sklears_core::plugin::PluginMarketplace;
671    ///
672    /// async fn rate_plugin() -> Result<(), Box<dyn std::error::Error>> {
673    ///     let marketplace = PluginMarketplace::new();
674    ///     marketplace.rate_plugin("awesome_classifier", "user123", 4.8).await?;
675    ///     println!("Rating submitted successfully");
676    ///     Ok(())
677    /// }
678    /// ```
679    pub async fn rate_plugin(&self, plugin_id: &str, user_id: &str, rating: f32) -> Result<()> {
680        if !(1.0..=5.0).contains(&rating) {
681            return Err(SklearsError::InvalidOperation(
682                "Rating must be between 1.0 and 5.0".to_string(),
683            ));
684        }
685
686        self.rating_system
687            .submit_rating(plugin_id, user_id, rating)
688            .await?;
689        self.analytics.track_rating_event(plugin_id, rating).await?;
690
691        Ok(())
692    }
693
694    /// Submit plugin review
695    ///
696    /// Allows users to submit detailed reviews for plugins, providing
697    /// valuable feedback to other users and plugin developers.
698    ///
699    /// # Arguments
700    ///
701    /// * `plugin_id` - The plugin to review
702    /// * `review` - The review content and metadata
703    ///
704    /// # Examples
705    ///
706    /// ```rust,ignore
707    /// use sklears_core::plugin::{PluginMarketplace, PluginReview};
708    ///
709    /// async fn submit_review() -> Result<(), Box<dyn std::error::Error>> {
710    ///     let marketplace = PluginMarketplace::new();
711    ///
712    ///     let review = PluginReview {
713    ///         user_id: "reviewer123".to_string(),
714    ///         rating: 4.5,
715    ///         title: "Excellent performance".to_string(),
716    ///         content: "This plugin significantly improved our model accuracy.".to_string(),
717    ///         verified_download: true,
718    ///     };
719    ///
720    ///     marketplace.submit_review("awesome_classifier", review).await?;
721    ///     Ok(())
722    /// }
723    /// ```
724    pub async fn submit_review(&self, plugin_id: &str, review: PluginReview) -> Result<()> {
725        self.review_system.submit_review(plugin_id, review).await?;
726        self.analytics.track_review_event(plugin_id).await?;
727
728        Ok(())
729    }
730
731    /// Get comprehensive plugin statistics
732    ///
733    /// Returns detailed statistics about a plugin including ratings,
734    /// downloads, reviews, and trend data.
735    ///
736    /// # Arguments
737    ///
738    /// * `plugin_id` - The plugin to get statistics for
739    ///
740    /// # Returns
741    ///
742    /// Comprehensive plugin statistics and metrics.
743    ///
744    /// # Examples
745    ///
746    /// ```rust,ignore
747    /// use sklears_core::plugin::PluginMarketplace;
748    ///
749    /// async fn show_stats() -> Result<(), Box<dyn std::error::Error>> {
750    ///     let marketplace = PluginMarketplace::new();
751    ///     let stats = marketplace.get_plugin_stats("popular_plugin").await?;
752    ///
753    ///     println!("Rating: {:.1}/5.0", stats.average_rating);
754    ///     println!("Downloads: {}", stats.total_downloads);
755    ///     println!("Reviews: {}", stats.recent_reviews.len());
756    ///     Ok(())
757    /// }
758    /// ```
759    pub async fn get_plugin_stats(&self, plugin_id: &str) -> Result<PluginStats> {
760        let rating = self.rating_system.get_average_rating(plugin_id).await?;
761        let downloads = self.download_tracker.get_download_count(plugin_id).await?;
762        let reviews = self.review_system.get_reviews(plugin_id, 0, 5).await?;
763        let trend = self.analytics.get_trend_data(plugin_id).await?;
764        let rating_distribution = self
765            .rating_system
766            .get_rating_distribution(plugin_id)
767            .await?;
768
769        Ok(PluginStats {
770            average_rating: rating,
771            total_downloads: downloads,
772            recent_reviews: reviews,
773            trend_data: trend,
774            rating_distribution,
775            monthly_downloads: self
776                .download_tracker
777                .get_monthly_downloads(plugin_id)
778                .await?,
779            last_updated: self.analytics.get_last_update_time(plugin_id).await?,
780        })
781    }
782
783    /// Get trending plugins
784    ///
785    /// Returns plugins that are currently trending based on recent
786    /// download activity, ratings, and community engagement.
787    pub async fn get_trending_plugins(&self, limit: usize) -> Result<Vec<TrendingPlugin>> {
788        let all_plugins = self.discovery.discover_all().await?;
789        let mut trending = Vec::new();
790
791        for manifest in all_plugins {
792            let plugin_id = manifest.metadata.name.clone();
793            let trend_score = self.analytics.calculate_trend_score(&plugin_id).await?;
794
795            if trend_score > 0.5 {
796                // Threshold for trending
797                trending.push(TrendingPlugin {
798                    manifest,
799                    trend_score,
800                    recent_downloads: self
801                        .download_tracker
802                        .get_recent_downloads(&plugin_id, 7)
803                        .await?,
804                    velocity: self.analytics.get_download_velocity(&plugin_id).await?,
805                });
806            }
807        }
808
809        trending.sort_by(|a, b| {
810            b.trend_score
811                .partial_cmp(&a.trend_score)
812                .unwrap_or(Ordering::Equal)
813        });
814        Ok(trending.into_iter().take(limit).collect())
815    }
816
817    /// Calculate feature score for plugin ranking
818    ///
819    /// Computes a composite score based on rating, downloads, and reviews
820    /// to determine plugin quality and popularity for featured listings.
821    pub fn calculate_feature_score(&self, rating: f32, downloads: u64, reviews: u64) -> f32 {
822        let rating_weight = 0.4;
823        let download_weight = 0.4;
824        let review_weight = 0.2;
825
826        // Normalize values to 0-10 scale
827        let normalized_rating = rating * 2.0; // 5-star scale to 10-point scale
828
829        // Log scale for downloads (handles zero values)
830        let normalized_downloads = if downloads == 0 {
831            0.0
832        } else {
833            (downloads as f32).log10().min(6.0) / 6.0 * 10.0 // Max at 1M downloads
834        };
835
836        // Log scale for reviews (handles zero values)
837        let normalized_reviews = if reviews == 0 {
838            0.0
839        } else {
840            (reviews as f32).log10().min(3.0) / 3.0 * 10.0 // Max at 1K reviews
841        };
842
843        normalized_rating * rating_weight
844            + normalized_downloads * download_weight
845            + normalized_reviews * review_weight
846    }
847
848    /// Get marketplace analytics summary
849    ///
850    /// Returns overall marketplace statistics and trends.
851    pub async fn get_marketplace_summary(&self) -> Result<MarketplaceSummary> {
852        let total_plugins = self.discovery.discover_all().await?.len();
853        let total_downloads = self.download_tracker.get_total_downloads().await?;
854        let active_users = self.rating_system.get_active_user_count().await?;
855        let trending_categories = self.analytics.get_trending_categories().await?;
856
857        Ok(MarketplaceSummary {
858            total_plugins,
859            total_downloads,
860            active_users,
861            trending_categories,
862            last_updated: std::time::SystemTime::now(),
863        })
864    }
865}
866
867impl Default for PluginMarketplace {
868    fn default() -> Self {
869        Self::new()
870    }
871}
872
873// =============================================================================
874// Supporting Types
875// =============================================================================
876
877/// Plugin repository configuration
878#[derive(Debug, Clone)]
879pub struct PluginRepository {
880    /// Repository name
881    pub name: String,
882    /// Repository URL
883    pub url: String,
884    /// Whether this repository is verified/trusted
885    pub verified: bool,
886    /// Repository priority (higher = checked first)
887    pub priority: u8,
888}
889
890impl PluginRepository {
891    /// Create the official SKLears plugin repository
892    pub fn official() -> Self {
893        Self {
894            name: "Official".to_string(),
895            url: "https://plugins.sklears.rs".to_string(),
896            verified: true,
897            priority: 10,
898        }
899    }
900
901    /// Create the community plugin repository
902    pub fn community() -> Self {
903        Self {
904            name: "Community".to_string(),
905            url: "https://community.sklears.rs".to_string(),
906            verified: true,
907            priority: 5,
908        }
909    }
910}
911
912/// Search query for plugin discovery
913#[derive(Debug, Clone)]
914pub struct SearchQuery {
915    /// Text to search for in plugin names and descriptions
916    pub text: String,
917    /// Filter by plugin category
918    pub category: Option<PluginCategory>,
919    /// Required capabilities
920    pub capabilities: Vec<PluginCapability>,
921    /// Maximum number of results
922    pub limit: Option<usize>,
923    /// Minimum rating filter
924    pub min_rating: Option<f32>,
925}
926
927/// Plugin search result
928#[derive(Debug, Clone)]
929pub struct PluginSearchResult {
930    /// Plugin identifier
931    pub plugin_id: String,
932    /// Plugin description
933    pub description: String,
934    /// Search relevance score
935    pub relevance_score: f32,
936    /// Popularity score
937    pub popularity_score: f32,
938    /// Plugin category
939    pub category: PluginCategory,
940    /// Plugin capabilities
941    pub capabilities: Vec<PluginCapability>,
942    /// Average rating
943    pub rating: f32,
944    /// Download count
945    pub download_count: u64,
946}
947
948/// Plugin installation result
949#[derive(Debug, Clone)]
950pub struct PluginInstallResult {
951    /// Plugin manifest
952    pub manifest: PluginManifest,
953    /// Installation path
954    pub install_path: String,
955    /// Validation report
956    pub validation_report: ValidationReport,
957}
958
959/// Featured plugin information
960#[derive(Debug, Clone)]
961pub struct FeaturedPlugin {
962    /// Plugin manifest
963    pub manifest: PluginManifest,
964    /// Average rating
965    pub rating: f32,
966    /// Total download count
967    pub download_count: u64,
968    /// Number of reviews
969    pub review_count: u64,
970    /// Feature score (0-10)
971    pub feature_score: f32,
972    /// Trend direction
973    pub trend_direction: TrendDirection,
974}
975
976/// Plugin review
977#[derive(Debug, Clone)]
978pub struct PluginReview {
979    /// User who submitted the review
980    pub user_id: String,
981    /// Rating given (1-5)
982    pub rating: f32,
983    /// Review title
984    pub title: String,
985    /// Review content
986    pub content: String,
987    /// Whether user has verified download
988    pub verified_download: bool,
989}
990
991/// Comprehensive plugin statistics
992#[derive(Debug, Clone)]
993pub struct PluginStats {
994    /// Average rating
995    pub average_rating: f32,
996    /// Total downloads
997    pub total_downloads: u64,
998    /// Recent reviews
999    pub recent_reviews: Vec<PluginReview>,
1000    /// Trend data points
1001    pub trend_data: Vec<f32>,
1002    /// Rating distribution (1-5 stars)
1003    pub rating_distribution: HashMap<u8, u64>,
1004    /// Monthly download counts
1005    pub monthly_downloads: Vec<u64>,
1006    /// Last update time
1007    pub last_updated: std::time::SystemTime,
1008}
1009
1010/// Trending plugin information
1011#[derive(Debug, Clone)]
1012pub struct TrendingPlugin {
1013    /// Plugin manifest
1014    pub manifest: PluginManifest,
1015    /// Trend score
1016    pub trend_score: f32,
1017    /// Recent downloads
1018    pub recent_downloads: u64,
1019    /// Download velocity (downloads per day)
1020    pub velocity: f32,
1021}
1022
1023/// Repository statistics
1024#[derive(Debug, Clone)]
1025pub struct RepositoryStats {
1026    /// Repository name
1027    pub name: String,
1028    /// Repository URL
1029    pub url: String,
1030    /// Number of plugins
1031    pub plugin_count: usize,
1032    /// Whether repository is verified
1033    pub verified: bool,
1034    /// Last update time
1035    pub last_updated: std::time::SystemTime,
1036}
1037
1038/// Marketplace summary statistics
1039#[derive(Debug, Clone)]
1040pub struct MarketplaceSummary {
1041    /// Total number of plugins
1042    pub total_plugins: usize,
1043    /// Total download count across all plugins
1044    pub total_downloads: u64,
1045    /// Number of active users
1046    pub active_users: u64,
1047    /// Trending categories
1048    pub trending_categories: Vec<PluginCategory>,
1049    /// Last update time
1050    pub last_updated: std::time::SystemTime,
1051}
1052
1053/// Trend direction enumeration
1054#[derive(Debug, Clone, PartialEq)]
1055pub enum TrendDirection {
1056    Rising,
1057    Stable,
1058    Declining,
1059}
1060
1061/// Marketplace specific metadata
1062#[derive(Debug, Clone)]
1063pub struct MarketplaceInfo {
1064    pub tags: Vec<String>,
1065    pub license: String,
1066    pub repository_url: Option<String>,
1067    pub documentation_url: Option<String>,
1068    pub pricing: PricingInfo,
1069    pub support_level: SupportLevel,
1070}
1071
1072/// Plugin pricing information
1073#[derive(Debug, Clone)]
1074pub enum PricingInfo {
1075    Free,
1076    OneTime(f64),
1077    Subscription(f64, SubscriptionPeriod),
1078    UsageBased(f64, UsageUnit),
1079}
1080
1081/// Subscription period
1082#[derive(Debug, Clone)]
1083pub enum SubscriptionPeriod {
1084    Monthly,
1085    Yearly,
1086}
1087
1088/// Usage unit for pricing
1089#[derive(Debug, Clone)]
1090pub enum UsageUnit {
1091    PerPrediction,
1092    PerDataPoint,
1093    PerHour,
1094}
1095
1096/// Support level offered
1097#[derive(Debug, Clone)]
1098pub enum SupportLevel {
1099    Community,
1100    Basic,
1101    Professional,
1102    Enterprise,
1103}
1104
1105// =============================================================================
1106// Placeholder Implementations for Supporting Services
1107// =============================================================================
1108
1109/// Plugin cache for local storage
1110#[derive(Debug, Clone)]
1111pub struct PluginCache;
1112
1113impl Default for PluginCache {
1114    fn default() -> Self {
1115        Self::new()
1116    }
1117}
1118
1119impl PluginCache {
1120    pub fn new() -> Self {
1121        Self
1122    }
1123
1124    pub fn get_repository_plugins(&self, _url: &str) -> Option<CachedPlugins> {
1125        None
1126    }
1127
1128    pub fn store_repository_plugins(&self, _url: &str, _plugins: &[PluginManifest]) {}
1129}
1130
1131/// Search index for fast plugin lookups
1132#[derive(Debug, Clone)]
1133pub struct SearchIndex;
1134
1135impl Default for SearchIndex {
1136    fn default() -> Self {
1137        Self::new()
1138    }
1139}
1140
1141impl SearchIndex {
1142    pub fn new() -> Self {
1143        Self
1144    }
1145
1146    pub fn index_plugins(&self, _plugins: &[PluginManifest]) {}
1147
1148    pub fn search(&self, _query: &SearchQuery) -> Result<Vec<PluginSearchResult>> {
1149        Ok(Vec::new())
1150    }
1151}
1152
1153/// Network client for remote operations
1154#[derive(Debug, Clone)]
1155pub struct NetworkClient;
1156
1157impl Default for NetworkClient {
1158    fn default() -> Self {
1159        Self::new()
1160    }
1161}
1162
1163impl NetworkClient {
1164    pub fn new() -> Self {
1165        Self
1166    }
1167
1168    pub async fn fetch_repository_plugins(
1169        &self,
1170        _repo: &PluginRepository,
1171    ) -> Result<Vec<PluginManifest>> {
1172        Ok(Vec::new())
1173    }
1174
1175    pub async fn search_repository(
1176        &self,
1177        _repo: &PluginRepository,
1178        _query: &SearchQuery,
1179    ) -> Result<Vec<PluginSearchResult>> {
1180        Ok(Vec::new())
1181    }
1182
1183    pub async fn download_plugin(&self, _manifest: &PluginManifest) -> Result<Vec<u8>> {
1184        Ok(Vec::new())
1185    }
1186
1187    pub async fn get_plugin_manifest(
1188        &self,
1189        _repo: &PluginRepository,
1190        _id: &str,
1191        _version: Option<&str>,
1192    ) -> Result<PluginManifest> {
1193        Err(SklearsError::InvalidOperation("Not found".to_string()))
1194    }
1195}
1196
1197/// Rating system for community feedback
1198#[derive(Debug, Clone)]
1199pub struct RatingSystem;
1200
1201impl Default for RatingSystem {
1202    fn default() -> Self {
1203        Self::new()
1204    }
1205}
1206
1207impl RatingSystem {
1208    pub fn new() -> Self {
1209        Self
1210    }
1211
1212    pub async fn get_average_rating(&self, _plugin_id: &str) -> Result<f32> {
1213        Ok(4.5)
1214    }
1215
1216    pub async fn submit_rating(
1217        &self,
1218        _plugin_id: &str,
1219        _user_id: &str,
1220        _rating: f32,
1221    ) -> Result<()> {
1222        Ok(())
1223    }
1224
1225    pub async fn get_rating_distribution(&self, _plugin_id: &str) -> Result<HashMap<u8, u64>> {
1226        Ok(HashMap::new())
1227    }
1228
1229    pub async fn get_active_user_count(&self) -> Result<u64> {
1230        Ok(1000)
1231    }
1232}
1233
1234/// Review system for detailed feedback
1235#[derive(Debug, Clone)]
1236pub struct ReviewSystem;
1237
1238impl Default for ReviewSystem {
1239    fn default() -> Self {
1240        Self::new()
1241    }
1242}
1243
1244impl ReviewSystem {
1245    pub fn new() -> Self {
1246        Self
1247    }
1248
1249    pub async fn get_review_count(&self, _plugin_id: &str) -> Result<u64> {
1250        Ok(100)
1251    }
1252
1253    pub async fn submit_review(&self, _plugin_id: &str, _review: PluginReview) -> Result<()> {
1254        Ok(())
1255    }
1256
1257    pub async fn get_reviews(
1258        &self,
1259        _plugin_id: &str,
1260        _offset: usize,
1261        _limit: usize,
1262    ) -> Result<Vec<PluginReview>> {
1263        Ok(Vec::new())
1264    }
1265}
1266
1267/// Download tracking for popularity metrics
1268#[derive(Debug, Clone)]
1269pub struct DownloadTracker;
1270
1271impl Default for DownloadTracker {
1272    fn default() -> Self {
1273        Self::new()
1274    }
1275}
1276
1277impl DownloadTracker {
1278    pub fn new() -> Self {
1279        Self
1280    }
1281
1282    pub async fn get_download_count(&self, _plugin_id: &str) -> Result<u64> {
1283        Ok(1000)
1284    }
1285
1286    pub async fn get_monthly_downloads(&self, _plugin_id: &str) -> Result<Vec<u64>> {
1287        Ok(vec![100, 120, 150, 180])
1288    }
1289
1290    pub async fn get_recent_downloads(&self, _plugin_id: &str, _days: u32) -> Result<u64> {
1291        Ok(50)
1292    }
1293
1294    pub async fn get_total_downloads(&self) -> Result<u64> {
1295        Ok(1000000)
1296    }
1297}
1298
1299/// Analytics engine for trend analysis
1300#[derive(Debug, Clone)]
1301pub struct PluginAnalytics;
1302
1303impl Default for PluginAnalytics {
1304    fn default() -> Self {
1305        Self::new()
1306    }
1307}
1308
1309impl PluginAnalytics {
1310    pub fn new() -> Self {
1311        Self
1312    }
1313
1314    pub async fn track_rating_event(&self, _plugin_id: &str, _rating: f32) -> Result<()> {
1315        Ok(())
1316    }
1317
1318    pub async fn track_review_event(&self, _plugin_id: &str) -> Result<()> {
1319        Ok(())
1320    }
1321
1322    pub async fn get_trend_data(&self, _plugin_id: &str) -> Result<Vec<f32>> {
1323        Ok(vec![1.0, 1.2, 1.5, 1.8])
1324    }
1325
1326    pub async fn get_trend_direction(&self, _plugin_id: &str) -> Result<TrendDirection> {
1327        Ok(TrendDirection::Stable)
1328    }
1329
1330    pub async fn calculate_trend_score(&self, _plugin_id: &str) -> Result<f32> {
1331        Ok(0.7)
1332    }
1333
1334    pub async fn get_download_velocity(&self, _plugin_id: &str) -> Result<f32> {
1335        Ok(5.0)
1336    }
1337
1338    pub async fn get_last_update_time(&self, _plugin_id: &str) -> Result<std::time::SystemTime> {
1339        Ok(std::time::SystemTime::now())
1340    }
1341
1342    pub async fn get_trending_categories(&self) -> Result<Vec<PluginCategory>> {
1343        Ok(vec![PluginCategory::Algorithm, PluginCategory::Transformer])
1344    }
1345}
1346
1347/// Cached plugins with expiration
1348#[derive(Debug, Clone)]
1349pub struct CachedPlugins {
1350    pub plugins: Vec<PluginManifest>,
1351}
1352
1353impl CachedPlugins {
1354    pub fn is_expired(&self) -> bool {
1355        false // Placeholder implementation
1356    }
1357}
1358
1359/// Dummy plugin for validation testing
1360#[derive(Debug)]
1361pub struct DummyPlugin;
1362
1363impl DummyPlugin {
1364    #[allow(clippy::new_ret_no_self)]
1365    pub fn new() -> Box<dyn Plugin> {
1366        Box::new(Self)
1367    }
1368}
1369
1370impl Plugin for DummyPlugin {
1371    fn id(&self) -> &str {
1372        "dummy"
1373    }
1374
1375    fn metadata(&self) -> PluginMetadata {
1376        PluginMetadata::default()
1377    }
1378
1379    fn initialize(&mut self, _config: &super::types_config::PluginConfig) -> Result<()> {
1380        Ok(())
1381    }
1382
1383    fn is_compatible(&self, _input_type: std::any::TypeId) -> bool {
1384        true
1385    }
1386
1387    fn as_any(&self) -> &dyn std::any::Any {
1388        self
1389    }
1390
1391    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
1392        self
1393    }
1394
1395    fn validate_config(&self, _config: &super::types_config::PluginConfig) -> Result<()> {
1396        Ok(())
1397    }
1398
1399    fn cleanup(&mut self) -> Result<()> {
1400        Ok(())
1401    }
1402}