use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub type PluginResult<T> = Result<T, PluginError>;
#[derive(Debug, Error)]
pub enum PluginError {
#[error("Plugin not found: {path}")]
NotFound { path: PathBuf },
#[error("Failed to load plugin '{name}': {message}")]
LoadError { name: String, message: String },
#[error("Plugin '{name}' execution error: {message}")]
ExecutionError { name: String, message: String },
#[error("Plugin '{name}' was cancelled")]
Cancelled { name: String },
#[error("Plugin '{name}' timed out after {timeout_ms}ms")]
Timeout { name: String, timeout_ms: u64 },
#[error("Invalid plugin configuration: {message}")]
ConfigError { message: String },
#[error("Permission denied for plugin '{name}': {action}")]
PermissionDenied { name: String, action: String },
#[error("Runtime '{runtime}' is not available")]
RuntimeNotAvailable { runtime: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Hook '{hook}' not implemented by plugin '{name}'")]
HookNotImplemented { name: String, hook: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PluginKind {
#[default]
Analyzer,
Previewer,
Action,
Renderer,
Filter,
Hook,
}
impl std::fmt::Display for PluginKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Analyzer => write!(f, "analyzer"),
Self::Previewer => write!(f, "previewer"),
Self::Action => write!(f, "action"),
Self::Renderer => write!(f, "renderer"),
Self::Filter => write!(f, "filter"),
Self::Hook => write!(f, "hook"),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
#[default]
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Array(Vec<Value>),
Object(std::collections::HashMap<String, Value>),
Bytes(Vec<u8>),
}
impl Value {
pub fn null() -> Self {
Self::Null
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Self::Integer(i) => Some(*i),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Self::Float(f) => Some(*f),
Self::Integer(i) => Some(*i as f64),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Self::Array(arr) => Some(arr),
_ => None,
}
}
pub fn as_object(&self) -> Option<&std::collections::HashMap<String, Value>> {
match self {
Self::Object(obj) => Some(obj),
_ => None,
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Self::Bool(b)
}
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Self::Integer(i)
}
}
impl From<i32> for Value {
fn from(i: i32) -> Self {
Self::Integer(i as i64)
}
}
impl From<u64> for Value {
fn from(u: u64) -> Self {
if let Ok(i) = i64::try_from(u) {
Self::Integer(i)
} else {
Self::Float(u as f64)
}
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Self::Float(f)
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Self::String(s.to_string())
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(arr: Vec<T>) -> Self {
Self::Array(arr.into_iter().map(Into::into).collect())
}
}
impl From<std::collections::HashMap<String, Value>> for Value {
fn from(obj: std::collections::HashMap<String, Value>) -> Self {
Self::Object(obj)
}
}
impl From<PathBuf> for Value {
fn from(path: PathBuf) -> Self {
Self::String(path.to_string_lossy().to_string())
}
}