Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I34e7c6d382ab7f4b6cf98ede9b7116056a6a6964
375 lines
9.9 KiB
Rust
375 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 async_trait::async_trait;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::path::{Path, PathBuf};
|
|
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>,
|
|
}
|