godwit-daemon 0.1.10

A daemon runner for GodWit.
Documentation
use crate::errors::{BackendError, TraceError};
use glob;
use libloading::Library;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::Path;
use std::rc::Rc;

#[macro_export]
macro_rules! export_backend {
	($register:expr) => {
		#[doc(hidden)]
		#[no_mangle]
		pub static backend_declaration: $crate::core::BackendDeclaration =
			$crate::core::BackendDeclaration {
				rustc_version: $crate::RUSTC_VERSION,
				core_version: $crate::CORE_VERSION,
				register: $register,
			};
	};
}

#[derive(Debug, Serialize, Deserialize, Copy, PartialEq, Clone)]
pub enum Ops {
	Trace,
}

pub trait Backend {
	fn trace(&self, args: BackendArgs) -> Result<(), TraceError>;
	fn help(&self) -> Option<&str> {
		None
	}
}

pub trait Registrar {
	fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>);
}

pub struct BackendDeclaration {
	pub rustc_version: &'static str,
	pub core_version: &'static str,
	pub register: unsafe extern "C" fn(&mut dyn Registrar),
}

pub struct BackendArgs {
	pub refresh: bool,
}

pub struct BackendProxy {
	backend: Box<dyn Backend>,
	_lib: Rc<Library>,
}

impl Backend for BackendProxy {
	fn trace(&self, args: BackendArgs) -> Result<(), TraceError> {
		self.backend.trace(args)
	}

	fn help(&self) -> Option<&str> {
		self.backend.help()
	}
}

struct BackendRegistrar {
	backends: HashMap<String, BackendProxy>,
	lib: Rc<Library>,
}

impl BackendRegistrar {
	fn new(lib: Rc<Library>) -> BackendRegistrar {
		BackendRegistrar {
			backends: HashMap::default(),
			lib,
		}
	}
}

impl Registrar for BackendRegistrar {
	fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>) {
		let proxy = BackendProxy {
			backend,
			_lib: Rc::clone(&self.lib),
		};
		self.backends.insert(name.to_string().to_lowercase(), proxy);
	}
}

pub struct ExternalBackends {
	pub backends: HashMap<String, BackendProxy>,
	libraries: Vec<Rc<Library>>,
}

impl ExternalBackends {
	pub fn new() -> ExternalBackends {
		ExternalBackends {
			backends: Default::default(),
			libraries: Default::default(),
		}
	}

	pub fn from_dir<P>(dir_path: P) -> Result<ExternalBackends, BackendError>
	where
		P: AsRef<Path>,
	{
		let mut ext_backends = ExternalBackends::new();

		for lib_so in glob::glob(&format!("{}/*.so", dir_path.as_ref().display())).unwrap() {
			unsafe {
				ext_backends.load(lib_so?)?;
			}
		}
		Ok(ext_backends)
	}

	pub unsafe fn load<P: AsRef<OsStr>>(&mut self, lib_path: P) -> Result<(), BackendError> {
		let library = Rc::new(Library::new(lib_path)?);

		let decl = library
			.get::<*mut BackendDeclaration>(b"backend_declaration\0")?
			.read();

		if decl.rustc_version != crate::RUSTC_VERSION || decl.core_version != crate::CORE_VERSION {
			return Err(BackendError::VersionMismatch);
		}

		let mut registrar = BackendRegistrar::new(Rc::clone(&library));

		(decl.register)(&mut registrar);

		self.backends.extend(registrar.backends);
		self.libraries.push(library);

		Ok(())
	}

	pub fn trace(&self, backend: &str, args: BackendArgs) -> Result<(), TraceError> {
		self.backends
			.get(backend)
			.ok_or_else(|| BackendError::NotFound {
				backend_str: backend.to_string(),
			})?
			.trace(args)
			.into()
	}
}