godwit_daemon/core/
mod.rs

1use crate::errors::{BackendError, TraceError};
2use glob;
3use libloading::Library;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::ffi::OsStr;
7use std::path::Path;
8use std::rc::Rc;
9
10#[macro_export]
11macro_rules! export_backend {
12	($register:expr) => {
13		#[doc(hidden)]
14		#[no_mangle]
15		pub static backend_declaration: $crate::core::BackendDeclaration =
16			$crate::core::BackendDeclaration {
17				rustc_version: $crate::RUSTC_VERSION,
18				core_version: $crate::CORE_VERSION,
19				register: $register,
20			};
21	};
22}
23
24#[derive(Debug, Serialize, Deserialize, Copy, PartialEq, Clone)]
25pub enum Ops {
26	Trace,
27}
28
29pub trait Backend {
30	fn trace(&self, args: BackendArgs) -> Result<(), TraceError>;
31	fn help(&self) -> Option<&str> {
32		None
33	}
34}
35
36pub trait Registrar {
37	fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>);
38}
39
40pub struct BackendDeclaration {
41	pub rustc_version: &'static str,
42	pub core_version: &'static str,
43	pub register: unsafe extern "C" fn(&mut dyn Registrar),
44}
45
46pub struct BackendArgs {
47	pub refresh: bool,
48}
49
50pub struct BackendProxy {
51	backend: Box<dyn Backend>,
52	_lib: Rc<Library>,
53}
54
55impl Backend for BackendProxy {
56	fn trace(&self, args: BackendArgs) -> Result<(), TraceError> {
57		self.backend.trace(args)
58	}
59
60	fn help(&self) -> Option<&str> {
61		self.backend.help()
62	}
63}
64
65struct BackendRegistrar {
66	backends: HashMap<String, BackendProxy>,
67	lib: Rc<Library>,
68}
69
70impl BackendRegistrar {
71	fn new(lib: Rc<Library>) -> BackendRegistrar {
72		BackendRegistrar {
73			backends: HashMap::default(),
74			lib,
75		}
76	}
77}
78
79impl Registrar for BackendRegistrar {
80	fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>) {
81		let proxy = BackendProxy {
82			backend,
83			_lib: Rc::clone(&self.lib),
84		};
85		self.backends.insert(name.to_string().to_lowercase(), proxy);
86	}
87}
88
89pub struct ExternalBackends {
90	pub backends: HashMap<String, BackendProxy>,
91	libraries: Vec<Rc<Library>>,
92}
93
94impl ExternalBackends {
95	pub fn new() -> ExternalBackends {
96		ExternalBackends {
97			backends: Default::default(),
98			libraries: Default::default(),
99		}
100	}
101
102	pub fn from_dir<P>(dir_path: P) -> Result<ExternalBackends, BackendError>
103	where
104		P: AsRef<Path>,
105	{
106		let mut ext_backends = ExternalBackends::new();
107
108		for lib_so in glob::glob(&format!("{}/*.so", dir_path.as_ref().display())).unwrap() {
109			unsafe {
110				ext_backends.load(lib_so?)?;
111			}
112		}
113		Ok(ext_backends)
114	}
115
116	pub unsafe fn load<P: AsRef<OsStr>>(&mut self, lib_path: P) -> Result<(), BackendError> {
117		let library = Rc::new(Library::new(lib_path)?);
118
119		let decl = library
120			.get::<*mut BackendDeclaration>(b"backend_declaration\0")?
121			.read();
122
123		if decl.rustc_version != crate::RUSTC_VERSION || decl.core_version != crate::CORE_VERSION {
124			return Err(BackendError::VersionMismatch);
125		}
126
127		let mut registrar = BackendRegistrar::new(Rc::clone(&library));
128
129		(decl.register)(&mut registrar);
130
131		self.backends.extend(registrar.backends);
132		self.libraries.push(library);
133
134		Ok(())
135	}
136
137	pub fn trace(&self, backend: &str, args: BackendArgs) -> Result<(), TraceError> {
138		self.backends
139			.get(backend)
140			.ok_or_else(|| BackendError::NotFound {
141				backend_str: backend.to_string(),
142			})?
143			.trace(args)
144			.into()
145	}
146}