Skip to main content

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_key(|r| std::cmp::Reverse(r.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 = std::env::temp_dir()
436            .join("plugins")
437            .join(&manifest.metadata.name)
438            .display()
439            .to_string();
440        std::fs::create_dir_all(&plugin_dir).map_err(|e| {
441            SklearsError::InvalidOperation(format!("Failed to create plugin directory: {}", e))
442        })?;
443
444        // Write plugin data
445        let plugin_file = format!("{}/plugin.so", plugin_dir);
446        std::fs::write(&plugin_file, data).map_err(|e| {
447            SklearsError::InvalidOperation(format!("Failed to write plugin file: {}", e))
448        })?;
449
450        // Set appropriate permissions (readable and executable)
451        #[cfg(unix)]
452        {
453            use std::os::unix::fs::PermissionsExt;
454            std::fs::set_permissions(&plugin_file, std::fs::Permissions::from_mode(0o755))
455                .map_err(|e| {
456                    SklearsError::InvalidOperation(format!("Failed to set permissions: {}", e))
457                })?;
458        }
459
460        // Write manifest for future reference
461        let manifest_file = format!("{}/manifest.json", plugin_dir);
462        let manifest_json = serde_json::to_string_pretty(manifest).map_err(|e| {
463            SklearsError::InvalidOperation(format!("Failed to serialize manifest: {}", e))
464        })?;
465        std::fs::write(manifest_file, manifest_json).map_err(|e| {
466            SklearsError::InvalidOperation(format!("Failed to write manifest: {}", e))
467        })?;
468
469        Ok(plugin_file)
470    }
471
472    /// Compute content hash for verification
473    fn compute_content_hash(&self, data: &[u8]) -> String {
474        // Simple hash implementation - in production, use SHA-256 or similar
475        use std::collections::hash_map::DefaultHasher;
476        use std::hash::{Hash, Hasher};
477
478        let mut hasher = DefaultHasher::new();
479        data.hash(&mut hasher);
480        format!("{:x}", hasher.finish())
481    }
482
483    /// Get repository statistics
484    ///
485    /// Returns statistics about configured repositories and their status.
486    pub async fn get_repository_stats(&self) -> Vec<RepositoryStats> {
487        let mut stats = Vec::new();
488
489        for repository in &self.repositories {
490            let plugin_count = match self.discover_from_repository(repository).await {
491                Ok(plugins) => plugins.len(),
492                Err(_) => 0,
493            };
494
495            stats.push(RepositoryStats {
496                name: repository.name.clone(),
497                url: repository.url.clone(),
498                plugin_count,
499                verified: repository.verified,
500                last_updated: std::time::SystemTime::now(), // Placeholder
501            });
502        }
503
504        stats
505    }
506}
507
508impl Default for PluginDiscoveryService {
509    fn default() -> Self {
510        Self::new()
511    }
512}
513
514/// Community plugin marketplace
515///
516/// The PluginMarketplace provides a comprehensive platform for plugin discovery,
517/// rating, reviewing, and analytics. It combines the discovery service with
518/// community features to create a full marketplace experience.
519///
520/// # Features
521///
522/// - Featured plugin recommendations
523/// - Community ratings and reviews
524/// - Download tracking and analytics
525/// - Plugin popularity scoring
526/// - Trend analysis and reporting
527///
528/// # Examples
529///
530/// ```rust,ignore
531/// use sklears_core::plugin::PluginMarketplace;
532///
533/// async fn marketplace_demo() -> Result<(), Box<dyn std::error::Error>> {
534///     let marketplace = PluginMarketplace::new();
535///
536///     // Get featured plugins
537///     let featured = marketplace.get_featured_plugins().await?;
538///     println!("Featured plugins: {}", featured.len());
539///
540///     // Rate a plugin
541///     marketplace.rate_plugin("linear_regression", "user123", 4.5).await?;
542///
543///     // Get plugin statistics
544///     let stats = marketplace.get_plugin_stats("linear_regression").await?;
545///     println!("Average rating: {:.1}", stats.average_rating);
546///
547///     Ok(())
548/// }
549/// ```
550#[derive(Debug)]
551pub struct PluginMarketplace {
552    /// Discovery service for plugin management
553    discovery: PluginDiscoveryService,
554    /// Rating system for community feedback
555    rating_system: RatingSystem,
556    /// Review system for detailed feedback
557    review_system: ReviewSystem,
558    /// Download tracking for popularity metrics
559    download_tracker: DownloadTracker,
560    /// Analytics engine for trend analysis
561    analytics: PluginAnalytics,
562}
563
564impl PluginMarketplace {
565    /// Create a new marketplace
566    ///
567    /// Initializes all marketplace components including discovery, ratings,
568    /// reviews, and analytics systems.
569    ///
570    /// # Examples
571    ///
572    /// ```rust
573    /// use sklears_core::plugin::PluginMarketplace;
574    ///
575    /// let marketplace = PluginMarketplace::new();
576    /// ```
577    pub fn new() -> Self {
578        Self {
579            discovery: PluginDiscoveryService::new(),
580            rating_system: RatingSystem::new(),
581            review_system: ReviewSystem::new(),
582            download_tracker: DownloadTracker::new(),
583            analytics: PluginAnalytics::new(),
584        }
585    }
586
587    /// Get featured plugins
588    ///
589    /// Returns a curated list of high-quality plugins based on ratings,
590    /// download counts, and community engagement metrics.
591    ///
592    /// # Returns
593    ///
594    /// A vector of featured plugins sorted by feature score.
595    ///
596    /// # Examples
597    ///
598    /// ```rust,ignore
599    /// use sklears_core::plugin::PluginMarketplace;
600    ///
601    /// async fn show_featured() -> Result<(), Box<dyn std::error::Error>> {
602    ///     let marketplace = PluginMarketplace::new();
603    ///     let featured = marketplace.get_featured_plugins().await?;
604    ///
605    ///     for plugin in featured {
606    ///         println!("{}: {:.1} stars, {} downloads",
607    ///                  plugin.manifest.metadata.name,
608    ///                  plugin.rating,
609    ///                  plugin.download_count);
610    ///     }
611    ///     Ok(())
612    /// }
613    /// ```
614    pub async fn get_featured_plugins(&self) -> Result<Vec<FeaturedPlugin>> {
615        let plugins = self.discovery.discover_all().await?;
616
617        let mut featured = Vec::new();
618        for plugin_manifest in plugins {
619            let plugin_id = plugin_manifest.metadata.name.clone();
620
621            // Get community metrics
622            let rating = self.rating_system.get_average_rating(&plugin_id).await?;
623            let download_count = self.download_tracker.get_download_count(&plugin_id).await?;
624            let review_count = self.review_system.get_review_count(&plugin_id).await?;
625
626            // Calculate feature score
627            let feature_score = self.calculate_feature_score(rating, download_count, review_count);
628
629            // Only include high-quality plugins
630            if feature_score > 7.0 {
631                featured.push(FeaturedPlugin {
632                    manifest: plugin_manifest,
633                    rating,
634                    download_count,
635                    review_count,
636                    feature_score,
637                    trend_direction: self
638                        .analytics
639                        .get_trend_direction(&plugin_id)
640                        .await
641                        .unwrap_or(TrendDirection::Stable),
642                });
643            }
644        }
645
646        // Sort by feature score (descending)
647        featured.sort_by(|a, b| {
648            b.feature_score
649                .partial_cmp(&a.feature_score)
650                .unwrap_or(Ordering::Equal)
651        });
652
653        Ok(featured.into_iter().take(10).collect())
654    }
655
656    /// Submit plugin rating
657    ///
658    /// Allows users to rate plugins on a 1-5 scale. Ratings are used for
659    /// featured plugin selection and recommendation algorithms.
660    ///
661    /// # Arguments
662    ///
663    /// * `plugin_id` - The plugin to rate
664    /// * `user_id` - The user submitting the rating
665    /// * `rating` - Rating value (1.0 to 5.0)
666    ///
667    /// # Errors
668    ///
669    /// Returns an error if the rating is outside the valid range.
670    ///
671    /// # Examples
672    ///
673    /// ```rust,ignore
674    /// use sklears_core::plugin::PluginMarketplace;
675    ///
676    /// async fn rate_plugin() -> Result<(), Box<dyn std::error::Error>> {
677    ///     let marketplace = PluginMarketplace::new();
678    ///     marketplace.rate_plugin("awesome_classifier", "user123", 4.8).await?;
679    ///     println!("Rating submitted successfully");
680    ///     Ok(())
681    /// }
682    /// ```
683    pub async fn rate_plugin(&self, plugin_id: &str, user_id: &str, rating: f32) -> Result<()> {
684        if !(1.0..=5.0).contains(&rating) {
685            return Err(SklearsError::InvalidOperation(
686                "Rating must be between 1.0 and 5.0".to_string(),
687            ));
688        }
689
690        self.rating_system
691            .submit_rating(plugin_id, user_id, rating)
692            .await?;
693        self.analytics.track_rating_event(plugin_id, rating).await?;
694
695        Ok(())
696    }
697
698    /// Submit plugin review
699    ///
700    /// Allows users to submit detailed reviews for plugins, providing
701    /// valuable feedback to other users and plugin developers.
702    ///
703    /// # Arguments
704    ///
705    /// * `plugin_id` - The plugin to review
706    /// * `review` - The review content and metadata
707    ///
708    /// # Examples
709    ///
710    /// ```rust,ignore
711    /// use sklears_core::plugin::{PluginMarketplace, PluginReview};
712    ///
713    /// async fn submit_review() -> Result<(), Box<dyn std::error::Error>> {
714    ///     let marketplace = PluginMarketplace::new();
715    ///
716    ///     let review = PluginReview {
717    ///         user_id: "reviewer123".to_string(),
718    ///         rating: 4.5,
719    ///         title: "Excellent performance".to_string(),
720    ///         content: "This plugin significantly improved our model accuracy.".to_string(),
721    ///         verified_download: true,
722    ///     };
723    ///
724    ///     marketplace.submit_review("awesome_classifier", review).await?;
725    ///     Ok(())
726    /// }
727    /// ```
728    pub async fn submit_review(&self, plugin_id: &str, review: PluginReview) -> Result<()> {
729        self.review_system.submit_review(plugin_id, review).await?;
730        self.analytics.track_review_event(plugin_id).await?;
731
732        Ok(())
733    }
734
735    /// Get comprehensive plugin statistics
736    ///
737    /// Returns detailed statistics about a plugin including ratings,
738    /// downloads, reviews, and trend data.
739    ///
740    /// # Arguments
741    ///
742    /// * `plugin_id` - The plugin to get statistics for
743    ///
744    /// # Returns
745    ///
746    /// Comprehensive plugin statistics and metrics.
747    ///
748    /// # Examples
749    ///
750    /// ```rust,ignore
751    /// use sklears_core::plugin::PluginMarketplace;
752    ///
753    /// async fn show_stats() -> Result<(), Box<dyn std::error::Error>> {
754    ///     let marketplace = PluginMarketplace::new();
755    ///     let stats = marketplace.get_plugin_stats("popular_plugin").await?;
756    ///
757    ///     println!("Rating: {:.1}/5.0", stats.average_rating);
758    ///     println!("Downloads: {}", stats.total_downloads);
759    ///     println!("Reviews: {}", stats.recent_reviews.len());
760    ///     Ok(())
761    /// }
762    /// ```
763    pub async fn get_plugin_stats(&self, plugin_id: &str) -> Result<PluginStats> {
764        let rating = self.rating_system.get_average_rating(plugin_id).await?;
765        let downloads = self.download_tracker.get_download_count(plugin_id).await?;
766        let reviews = self.review_system.get_reviews(plugin_id, 0, 5).await?;
767        let trend = self.analytics.get_trend_data(plugin_id).await?;
768        let rating_distribution = self
769            .rating_system
770            .get_rating_distribution(plugin_id)
771            .await?;
772
773        Ok(PluginStats {
774            average_rating: rating,
775            total_downloads: downloads,
776            recent_reviews: reviews,
777            trend_data: trend,
778            rating_distribution,
779            monthly_downloads: self
780                .download_tracker
781                .get_monthly_downloads(plugin_id)
782                .await?,
783            last_updated: self.analytics.get_last_update_time(plugin_id).await?,
784        })
785    }
786
787    /// Get trending plugins
788    ///
789    /// Returns plugins that are currently trending based on recent
790    /// download activity, ratings, and community engagement.
791    pub async fn get_trending_plugins(&self, limit: usize) -> Result<Vec<TrendingPlugin>> {
792        let all_plugins = self.discovery.discover_all().await?;
793        let mut trending = Vec::new();
794
795        for manifest in all_plugins {
796            let plugin_id = manifest.metadata.name.clone();
797            let trend_score = self.analytics.calculate_trend_score(&plugin_id).await?;
798
799            if trend_score > 0.5 {
800                // Threshold for trending
801                trending.push(TrendingPlugin {
802                    manifest,
803                    trend_score,
804                    recent_downloads: self
805                        .download_tracker
806                        .get_recent_downloads(&plugin_id, 7)
807                        .await?,
808                    velocity: self.analytics.get_download_velocity(&plugin_id).await?,
809                });
810            }
811        }
812
813        trending.sort_by(|a, b| {
814            b.trend_score
815                .partial_cmp(&a.trend_score)
816                .unwrap_or(Ordering::Equal)
817        });
818        Ok(trending.into_iter().take(limit).collect())
819    }
820
821    /// Calculate feature score for plugin ranking
822    ///
823    /// Computes a composite score based on rating, downloads, and reviews
824    /// to determine plugin quality and popularity for featured listings.
825    pub fn calculate_feature_score(&self, rating: f32, downloads: u64, reviews: u64) -> f32 {
826        let rating_weight = 0.4;
827        let download_weight = 0.4;
828        let review_weight = 0.2;
829
830        // Normalize values to 0-10 scale
831        let normalized_rating = rating * 2.0; // 5-star scale to 10-point scale
832
833        // Log scale for downloads (handles zero values)
834        let normalized_downloads = if downloads == 0 {
835            0.0
836        } else {
837            (downloads as f32).log10().min(6.0) / 6.0 * 10.0 // Max at 1M downloads
838        };
839
840        // Log scale for reviews (handles zero values)
841        let normalized_reviews = if reviews == 0 {
842            0.0
843        } else {
844            (reviews as f32).log10().min(3.0) / 3.0 * 10.0 // Max at 1K reviews
845        };
846
847        normalized_rating * rating_weight
848            + normalized_downloads * download_weight
849            + normalized_reviews * review_weight
850    }
851
852    /// Get marketplace analytics summary
853    ///
854    /// Returns overall marketplace statistics and trends.
855    pub async fn get_marketplace_summary(&self) -> Result<MarketplaceSummary> {
856        let total_plugins = self.discovery.discover_all().await?.len();
857        let total_downloads = self.download_tracker.get_total_downloads().await?;
858        let active_users = self.rating_system.get_active_user_count().await?;
859        let trending_categories = self.analytics.get_trending_categories().await?;
860
861        Ok(MarketplaceSummary {
862            total_plugins,
863            total_downloads,
864            active_users,
865            trending_categories,
866            last_updated: std::time::SystemTime::now(),
867        })
868    }
869}
870
871impl Default for PluginMarketplace {
872    fn default() -> Self {
873        Self::new()
874    }
875}
876
877// =============================================================================
878// Supporting Types
879// =============================================================================
880
881/// Plugin repository configuration
882#[derive(Debug, Clone)]
883pub struct PluginRepository {
884    /// Repository name
885    pub name: String,
886    /// Repository URL
887    pub url: String,
888    /// Whether this repository is verified/trusted
889    pub verified: bool,
890    /// Repository priority (higher = checked first)
891    pub priority: u8,
892}
893
894impl PluginRepository {
895    /// Create the official SKLears plugin repository
896    pub fn official() -> Self {
897        Self {
898            name: "Official".to_string(),
899            url: "https://plugins.sklears.rs".to_string(),
900            verified: true,
901            priority: 10,
902        }
903    }
904
905    /// Create the community plugin repository
906    pub fn community() -> Self {
907        Self {
908            name: "Community".to_string(),
909            url: "https://community.sklears.rs".to_string(),
910            verified: true,
911            priority: 5,
912        }
913    }
914}
915
916/// Search query for plugin discovery
917#[derive(Debug, Clone)]
918pub struct SearchQuery {
919    /// Text to search for in plugin names and descriptions
920    pub text: String,
921    /// Filter by plugin category
922    pub category: Option<PluginCategory>,
923    /// Required capabilities
924    pub capabilities: Vec<PluginCapability>,
925    /// Maximum number of results
926    pub limit: Option<usize>,
927    /// Minimum rating filter
928    pub min_rating: Option<f32>,
929}
930
931/// Plugin search result
932#[derive(Debug, Clone)]
933pub struct PluginSearchResult {
934    /// Plugin identifier
935    pub plugin_id: String,
936    /// Plugin description
937    pub description: String,
938    /// Search relevance score
939    pub relevance_score: f32,
940    /// Popularity score
941    pub popularity_score: f32,
942    /// Plugin category
943    pub category: PluginCategory,
944    /// Plugin capabilities
945    pub capabilities: Vec<PluginCapability>,
946    /// Average rating
947    pub rating: f32,
948    /// Download count
949    pub download_count: u64,
950}
951
952/// Plugin installation result
953#[derive(Debug, Clone)]
954pub struct PluginInstallResult {
955    /// Plugin manifest
956    pub manifest: PluginManifest,
957    /// Installation path
958    pub install_path: String,
959    /// Validation report
960    pub validation_report: ValidationReport,
961}
962
963/// Featured plugin information
964#[derive(Debug, Clone)]
965pub struct FeaturedPlugin {
966    /// Plugin manifest
967    pub manifest: PluginManifest,
968    /// Average rating
969    pub rating: f32,
970    /// Total download count
971    pub download_count: u64,
972    /// Number of reviews
973    pub review_count: u64,
974    /// Feature score (0-10)
975    pub feature_score: f32,
976    /// Trend direction
977    pub trend_direction: TrendDirection,
978}
979
980/// Plugin review
981#[derive(Debug, Clone)]
982pub struct PluginReview {
983    /// User who submitted the review
984    pub user_id: String,
985    /// Rating given (1-5)
986    pub rating: f32,
987    /// Review title
988    pub title: String,
989    /// Review content
990    pub content: String,
991    /// Whether user has verified download
992    pub verified_download: bool,
993}
994
995/// Comprehensive plugin statistics
996#[derive(Debug, Clone)]
997pub struct PluginStats {
998    /// Average rating
999    pub average_rating: f32,
1000    /// Total downloads
1001    pub total_downloads: u64,
1002    /// Recent reviews
1003    pub recent_reviews: Vec<PluginReview>,
1004    /// Trend data points
1005    pub trend_data: Vec<f32>,
1006    /// Rating distribution (1-5 stars)
1007    pub rating_distribution: HashMap<u8, u64>,
1008    /// Monthly download counts
1009    pub monthly_downloads: Vec<u64>,
1010    /// Last update time
1011    pub last_updated: std::time::SystemTime,
1012}
1013
1014/// Trending plugin information
1015#[derive(Debug, Clone)]
1016pub struct TrendingPlugin {
1017    /// Plugin manifest
1018    pub manifest: PluginManifest,
1019    /// Trend score
1020    pub trend_score: f32,
1021    /// Recent downloads
1022    pub recent_downloads: u64,
1023    /// Download velocity (downloads per day)
1024    pub velocity: f32,
1025}
1026
1027/// Repository statistics
1028#[derive(Debug, Clone)]
1029pub struct RepositoryStats {
1030    /// Repository name
1031    pub name: String,
1032    /// Repository URL
1033    pub url: String,
1034    /// Number of plugins
1035    pub plugin_count: usize,
1036    /// Whether repository is verified
1037    pub verified: bool,
1038    /// Last update time
1039    pub last_updated: std::time::SystemTime,
1040}
1041
1042/// Marketplace summary statistics
1043#[derive(Debug, Clone)]
1044pub struct MarketplaceSummary {
1045    /// Total number of plugins
1046    pub total_plugins: usize,
1047    /// Total download count across all plugins
1048    pub total_downloads: u64,
1049    /// Number of active users
1050    pub active_users: u64,
1051    /// Trending categories
1052    pub trending_categories: Vec<PluginCategory>,
1053    /// Last update time
1054    pub last_updated: std::time::SystemTime,
1055}
1056
1057/// Trend direction enumeration
1058#[derive(Debug, Clone, PartialEq)]
1059pub enum TrendDirection {
1060    Rising,
1061    Stable,
1062    Declining,
1063}
1064
1065/// Marketplace specific metadata
1066#[derive(Debug, Clone)]
1067pub struct MarketplaceInfo {
1068    pub tags: Vec<String>,
1069    pub license: String,
1070    pub repository_url: Option<String>,
1071    pub documentation_url: Option<String>,
1072    pub pricing: PricingInfo,
1073    pub support_level: SupportLevel,
1074}
1075
1076/// Plugin pricing information
1077#[derive(Debug, Clone)]
1078pub enum PricingInfo {
1079    Free,
1080    OneTime(f64),
1081    Subscription(f64, SubscriptionPeriod),
1082    UsageBased(f64, UsageUnit),
1083}
1084
1085/// Subscription period
1086#[derive(Debug, Clone)]
1087pub enum SubscriptionPeriod {
1088    Monthly,
1089    Yearly,
1090}
1091
1092/// Usage unit for pricing
1093#[derive(Debug, Clone)]
1094pub enum UsageUnit {
1095    PerPrediction,
1096    PerDataPoint,
1097    PerHour,
1098}
1099
1100/// Support level offered
1101#[derive(Debug, Clone)]
1102pub enum SupportLevel {
1103    Community,
1104    Basic,
1105    Professional,
1106    Enterprise,
1107}
1108
1109// =============================================================================
1110// Placeholder Implementations for Supporting Services
1111// =============================================================================
1112
1113/// Plugin cache for local storage
1114#[derive(Debug, Clone)]
1115pub struct PluginCache;
1116
1117impl Default for PluginCache {
1118    fn default() -> Self {
1119        Self::new()
1120    }
1121}
1122
1123impl PluginCache {
1124    pub fn new() -> Self {
1125        Self
1126    }
1127
1128    pub fn get_repository_plugins(&self, _url: &str) -> Option<CachedPlugins> {
1129        None
1130    }
1131
1132    pub fn store_repository_plugins(&self, _url: &str, _plugins: &[PluginManifest]) {}
1133}
1134
1135/// Search index for fast plugin lookups
1136#[derive(Debug, Clone)]
1137pub struct SearchIndex;
1138
1139impl Default for SearchIndex {
1140    fn default() -> Self {
1141        Self::new()
1142    }
1143}
1144
1145impl SearchIndex {
1146    pub fn new() -> Self {
1147        Self
1148    }
1149
1150    pub fn index_plugins(&self, _plugins: &[PluginManifest]) {}
1151
1152    pub fn search(&self, _query: &SearchQuery) -> Result<Vec<PluginSearchResult>> {
1153        Ok(Vec::new())
1154    }
1155}
1156
1157/// Network client for remote operations
1158#[derive(Debug, Clone)]
1159pub struct NetworkClient;
1160
1161impl Default for NetworkClient {
1162    fn default() -> Self {
1163        Self::new()
1164    }
1165}
1166
1167impl NetworkClient {
1168    pub fn new() -> Self {
1169        Self
1170    }
1171
1172    pub async fn fetch_repository_plugins(
1173        &self,
1174        _repo: &PluginRepository,
1175    ) -> Result<Vec<PluginManifest>> {
1176        Ok(Vec::new())
1177    }
1178
1179    pub async fn search_repository(
1180        &self,
1181        _repo: &PluginRepository,
1182        _query: &SearchQuery,
1183    ) -> Result<Vec<PluginSearchResult>> {
1184        Ok(Vec::new())
1185    }
1186
1187    pub async fn download_plugin(&self, _manifest: &PluginManifest) -> Result<Vec<u8>> {
1188        Ok(Vec::new())
1189    }
1190
1191    pub async fn get_plugin_manifest(
1192        &self,
1193        _repo: &PluginRepository,
1194        _id: &str,
1195        _version: Option<&str>,
1196    ) -> Result<PluginManifest> {
1197        Err(SklearsError::InvalidOperation("Not found".to_string()))
1198    }
1199}
1200
1201/// Rating system for community feedback
1202#[derive(Debug, Clone)]
1203pub struct RatingSystem;
1204
1205impl Default for RatingSystem {
1206    fn default() -> Self {
1207        Self::new()
1208    }
1209}
1210
1211impl RatingSystem {
1212    pub fn new() -> Self {
1213        Self
1214    }
1215
1216    pub async fn get_average_rating(&self, _plugin_id: &str) -> Result<f32> {
1217        Ok(4.5)
1218    }
1219
1220    pub async fn submit_rating(
1221        &self,
1222        _plugin_id: &str,
1223        _user_id: &str,
1224        _rating: f32,
1225    ) -> Result<()> {
1226        Ok(())
1227    }
1228
1229    pub async fn get_rating_distribution(&self, _plugin_id: &str) -> Result<HashMap<u8, u64>> {
1230        Ok(HashMap::new())
1231    }
1232
1233    pub async fn get_active_user_count(&self) -> Result<u64> {
1234        Ok(1000)
1235    }
1236}
1237
1238/// Review system for detailed feedback
1239#[derive(Debug, Clone)]
1240pub struct ReviewSystem;
1241
1242impl Default for ReviewSystem {
1243    fn default() -> Self {
1244        Self::new()
1245    }
1246}
1247
1248impl ReviewSystem {
1249    pub fn new() -> Self {
1250        Self
1251    }
1252
1253    pub async fn get_review_count(&self, _plugin_id: &str) -> Result<u64> {
1254        Ok(100)
1255    }
1256
1257    pub async fn submit_review(&self, _plugin_id: &str, _review: PluginReview) -> Result<()> {
1258        Ok(())
1259    }
1260
1261    pub async fn get_reviews(
1262        &self,
1263        _plugin_id: &str,
1264        _offset: usize,
1265        _limit: usize,
1266    ) -> Result<Vec<PluginReview>> {
1267        Ok(Vec::new())
1268    }
1269}
1270
1271/// Download tracking for popularity metrics
1272#[derive(Debug, Clone)]
1273pub struct DownloadTracker;
1274
1275impl Default for DownloadTracker {
1276    fn default() -> Self {
1277        Self::new()
1278    }
1279}
1280
1281impl DownloadTracker {
1282    pub fn new() -> Self {
1283        Self
1284    }
1285
1286    pub async fn get_download_count(&self, _plugin_id: &str) -> Result<u64> {
1287        Ok(1000)
1288    }
1289
1290    pub async fn get_monthly_downloads(&self, _plugin_id: &str) -> Result<Vec<u64>> {
1291        Ok(vec![100, 120, 150, 180])
1292    }
1293
1294    pub async fn get_recent_downloads(&self, _plugin_id: &str, _days: u32) -> Result<u64> {
1295        Ok(50)
1296    }
1297
1298    pub async fn get_total_downloads(&self) -> Result<u64> {
1299        Ok(1000000)
1300    }
1301}
1302
1303/// Analytics engine for trend analysis
1304#[derive(Debug, Clone)]
1305pub struct PluginAnalytics;
1306
1307impl Default for PluginAnalytics {
1308    fn default() -> Self {
1309        Self::new()
1310    }
1311}
1312
1313impl PluginAnalytics {
1314    pub fn new() -> Self {
1315        Self
1316    }
1317
1318    pub async fn track_rating_event(&self, _plugin_id: &str, _rating: f32) -> Result<()> {
1319        Ok(())
1320    }
1321
1322    pub async fn track_review_event(&self, _plugin_id: &str) -> Result<()> {
1323        Ok(())
1324    }
1325
1326    pub async fn get_trend_data(&self, _plugin_id: &str) -> Result<Vec<f32>> {
1327        Ok(vec![1.0, 1.2, 1.5, 1.8])
1328    }
1329
1330    pub async fn get_trend_direction(&self, _plugin_id: &str) -> Result<TrendDirection> {
1331        Ok(TrendDirection::Stable)
1332    }
1333
1334    pub async fn calculate_trend_score(&self, _plugin_id: &str) -> Result<f32> {
1335        Ok(0.7)
1336    }
1337
1338    pub async fn get_download_velocity(&self, _plugin_id: &str) -> Result<f32> {
1339        Ok(5.0)
1340    }
1341
1342    pub async fn get_last_update_time(&self, _plugin_id: &str) -> Result<std::time::SystemTime> {
1343        Ok(std::time::SystemTime::now())
1344    }
1345
1346    pub async fn get_trending_categories(&self) -> Result<Vec<PluginCategory>> {
1347        Ok(vec![PluginCategory::Algorithm, PluginCategory::Transformer])
1348    }
1349}
1350
1351/// Cached plugins with expiration
1352#[derive(Debug, Clone)]
1353pub struct CachedPlugins {
1354    pub plugins: Vec<PluginManifest>,
1355}
1356
1357impl CachedPlugins {
1358    pub fn is_expired(&self) -> bool {
1359        false // Placeholder implementation
1360    }
1361}
1362
1363/// Dummy plugin for validation testing
1364#[derive(Debug)]
1365pub struct DummyPlugin;
1366
1367impl DummyPlugin {
1368    #[allow(clippy::new_ret_no_self)]
1369    pub fn new() -> Box<dyn Plugin> {
1370        Box::new(Self)
1371    }
1372}
1373
1374impl Plugin for DummyPlugin {
1375    fn id(&self) -> &str {
1376        "dummy"
1377    }
1378
1379    fn metadata(&self) -> PluginMetadata {
1380        PluginMetadata::default()
1381    }
1382
1383    fn initialize(&mut self, _config: &super::types_config::PluginConfig) -> Result<()> {
1384        Ok(())
1385    }
1386
1387    fn is_compatible(&self, _input_type: std::any::TypeId) -> bool {
1388        true
1389    }
1390
1391    fn as_any(&self) -> &dyn std::any::Any {
1392        self
1393    }
1394
1395    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
1396        self
1397    }
1398
1399    fn validate_config(&self, _config: &super::types_config::PluginConfig) -> Result<()> {
1400        Ok(())
1401    }
1402
1403    fn cleanup(&mut self) -> Result<()> {
1404        Ok(())
1405    }
1406}