openscript_sdk 0.1.0

Standard library and AI integration for OpenScript
Documentation
//! Plugin system for OpenScript

use std::collections::HashMap;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginMetadata {
    pub name: String,
    pub version: String,
    pub description: String,
    pub author: String,
    pub license: String,
    pub dependencies: Vec<String>,
    pub api_version: String,
}

#[async_trait]
pub trait Plugin: Send + Sync {
    /// Get plugin metadata
    fn metadata(&self) -> &PluginMetadata;
    
    /// Initialize the plugin
    async fn initialize(&mut self) -> Result<()>;
    
    /// Register plugin commands
    fn register_commands(&self) -> HashMap<String, Box<dyn PluginCommand>>;
    
    /// Cleanup when plugin is unloaded
    async fn cleanup(&mut self) -> Result<()>;
}

#[async_trait]
pub trait PluginCommand: Send + Sync {
    /// Execute the command
    async fn execute(&self, args: &[String]) -> Result<String>;
    
    /// Get command help text
    fn help(&self) -> &str;
    
    /// Get command usage information
    fn usage(&self) -> &str;
}

pub struct PluginManager {
    plugins: HashMap<String, Box<dyn Plugin>>,
    commands: HashMap<String, Box<dyn PluginCommand>>,
}

impl PluginManager {
    pub fn new() -> Self {
        Self {
            plugins: HashMap::new(),
            commands: HashMap::new(),
        }
    }
    
    pub async fn load_plugin(&mut self, plugin: Box<dyn Plugin>) -> Result<()> {
        let name = plugin.metadata().name.clone();
        
        // Initialize plugin
        let mut plugin = plugin;
        plugin.initialize().await?;
        
        // Register commands
        let commands = plugin.register_commands();
        for (cmd_name, command) in commands {
            self.commands.insert(cmd_name, command);
        }
        
        self.plugins.insert(name, plugin);
        Ok(())
    }
    
    pub async fn unload_plugin(&mut self, name: &str) -> Result<()> {
        if let Some(mut plugin) = self.plugins.remove(name) {
            plugin.cleanup().await?;
        }
        Ok(())
    }
    
    pub fn get_command(&self, name: &str) -> Option<&Box<dyn PluginCommand>> {
        self.commands.get(name)
    }
    
    pub fn list_plugins(&self) -> Vec<&PluginMetadata> {
        self.plugins.values().map(|p| p.metadata()).collect()
    }
    
    pub fn list_commands(&self) -> Vec<&str> {
        self.commands.keys().map(|s| s.as_str()).collect()
    }
}

// Example plugin implementation
pub struct HttpPlugin {
    metadata: PluginMetadata,
}

impl HttpPlugin {
    pub fn new() -> Self {
        Self {
            metadata: PluginMetadata {
                name: "http".to_string(),
                version: "1.0.0".to_string(),
                description: "HTTP client plugin for OpenScript".to_string(),
                author: "OpenScript Team".to_string(),
                license: "MIT".to_string(),
                dependencies: vec!["reqwest".to_string()],
                api_version: "1.0".to_string(),
            },
        }
    }
}

#[async_trait]
impl Plugin for HttpPlugin {
    fn metadata(&self) -> &PluginMetadata {
        &self.metadata
    }
    
    async fn initialize(&mut self) -> Result<()> {
        // Initialize HTTP client, load configuration, etc.
        Ok(())
    }
    
    fn register_commands(&self) -> HashMap<String, Box<dyn PluginCommand>> {
        let mut commands = HashMap::new();
        commands.insert("http_get".to_string(), Box::new(HttpGetCommand) as Box<dyn PluginCommand>);
        commands.insert("http_post".to_string(), Box::new(HttpPostCommand) as Box<dyn PluginCommand>);
        commands
    }
    
    async fn cleanup(&mut self) -> Result<()> {
        // Cleanup resources
        Ok(())
    }
}

struct HttpGetCommand;

#[async_trait]
impl PluginCommand for HttpGetCommand {
    async fn execute(&self, args: &[String]) -> Result<String> {
        if args.is_empty() {
            return Err("URL required".into());
        }
        
        let url = &args[0];
        let client = reqwest::Client::new();
        let response = client.get(url).send().await.map_err(|e| e.to_string())?;
        let text = response.text().await.map_err(|e| e.to_string())?;
        
        Ok(text)
    }
    
    fn help(&self) -> &str {
        "Send HTTP GET request to specified URL"
    }
    
    fn usage(&self) -> &str {
        "http_get <url> [headers...]"
    }
}

struct HttpPostCommand;

#[async_trait]
impl PluginCommand for HttpPostCommand {
    async fn execute(&self, args: &[String]) -> Result<String> {
        if args.len() < 2 {
            return Err("URL and body required".into());
        }
        
        let url = &args[0];
        let body = &args[1];
        
        let client = reqwest::Client::new();
        let response = client.post(url)
            .body(body.clone())
            .header("content-type", "application/json")
            .send()
            .await
            .map_err(|e| e.to_string())?;
            
        let text = response.text().await.map_err(|e| e.to_string())?;
        Ok(text)
    }
    
    fn help(&self) -> &str {
        "Send HTTP POST request with specified body"
    }
    
    fn usage(&self) -> &str {
        "http_post <url> <body> [headers...]"
    }
}