插件(Plugin)

如何实现一个可扩展的插件系统,在 rust中,可以使用 inventory crates 实现类似的目标。

样例代码:

#![allow(unused)]
fn main() {
use inventory::submit;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

// Define a trait for inventory operations
trait InventoryOp: Send + Sync {
    fn name(&self) -> &'static str;
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32);
}

// Structure for registry
#[derive(Clone, Copy)]
struct InventoryPlugin {
    name: &'static str,
    handler: &'static dyn InventoryOp,
}

inventory::collect!(InventoryPlugin);

// Plugin: Empty Operation
struct EmptyOp;
impl InventoryOp for EmptyOp {
    fn name(&self) -> &'static str {
        ""
    }
    fn execute(&self, _inventory: &Mutex<HashMap<String, u32>>, _item: &str, _quantity: u32) {}
}

inventory::submit! {
    InventoryPlugin {
        name: "",
        handler: &EmptyOp,
    }
}

// Plugin 1: Add Item
struct AddItem;
impl InventoryOp for AddItem {
    fn name(&self) -> &'static str {
        "add"
    }
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32) {
        let mut inv = inventory.lock().unwrap();
        let current = inv.get(item).copied().unwrap_or(0);
        inv.insert(item.to_string(), current + quantity);
        println!("Added {} {} to inventory", quantity, item);
    }
}

inventory::submit! {
    InventoryPlugin {
        name: "add",
        handler: &AddItem,
    }
}

// Plugin 2: Remove Item
struct RemoveItem;
impl InventoryOp for RemoveItem {
    fn name(&self) -> &'static str {
        "remove"
    }
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32) {
        let mut inv = inventory.lock().unwrap();
        if let Some(current) = inv.get_mut(item) {
            if *current >= quantity {
                *current -= quantity;
                println!("Removed {} {} from inventory", quantity, item);
            } else {
                println!("Error: Not enough {} in inventory", item);
            }
        } else {
            println!("Error: {} not found in inventory", item);
        }
    }
}

inventory::submit! {
    InventoryPlugin {
        name: "remove",
        handler: &RemoveItem,
    }
}

fn inventory_main() {
    // Thread-safe inventory
    let inventory = Arc::new(Mutex::new(HashMap::<String, u32>::new()));
    inventory.lock().unwrap().insert("apple".to_string(), 10);

    // Collect plugins
    let mut plugins: HashMap<&'static str, &'static dyn InventoryOp> = HashMap::new();

    for plugin in inventory::iter::<InventoryPlugin> {
        if !plugin.name.is_empty() {
            plugins.insert(plugin.name, plugin.handler);
        }
    }

    // Simulate operations
    let ops = vec![
        ("add", "apple", 5),
        ("remove", "apple", 3),
        ("add", "sword", 2),
        ("remove", "apple", 20),
    ];

    for (op_name, item, quantity) in ops {
        if let Some(op) = plugins.get(op_name) {
            op.execute(&inventory, item, quantity);
        } else {
            println!("Unknown operation: {}", op_name);
        }
    }

    // Print final inventory
    println!("Final inventory:");
    let inv = inventory.lock().unwrap();
    for (key, quantity) in inv.iter() {
        println!("{}: {}", key, quantity);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_inventory_main() {
        inventory_main();
    }
}



}