pgdog_plugin/
plugin.rs

1//! Plugin interface.
2
3use libloading::{library_filename, Library, Symbol};
4
5use crate::{PdRoute, PdRouterContext, PdStr};
6
7/// Plugin interface.
8#[derive(Debug)]
9pub struct Plugin<'a> {
10    name: String,
11    /// Initialization routine.
12    init: Option<Symbol<'a, unsafe extern "C" fn()>>,
13    /// Shutdown routine.
14    fini: Option<Symbol<'a, unsafe extern "C" fn()>>,
15    /// Route query.
16    route: Option<Symbol<'a, unsafe extern "C" fn(PdRouterContext, *mut PdRoute)>>,
17    /// Compiler version.
18    rustc_version: Option<Symbol<'a, unsafe extern "C" fn(*mut PdStr)>>,
19    /// PgQuery version.
20    pg_query_version: Option<Symbol<'a, unsafe extern "C" fn(*mut PdStr)>>,
21    /// Plugin version.
22    plugin_version: Option<Symbol<'a, unsafe extern "C" fn(*mut PdStr)>>,
23}
24
25impl<'a> Plugin<'a> {
26    /// Load library using a cross-platform naming convention.
27    pub fn library(name: &str) -> Result<Library, libloading::Error> {
28        let name = library_filename(name);
29        unsafe { Library::new(name) }
30    }
31
32    /// Load standard methods from the plugin library.
33    pub fn load(name: &str, library: &'a Library) -> Self {
34        let init = unsafe { library.get(b"pgdog_init\0") }.ok();
35        let fini = unsafe { library.get(b"pgdog_fini\0") }.ok();
36        let route = unsafe { library.get(b"pgdog_route\0") }.ok();
37        let rustc_version = unsafe { library.get(b"pgdog_rustc_version\0") }.ok();
38        let pg_query_version = unsafe { library.get(b"pgdog_pg_query_version\0") }.ok();
39        let plugin_version = unsafe { library.get(b"pgdog_plugin_version\0") }.ok();
40
41        Self {
42            name: name.to_owned(),
43            init,
44            fini,
45            route,
46            rustc_version,
47            pg_query_version,
48            plugin_version,
49        }
50    }
51
52    /// Perform initialization.
53    pub fn init(&self) -> bool {
54        if let Some(init) = &self.init {
55            unsafe {
56                init();
57            }
58            true
59        } else {
60            false
61        }
62    }
63
64    pub fn fini(&self) {
65        if let Some(ref fini) = &self.fini {
66            unsafe { fini() }
67        }
68    }
69
70    pub fn route(&self, context: PdRouterContext) -> Option<PdRoute> {
71        if let Some(ref route) = &self.route {
72            let mut output = PdRoute::default();
73            unsafe {
74                route(context, &mut output as *mut PdRoute);
75            }
76            Some(output)
77        } else {
78            None
79        }
80    }
81
82    /// Plugin name.
83    pub fn name(&self) -> &str {
84        &self.name
85    }
86
87    /// Get Rust compiler version used to build the plugin.
88    pub fn rustc_version(&self) -> Option<PdStr> {
89        let mut output = PdStr::default();
90        self.rustc_version.as_ref().map(|rustc_version| unsafe {
91            rustc_version(&mut output);
92            output
93        })
94    }
95
96    /// Get plugin version, if any.
97    pub fn version(&self) -> Option<PdStr> {
98        let mut output = PdStr::default();
99        self.plugin_version.as_ref().map(|func| unsafe {
100            func(&mut output as *mut PdStr);
101            output
102        })
103    }
104
105    pub fn pg_query_version(&self) -> Option<PdStr> {
106        let mut output = PdStr::default();
107
108        self.pg_query_version.as_ref().map(|func| unsafe {
109            func(&mut output as *mut PdStr);
110            output
111        })
112    }
113}