• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dariusbakunas / cogrs / 13486377277

23 Feb 2025 07:54PM UTC coverage: 36.597% (-0.4%) from 36.957%
13486377277

push

github

dariusbakunas
refactor: extract init code from adhoc cli to cli trait

0 of 29 new or added lines in 2 files covered. (0.0%)

95 existing lines in 4 files now uncovered.

714 of 1951 relevant lines covered (36.6%)

1.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/cogrs-plugins/src/plugin_loader.rs
1
use crate::callback::CallbackPlugin;
2
use crate::plugin_type::PluginType;
3
use anyhow::{Context, Result};
4
use libloading::{Library, Symbol};
5
use log::warn;
6
use once_cell::sync::Lazy;
7
use std::collections::HashMap;
8
use std::fs;
9
use std::path::{Path, PathBuf};
10
use std::sync::Arc;
11
use tokio::sync::Mutex;
12

13
pub struct PluginLoader {
14
    plugin_paths: HashMap<PluginType, Vec<PathBuf>>,
15
    cached_callback_plugins: Mutex<Option<Vec<Arc<dyn CallbackPlugin>>>>,
16
}
17

18
impl PluginLoader {
19
    fn new() -> Self {
×
20
        PluginLoader {
UNCOV
21
            cached_callback_plugins: Mutex::new(None),
×
UNCOV
22
            plugin_paths: HashMap::new(),
×
23
        }
24
    }
25

UNCOV
26
    pub fn set_plugin_paths(&mut self, plugin_paths: HashMap<PluginType, Vec<PathBuf>>) {
×
UNCOV
27
        self.plugin_paths = plugin_paths;
×
28
    }
29

30
    /// Determines the platform-specific plugin file extension
31
    fn get_plugin_extension() -> &'static str {
32
        if cfg!(target_os = "windows") {
33
            "dll"
34
        } else if cfg!(target_os = "macos") {
35
            "dylib"
36
        } else {
37
            "so"
38
        }
39
    }
40

41
    /// Attempts to retrieve the plugin type
UNCOV
42
    unsafe fn get_plugin_type(&self, lib: &Library) -> Result<u64> {
×
43
        let plugin_type_fn: Symbol<unsafe extern "C" fn() -> u64> = lib
×
44
            .get(b"plugin_type")
45
            .with_context(|| "Failed to retrieve `plugin_type` function")?;
×
46
        Ok(plugin_type_fn())
×
47
    }
48

49
    /// Checks whether a given file path corresponds to a valid plugin file
50
    fn is_valid_plugin_file(&self, path: &Path, plugin_extension: &str) -> bool {
×
UNCOV
51
        path.extension()
×
UNCOV
52
            .and_then(|ext| ext.to_str())
×
UNCOV
53
            .map_or(false, |ext| ext == plugin_extension)
×
54
    }
55

56
    /// Attempts to create a callback plugin
57
    unsafe fn create_callback_plugin(
×
58
        &self,
59
        lib: &Library,
60
        path: &Path,
61
    ) -> Result<Arc<dyn CallbackPlugin>> {
UNCOV
62
        let create_plugin_fn: Symbol<fn() -> Arc<dyn CallbackPlugin>> =
×
63
            lib.get(b"create_plugin").with_context(|| {
64
                format!("Missing `create_plugin` function in plugin at {:?}", path)
×
65
            })?;
66
        Ok(create_plugin_fn())
×
67
    }
68

69
    /// Loads an individual plugin from a file path.
70
    unsafe fn load_callback_plugin(&self, path: &Path) -> Result<Option<Arc<dyn CallbackPlugin>>> {
×
UNCOV
71
        let lib = Library::new(path).with_context(|| "Failed to load plugin")?;
×
72

73
        let plugin_type_value = self.get_plugin_type(&lib)?;
×
74
        match PluginType::from_u64(plugin_type_value) {
×
75
            Some(PluginType::Callback) => {
UNCOV
76
                let plugin = self.create_callback_plugin(&lib, path)?;
×
UNCOV
77
                Ok(Some(plugin))
×
78
            }
79
            _ => {
UNCOV
80
                warn!("Skipping non-callback plugin at {:?}", path);
×
UNCOV
81
                Ok(None)
×
82
            }
83
        }
84
    }
85

UNCOV
86
    pub async fn get_callback_plugins(&self) -> Result<Vec<Arc<dyn CallbackPlugin>>> {
×
87
        if let Some(cached) = self.get_cached_callback_plugins().await {
×
88
            return Ok(cached);
×
89
        }
90

91
        let mut plugins: Vec<Arc<dyn CallbackPlugin>> = Vec::new();
×
92
        let plugin_extension = Self::get_plugin_extension();
×
93

94
        if let Some(paths) = self.plugin_paths.get(&PluginType::Callback) {
×
UNCOV
95
            for path in paths {
×
UNCOV
96
                let entries = match fs::read_dir(path) {
×
UNCOV
97
                    Ok(entries) => entries,
×
UNCOV
98
                    Err(e) => {
×
99
                        warn!("Failed to read plugin directory {:?}: {}", path, e);
×
100
                        continue; // Skip this path
101
                    }
102
                };
103

UNCOV
104
                for entry in entries {
×
105
                    let path = entry
×
106
                        .with_context(|| format!("Failed to read directory entry in {:?}", path))?
×
107
                        .path();
UNCOV
108
                    if self.is_valid_plugin_file(&path, &plugin_extension) {
×
109
                        // Load the plugin and register it if valid
UNCOV
110
                        if let Some(plugin) = unsafe { self.load_callback_plugin(&path) }? {
×
UNCOV
111
                            plugins.push(plugin);
×
112
                        }
113
                    }
114
                }
115
            }
116
        } else {
UNCOV
117
            warn!("No callback plugin paths configured, no callback plugins will be loaded.");
×
118
            return Ok(plugins);
×
119
        }
120

UNCOV
121
        self.cache_callback_plugins(plugins.clone()).await;
×
122

UNCOV
123
        Ok(plugins)
×
124
    }
125

126
    /// Retrieves the cached plugins if available.
UNCOV
127
    async fn get_cached_callback_plugins(&self) -> Option<Vec<Arc<dyn CallbackPlugin>>> {
×
UNCOV
128
        let cache = self.cached_callback_plugins.lock().await;
×
UNCOV
129
        cache.as_ref().cloned()
×
130
    }
131

132
    /// Caches the provided plugins.
UNCOV
133
    async fn cache_callback_plugins(&self, plugins: Vec<Arc<dyn CallbackPlugin>>) {
×
134
        let mut cache = self.cached_callback_plugins.lock().await;
×
135
        *cache = Some(plugins);
×
136
    }
137
}
138

UNCOV
139
static PLUGIN_LOADER: Lazy<Mutex<PluginLoader>> = Lazy::new(|| Mutex::new(PluginLoader::new()));
×
140

141
impl PluginLoader {
142
    // Provide access to the singleton instance
UNCOV
143
    pub fn instance() -> &'static Mutex<PluginLoader> {
×
UNCOV
144
        &PLUGIN_LOADER
×
145
    }
146
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc