pinakes/crates/pinakes-plugin-api/src/lib.rs
NotAShelf 3ccddce7fd
treewide: fix various UI bugs; optimize crypto dependencies & format
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: If8fe8b38c1d9c4fecd40ff71f88d2ae06a6a6964
2026-03-06 18:29:33 +03:00

390 lines
9.9 KiB
Rust

//! Pinakes Plugin API
//!
//! This crate defines the stable plugin interface for Pinakes.
//! Plugins can extend Pinakes by implementing one or more of the provided
//! traits.
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub mod manifest;
pub mod types;
pub mod wasm;
pub use manifest::PluginManifest;
pub use types::*;
pub use wasm::host_functions;
/// Plugin API version - plugins must match this version
pub const PLUGIN_API_VERSION: &str = "1.0";
/// Result type for plugin operations
pub type PluginResult<T> = Result<T, PluginError>;
/// Errors that can occur in plugin operations
#[derive(Debug, Error, Serialize, Deserialize)]
pub enum PluginError {
#[error("Plugin initialization failed: {0}")]
InitializationFailed(String),
#[error("Unsupported operation: {0}")]
UnsupportedOperation(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("IO error: {0}")]
IoError(String),
#[error("Metadata extraction failed: {0}")]
MetadataExtractionFailed(String),
#[error("Thumbnail generation failed: {0}")]
ThumbnailGenerationFailed(String),
#[error("Search backend error: {0}")]
SearchBackendError(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Resource limit exceeded: {0}")]
ResourceLimitExceeded(String),
#[error("Plugin error: {0}")]
Other(String),
}
/// Context provided to plugins during initialization
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginContext {
/// Plugin's data directory for persistent storage
pub data_dir: PathBuf,
/// Plugin's cache directory for temporary data
pub cache_dir: PathBuf,
/// Plugin configuration from manifest
pub config: HashMap<String, serde_json::Value>,
/// Capabilities granted to the plugin
pub capabilities: Capabilities,
}
/// Capabilities that can be granted to plugins
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Capabilities {
/// Filesystem access permissions
pub filesystem: FilesystemCapability,
/// Network access permissions
pub network: NetworkCapability,
/// Environment variable access
pub environment: EnvironmentCapability,
/// Maximum memory usage in bytes
pub max_memory_bytes: Option<usize>,
/// Maximum CPU time in milliseconds
pub max_cpu_time_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FilesystemCapability {
/// Paths allowed for reading
pub read: Vec<PathBuf>,
/// Paths allowed for writing
pub write: Vec<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct NetworkCapability {
/// Whether network access is allowed
pub enabled: bool,
/// Allowed domains (if None, all domains allowed when enabled)
pub allowed_domains: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EnvironmentCapability {
/// Whether environment variable access is allowed
pub enabled: bool,
/// Specific environment variables allowed (if None, all allowed when
/// enabled)
pub allowed_vars: Option<Vec<String>>,
}
/// Base trait that all plugins must implement
#[async_trait]
pub trait Plugin: Send + Sync {
/// Get plugin metadata
fn metadata(&self) -> &PluginMetadata;
/// Initialize the plugin with provided context
async fn initialize(&mut self, context: PluginContext) -> PluginResult<()>;
/// Shutdown the plugin gracefully
async fn shutdown(&mut self) -> PluginResult<()>;
/// Get plugin health status
async fn health_check(&self) -> PluginResult<HealthStatus>;
}
/// Plugin metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginMetadata {
pub id: String,
pub name: String,
pub version: String,
pub author: String,
pub description: String,
pub api_version: String,
pub capabilities_required: Capabilities,
}
/// Health status of a plugin
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
pub healthy: bool,
pub message: Option<String>,
pub metrics: HashMap<String, f64>,
}
/// Trait for plugins that provide custom media type support
#[async_trait]
pub trait MediaTypeProvider: Plugin {
/// Get the list of media types this plugin supports
fn supported_media_types(&self) -> Vec<MediaTypeDefinition>;
/// Check if this plugin can handle the given file
async fn can_handle(
&self,
path: &Path,
mime_type: Option<&str>,
) -> PluginResult<bool>;
}
/// Definition of a custom media type
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediaTypeDefinition {
/// Unique identifier for this media type
pub id: String,
/// Display name
pub name: String,
/// Category (e.g., "video", "audio", "document", "image")
pub category: String,
/// File extensions associated with this type
pub extensions: Vec<String>,
/// MIME types associated with this type
pub mime_types: Vec<String>,
/// Icon name or path
pub icon: Option<String>,
}
/// Trait for plugins that extract metadata from files
#[async_trait]
pub trait MetadataExtractor: Plugin {
/// Extract metadata from a file
async fn extract_metadata(
&self,
path: &Path,
) -> PluginResult<ExtractedMetadata>;
/// Get the media types this extractor supports
fn supported_types(&self) -> Vec<String>;
}
/// Metadata extracted from a file
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ExtractedMetadata {
pub title: Option<String>,
pub description: Option<String>,
pub author: Option<String>,
pub created_at: Option<String>,
pub duration_secs: Option<f64>,
pub width: Option<u32>,
pub height: Option<u32>,
pub file_size_bytes: Option<u64>,
pub codec: Option<String>,
pub bitrate_kbps: Option<u32>,
/// Custom metadata fields specific to this file type
pub custom_fields: HashMap<String, serde_json::Value>,
/// Tags extracted from the file
pub tags: Vec<String>,
}
/// Trait for plugins that generate thumbnails
#[async_trait]
pub trait ThumbnailGenerator: Plugin {
/// Generate a thumbnail for the given file
async fn generate_thumbnail(
&self,
path: &Path,
output_path: &Path,
options: ThumbnailOptions,
) -> PluginResult<ThumbnailInfo>;
/// Get the media types this generator supports
fn supported_types(&self) -> Vec<String>;
}
/// Options for thumbnail generation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThumbnailOptions {
pub width: u32,
pub height: u32,
pub quality: u8,
pub format: ThumbnailFormat,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ThumbnailFormat {
Jpeg,
Png,
WebP,
}
/// Information about a generated thumbnail
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThumbnailInfo {
pub path: PathBuf,
pub width: u32,
pub height: u32,
pub file_size_bytes: u64,
}
/// Trait for plugins that provide custom search backends
#[async_trait]
pub trait SearchBackend: Plugin {
/// Index a media item for search
async fn index_item(&self, item: &SearchIndexItem) -> PluginResult<()>;
/// Remove an item from the search index
async fn remove_item(&self, item_id: &str) -> PluginResult<()>;
/// Perform a search query
async fn search(
&self,
query: &SearchQuery,
) -> PluginResult<Vec<SearchResult>>;
/// Get search statistics
async fn get_stats(&self) -> PluginResult<SearchStats>;
}
/// Item to be indexed for search
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchIndexItem {
pub id: String,
pub title: Option<String>,
pub description: Option<String>,
pub content: Option<String>,
pub tags: Vec<String>,
pub media_type: String,
pub metadata: HashMap<String, serde_json::Value>,
}
/// Search query
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchQuery {
pub query_text: String,
pub filters: HashMap<String, serde_json::Value>,
pub limit: usize,
pub offset: usize,
}
/// Search result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub id: String,
pub score: f64,
pub highlights: Vec<String>,
}
/// Search statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchStats {
pub total_indexed: usize,
pub index_size_bytes: u64,
pub last_update: Option<String>,
}
/// Trait for plugins that handle events
#[async_trait]
pub trait EventHandler: Plugin {
/// Handle an event
async fn handle_event(&self, event: &Event) -> PluginResult<()>;
/// Get the event types this handler is interested in
fn interested_events(&self) -> Vec<EventType>;
}
/// Event type
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum EventType {
MediaImported,
MediaUpdated,
MediaDeleted,
MediaTagged,
MediaUntagged,
CollectionCreated,
CollectionUpdated,
CollectionDeleted,
ScanStarted,
ScanCompleted,
Custom(String),
}
/// Event data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub event_type: EventType,
pub timestamp: String,
pub data: HashMap<String, serde_json::Value>,
}
/// Trait for plugins that provide UI themes
#[async_trait]
pub trait ThemeProvider: Plugin {
/// Get available themes from this provider
fn get_themes(&self) -> Vec<ThemeDefinition>;
/// Load a specific theme by ID
async fn load_theme(&self, theme_id: &str) -> PluginResult<Theme>;
}
/// Theme definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThemeDefinition {
pub id: String,
pub name: String,
pub description: String,
pub author: String,
pub preview_url: Option<String>,
}
/// Theme data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Theme {
pub id: String,
pub colors: HashMap<String, String>,
pub fonts: HashMap<String, String>,
pub custom_css: Option<String>,
}