use serde::{Deserialize, Serialize};
use crate::hooks::Hook;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginManifest {
pub name: String,
pub version: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub author: Option<String>,
#[serde(default)]
pub license: Option<String>,
#[serde(default)]
pub homepage: Option<String>,
#[serde(default)]
pub min_rx_version: Option<String>,
#[serde(default)]
pub hooks: Vec<String>,
#[serde(default)]
pub permissions: PluginPermissions,
}
impl PluginManifest {
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
name: name.into(),
version: version.into(),
description: String::new(),
author: None,
license: None,
homepage: None,
min_rx_version: None,
hooks: Vec::new(),
permissions: PluginPermissions::default(),
}
}
pub fn has_hook(&self, hook: Hook) -> bool {
self.hooks.contains(&hook.function_name().to_string())
}
pub fn from_toml(content: &str) -> Result<Self, toml::de::Error> {
toml::from_str(content)
}
pub fn to_toml(&self) -> Result<String, toml::ser::Error> {
toml::to_string_pretty(self)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PluginPermissions {
#[serde(default)]
pub read_files: bool,
#[serde(default)]
pub write_files: bool,
#[serde(default)]
pub network: bool,
#[serde(default)]
pub env_vars: bool,
#[serde(default)]
pub execute: bool,
#[serde(default)]
pub allowed_read_paths: Vec<String>,
#[serde(default)]
pub allowed_write_paths: Vec<String>,
#[serde(default)]
pub allowed_hosts: Vec<String>,
}
impl PluginPermissions {
pub fn none() -> Self {
Self::default()
}
pub fn read_only() -> Self {
Self {
read_files: true,
..Default::default()
}
}
pub fn full_file_access() -> Self {
Self {
read_files: true,
write_files: true,
..Default::default()
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
pub source: String,
#[serde(default)]
pub permissions: Option<PluginPermissions>,
#[serde(default)]
pub settings: serde_json::Value,
#[serde(default = "default_enabled")]
pub enabled: bool,
}
fn default_enabled() -> bool {
true
}
impl PluginConfig {
pub fn new(source: impl Into<String>) -> Self {
Self {
source: source.into(),
permissions: None,
settings: serde_json::Value::Null,
enabled: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_manifest_from_toml() {
let toml = r#"
name = "my-plugin"
version = "1.0.0"
description = "A test plugin"
hooks = ["pre_build", "post_build"]
[permissions]
read_files = true
write_files = false
"#;
let manifest = PluginManifest::from_toml(toml).unwrap();
assert_eq!(manifest.name, "my-plugin");
assert_eq!(manifest.version, "1.0.0");
assert!(manifest.has_hook(Hook::PreBuild));
assert!(manifest.has_hook(Hook::PostBuild));
assert!(!manifest.has_hook(Hook::PreResolve));
assert!(manifest.permissions.read_files);
assert!(!manifest.permissions.write_files);
}
#[test]
fn test_manifest_to_toml() {
let manifest = PluginManifest::new("test", "0.1.0");
let toml = manifest.to_toml().unwrap();
assert!(toml.contains("name = \"test\""));
}
}