rootftp 0.3.0

A simple FTP server tool that allows you to play with files. Multiple users can share their files with each other. You can also build custom plugins see examples for more info.
Documentation
use crate::plugin_handler::plugin_trait::Plugin;
use libloading::Library;
use std::sync::Arc;
use std::{collections::HashMap, fs, path::Path};

pub struct PluginInstance {
    pub plugin: Box<dyn Plugin>,
    pub exts: Vec<String>,
    _lib: Library,
}

pub fn load_plugins(dir: &Path) -> HashMap<String, Arc<PluginInstance>> {
    let mut map = HashMap::new();

    let Ok(entries) = fs::read_dir(dir) else {
        return map;
    };

    for entry in entries.flatten() {
        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) != Some("so") {
            continue;
        }
        let name = match path.file_stem() {
            Some(s) => s.to_string_lossy().into_owned(),
            None => continue,
        };

        let lib = match unsafe { Library::new(&path) } {
            Ok(l) => l,
            Err(e) => {
                eprintln!("Failed to load plugin '{}': {}", path.display(), e);
                continue;
            }
        };

        let plugin = unsafe {
            let func: libloading::Symbol<unsafe extern "C" fn() -> *mut dyn Plugin> = match lib.get(b"register_plugin") {
                Ok(f) => f,
                Err(e) => {
                    eprintln!("Plugin '{}' missing register_plugin: {}", name, e);
                    continue;
                }
            };
            let raw = func();
            if raw.is_null() {
                eprintln!("Plugin '{}' returned null pointer from register_plugin", name);
                continue;
            }
            Box::from_raw(raw)
        };

        plugin.init();
        let exts = plugin.extensions().iter().map(|&s| s.to_string()).collect();
        map.insert(name, Arc::new(PluginInstance { plugin, exts, _lib: lib }));
    }
    map
}