use crate::observation::Value;
pub trait Bridge: Send + Sync {
fn call(&self, api: &str, args: &[Value]) -> Result<Value, String>;
fn get_property(&self, object: &str, property: &str) -> Result<Value, String>;
fn set_property(&self, object: &str, property: &str, value: &Value) -> Result<(), String>;
fn provided_globals(&self) -> Vec<String>;
fn bootstrap_js(&self) -> String;
}
pub struct EmptyBridge;
impl Bridge for EmptyBridge {
fn call(&self, api: &str, _args: &[Value]) -> Result<Value, String> {
Err(format!("{api} is not defined"))
}
fn get_property(&self, object: &str, property: &str) -> Result<Value, String> {
Err(format!("{object}.{property} is not defined"))
}
fn set_property(&self, _object: &str, _property: &str, _value: &Value) -> Result<(), String> {
Ok(()) }
fn provided_globals(&self) -> Vec<String> {
Vec::new()
}
fn bootstrap_js(&self) -> String {
String::new()
}
}
pub struct CompositeBridge {
bridges: Vec<Box<dyn Bridge>>,
}
impl CompositeBridge {
#[must_use]
pub fn new(bridges: Vec<Box<dyn Bridge>>) -> Self {
Self { bridges }
}
}
impl Bridge for CompositeBridge {
fn call(&self, api: &str, args: &[Value]) -> Result<Value, String> {
for bridge in &self.bridges {
match bridge.call(api, args) {
Ok(value) => return Ok(value),
Err(e) if e.ends_with("is not defined") => {}
Err(e) => return Err(e),
}
}
Err(format!("{api} is not defined"))
}
fn get_property(&self, object: &str, property: &str) -> Result<Value, String> {
for bridge in &self.bridges {
match bridge.get_property(object, property) {
Ok(value) => return Ok(value),
Err(e) if e.ends_with("is not defined") => {}
Err(e) => return Err(e),
}
}
Err(format!("{object}.{property} is not defined"))
}
fn set_property(&self, object: &str, property: &str, value: &Value) -> Result<(), String> {
for bridge in &self.bridges {
match bridge.set_property(object, property, value) {
Ok(()) => return Ok(()),
Err(e) if e.ends_with("is not defined") => {}
Err(e) => return Err(e),
}
}
Ok(())
}
fn provided_globals(&self) -> Vec<String> {
self.bridges
.iter()
.flat_map(|b| b.provided_globals())
.collect()
}
fn bootstrap_js(&self) -> String {
self.bridges
.iter()
.map(|b| b.bootstrap_js())
.collect::<Vec<_>>()
.join("\n")
}
}
pub trait Hook: Send + Sync {
fn before_call(&self, _api: &str, _args: &[Value]) -> Option<Result<Value, String>> {
None
}
fn after_call(
&self,
_api: &str,
_args: &[Value],
result: Result<Value, String>,
) -> Result<Value, String> {
result
}
}
pub struct HookedBridge {
inner: Box<dyn Bridge>,
hooks: Vec<Box<dyn Hook>>,
}
impl HookedBridge {
#[must_use]
pub fn new(inner: Box<dyn Bridge>, hooks: Vec<Box<dyn Hook>>) -> Self {
Self { inner, hooks }
}
}
impl Bridge for HookedBridge {
fn call(&self, api: &str, args: &[Value]) -> Result<Value, String> {
for hook in &self.hooks {
if let Some(result) = hook.before_call(api, args) {
return result;
}
}
let mut result = self.inner.call(api, args);
for hook in &self.hooks {
result = hook.after_call(api, args, result);
}
result
}
fn get_property(&self, object: &str, property: &str) -> Result<Value, String> {
self.inner.get_property(object, property)
}
fn set_property(&self, object: &str, property: &str, value: &Value) -> Result<(), String> {
self.inner.set_property(object, property, value)
}
fn provided_globals(&self) -> Vec<String> {
self.inner.provided_globals()
}
fn bootstrap_js(&self) -> String {
self.inner.bootstrap_js()
}
}