use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, OnceLock, RwLock};
static NATIVE_BRIDGE: OnceLock<NativeBridgeState> = OnceLock::new();
pub type NativeResult<T> = Result<T, NativeBridgeError>;
pub type NativeHandler = Arc<dyn Fn(Vec<NativeValue>) -> NativeResult<NativeValue> + Send + Sync>;
#[derive(Debug, Clone)]
pub enum NativeBridgeError {
NotRegistered { namespace: String, name: String },
TypeMismatch {
expected: &'static str,
actual: String,
},
PlatformError(String),
SerializationError(String),
NotInitialized,
}
impl fmt::Display for NativeBridgeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotRegistered { namespace, name } => {
write!(f, "Native function '{}.{}' not registered", namespace, name)
}
Self::TypeMismatch { expected, actual } => {
write!(f, "Type mismatch: expected {}, got {}", expected, actual)
}
Self::PlatformError(msg) => write!(f, "Platform error: {}", msg),
Self::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
Self::NotInitialized => write!(f, "Native bridge not initialized"),
}
}
}
impl std::error::Error for NativeBridgeError {}
#[derive(Debug, Clone, PartialEq)]
pub enum NativeValue {
Void,
Bool(bool),
Int32(i32),
Int64(i64),
Float32(f32),
Float64(f64),
String(String),
Bytes(Vec<u8>),
Json(String),
}
impl NativeValue {
pub fn as_bool(&self) -> Option<bool> {
match self {
NativeValue::Bool(v) => Some(*v),
_ => None,
}
}
pub fn as_i32(&self) -> Option<i32> {
match self {
NativeValue::Int32(v) => Some(*v),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
NativeValue::Int64(v) => Some(*v),
NativeValue::Int32(v) => Some(*v as i64),
_ => None,
}
}
pub fn as_f32(&self) -> Option<f32> {
match self {
NativeValue::Float32(v) => Some(*v),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
NativeValue::Float64(v) => Some(*v),
NativeValue::Float32(v) => Some(*v as f64),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
NativeValue::String(v) => Some(v),
_ => None,
}
}
pub fn into_string(self) -> Option<String> {
match self {
NativeValue::String(v) => Some(v),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
NativeValue::Bytes(v) => Some(v),
_ => None,
}
}
pub fn as_json(&self) -> Option<&str> {
match self {
NativeValue::Json(v) => Some(v),
_ => None,
}
}
pub fn type_name(&self) -> &'static str {
match self {
NativeValue::Void => "Void",
NativeValue::Bool(_) => "Bool",
NativeValue::Int32(_) => "Int32",
NativeValue::Int64(_) => "Int64",
NativeValue::Float32(_) => "Float32",
NativeValue::Float64(_) => "Float64",
NativeValue::String(_) => "String",
NativeValue::Bytes(_) => "Bytes",
NativeValue::Json(_) => "Json",
}
}
}
pub trait IntoNativeArgs {
fn into_native_args(self) -> Vec<NativeValue>;
}
impl IntoNativeArgs for () {
fn into_native_args(self) -> Vec<NativeValue> {
vec![]
}
}
impl IntoNativeArgs for (i32,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Int32(self.0)]
}
}
impl IntoNativeArgs for (i64,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Int64(self.0)]
}
}
impl IntoNativeArgs for (f32,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Float32(self.0)]
}
}
impl IntoNativeArgs for (f64,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Float64(self.0)]
}
}
impl IntoNativeArgs for (bool,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Bool(self.0)]
}
}
impl IntoNativeArgs for (String,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::String(self.0)]
}
}
impl IntoNativeArgs for (&str,) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::String(self.0.to_string())]
}
}
impl IntoNativeArgs for (i32, i32) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::Int32(self.0), NativeValue::Int32(self.1)]
}
}
impl IntoNativeArgs for (String, String) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![NativeValue::String(self.0), NativeValue::String(self.1)]
}
}
impl IntoNativeArgs for (&str, &str) {
fn into_native_args(self) -> Vec<NativeValue> {
vec![
NativeValue::String(self.0.to_string()),
NativeValue::String(self.1.to_string()),
]
}
}
impl IntoNativeArgs for Vec<NativeValue> {
fn into_native_args(self) -> Vec<NativeValue> {
self
}
}
pub trait FromNativeValue: Sized {
fn from_native_value(value: NativeValue) -> NativeResult<Self>;
}
impl FromNativeValue for () {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
match value {
NativeValue::Void => Ok(()),
_ => Ok(()), }
}
}
impl FromNativeValue for bool {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.as_bool()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "Bool",
actual: value.type_name().to_string(),
})
}
}
impl FromNativeValue for i32 {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.as_i32()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "Int32",
actual: value.type_name().to_string(),
})
}
}
impl FromNativeValue for i64 {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.as_i64()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "Int64",
actual: value.type_name().to_string(),
})
}
}
impl FromNativeValue for f32 {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.as_f32()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "Float32",
actual: value.type_name().to_string(),
})
}
}
impl FromNativeValue for f64 {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.as_f64()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "Float64",
actual: value.type_name().to_string(),
})
}
}
impl FromNativeValue for String {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
value
.into_string()
.ok_or_else(|| NativeBridgeError::TypeMismatch {
expected: "String",
actual: "non-string".to_string(),
})
}
}
impl FromNativeValue for Vec<u8> {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
match value {
NativeValue::Bytes(v) => Ok(v),
_ => Err(NativeBridgeError::TypeMismatch {
expected: "Bytes",
actual: value.type_name().to_string(),
}),
}
}
}
impl FromNativeValue for NativeValue {
fn from_native_value(value: NativeValue) -> NativeResult<Self> {
Ok(value)
}
}
pub trait PlatformAdapter: Send + Sync {
fn call(
&self,
namespace: &str,
name: &str,
args: Vec<NativeValue>,
) -> NativeResult<NativeValue>;
}
pub struct NativeBridgeState {
handlers: RwLock<HashMap<String, HashMap<String, NativeHandler>>>,
platform_adapter: RwLock<Option<Arc<dyn PlatformAdapter>>>,
}
impl NativeBridgeState {
pub fn init() {
let state = NativeBridgeState {
handlers: RwLock::new(HashMap::new()),
platform_adapter: RwLock::new(None),
};
if NATIVE_BRIDGE.set(state).is_err() {
tracing::debug!("NativeBridgeState already initialized");
}
}
pub fn get() -> &'static NativeBridgeState {
NATIVE_BRIDGE.get().expect(
"NativeBridgeState not initialized. Call NativeBridgeState::init() at app startup.",
)
}
pub fn try_get() -> Option<&'static NativeBridgeState> {
NATIVE_BRIDGE.get()
}
pub fn is_initialized() -> bool {
NATIVE_BRIDGE.get().is_some()
}
pub fn set_platform_adapter(&self, adapter: Arc<dyn PlatformAdapter>) {
*self.platform_adapter.write().unwrap() = Some(adapter);
}
pub fn clear_platform_adapter(&self) {
*self.platform_adapter.write().unwrap() = None;
}
pub fn register<F>(&self, namespace: &str, name: &str, handler: F)
where
F: Fn(Vec<NativeValue>) -> NativeResult<NativeValue> + Send + Sync + 'static,
{
let mut handlers = self.handlers.write().unwrap();
let ns_handlers = handlers.entry(namespace.to_string()).or_default();
ns_handlers.insert(name.to_string(), Arc::new(handler));
}
pub fn unregister(&self, namespace: &str, name: &str) -> bool {
let mut handlers = self.handlers.write().unwrap();
if let Some(ns_handlers) = handlers.get_mut(namespace) {
return ns_handlers.remove(name).is_some();
}
false
}
pub fn call<R, A>(&self, namespace: &str, name: &str, args: A) -> NativeResult<R>
where
R: FromNativeValue,
A: IntoNativeArgs,
{
let native_args = args.into_native_args();
{
let handlers = self.handlers.read().unwrap();
if let Some(ns_handlers) = handlers.get(namespace) {
if let Some(handler) = ns_handlers.get(name) {
let result = handler(native_args.clone())?;
return R::from_native_value(result);
}
}
}
if let Some(adapter) = self.platform_adapter.read().unwrap().as_ref() {
let result = adapter.call(namespace, name, native_args)?;
return R::from_native_value(result);
}
Err(NativeBridgeError::NotRegistered {
namespace: namespace.to_string(),
name: name.to_string(),
})
}
pub fn has_handler(&self, namespace: &str, name: &str) -> bool {
let handlers = self.handlers.read().unwrap();
if let Some(ns_handlers) = handlers.get(namespace) {
if ns_handlers.contains_key(name) {
return true;
}
}
self.platform_adapter.read().unwrap().is_some()
}
pub fn namespaces(&self) -> Vec<String> {
self.handlers.read().unwrap().keys().cloned().collect()
}
pub fn functions(&self, namespace: &str) -> Vec<String> {
self.handlers
.read()
.unwrap()
.get(namespace)
.map(|ns| ns.keys().cloned().collect())
.unwrap_or_default()
}
}
pub fn native_call<R, A>(namespace: &str, name: &str, args: A) -> NativeResult<R>
where
R: FromNativeValue,
A: IntoNativeArgs,
{
NativeBridgeState::get().call(namespace, name, args)
}
pub fn native_register<F>(namespace: &str, name: &str, handler: F)
where
F: Fn(Vec<NativeValue>) -> NativeResult<NativeValue> + Send + Sync + 'static,
{
NativeBridgeState::get().register(namespace, name, handler)
}
pub fn set_platform_adapter(adapter: Arc<dyn PlatformAdapter>) {
NativeBridgeState::get().set_platform_adapter(adapter)
}
pub fn parse_native_result_json(json: &str) -> NativeResult<NativeValue> {
if json.contains("\"success\":true") || json.contains("\"success\": true") {
if let Some(value_start) = json.find("\"value\":") {
let value_part = &json[value_start + 8..];
let value_str = value_part.trim();
if value_str.starts_with("null") || value_str.starts_with("\"null\"") {
return Ok(NativeValue::Void);
} else if value_str.starts_with("true") {
return Ok(NativeValue::Bool(true));
} else if value_str.starts_with("false") {
return Ok(NativeValue::Bool(false));
} else if let Some(stripped) = value_str.strip_prefix('"') {
if let Some(end) = stripped.find('"') {
let s = &stripped[..end];
return Ok(NativeValue::String(s.to_string()));
}
} else if let Ok(n) = value_str
.chars()
.take_while(|c| c.is_ascii_digit() || *c == '-' || *c == '.')
.collect::<String>()
.parse::<i64>()
{
if n >= i32::MIN as i64 && n <= i32::MAX as i64 {
return Ok(NativeValue::Int32(n as i32));
} else {
return Ok(NativeValue::Int64(n));
}
}
}
Ok(NativeValue::Void)
} else {
let error_type = extract_json_string(json, "errorType").unwrap_or("Unknown");
let error_msg = extract_json_string(json, "errorMessage").unwrap_or("Unknown error");
match error_type {
"NotRegistered" => Err(NativeBridgeError::NotRegistered {
namespace: "unknown".to_string(),
name: "unknown".to_string(),
}),
_ => Err(NativeBridgeError::PlatformError(error_msg.to_string())),
}
}
}
fn extract_json_string<'a>(json: &'a str, key: &str) -> Option<&'a str> {
let search = format!("\"{}\":\"", key);
if let Some(start) = json.find(&search) {
let value_start = start + search.len();
if let Some(end) = json[value_start..].find('"') {
return Some(&json[value_start..value_start + end]);
}
}
None
}
pub struct NativeStream {
namespace: String,
name: String,
_callback_id: u64,
}
impl Drop for NativeStream {
fn drop(&mut self) {
remove_stream_callback(self._callback_id);
let _ = native_call::<(), _>(&self.namespace, &format!("{}_stop", self.name), ());
}
}
pub type StreamCallback = Arc<dyn Fn(NativeValue) + Send + Sync>;
static STREAM_CALLBACKS: std::sync::Mutex<Option<HashMap<u64, StreamCallback>>> =
std::sync::Mutex::new(None);
static NEXT_STREAM_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
pub fn native_stream<F>(
namespace: &str,
name: &str,
args: Vec<NativeValue>,
callback: F,
) -> NativeStream
where
F: Fn(NativeValue) + Send + Sync + 'static,
{
let id = NEXT_STREAM_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
{
let mut guard = STREAM_CALLBACKS.lock().unwrap();
let map = guard.get_or_insert_with(HashMap::new);
map.insert(id, Arc::new(callback));
}
let mut start_args = args;
start_args.push(NativeValue::Int64(id as i64));
let _ = native_call::<(), _>(namespace, &format!("{}_start", name), start_args);
NativeStream {
namespace: namespace.to_string(),
name: name.to_string(),
_callback_id: id,
}
}
pub fn dispatch_stream_data(stream_id: u64, data: NativeValue) {
let callback = {
let guard = STREAM_CALLBACKS.lock().unwrap();
guard.as_ref().and_then(|map| map.get(&stream_id).cloned())
};
if let Some(cb) = callback {
cb(data);
}
}
fn remove_stream_callback(id: u64) {
let mut guard = STREAM_CALLBACKS.lock().unwrap();
if let Some(map) = guard.as_mut() {
map.remove(&id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_native_value_accessors() {
assert_eq!(NativeValue::Bool(true).as_bool(), Some(true));
assert_eq!(NativeValue::Int32(42).as_i32(), Some(42));
assert_eq!(NativeValue::Int64(100).as_i64(), Some(100));
assert_eq!(NativeValue::Float32(3.25).as_f32(), Some(3.25));
assert_eq!(
NativeValue::String("hello".to_string()).as_str(),
Some("hello")
);
}
#[test]
fn test_native_value_type_mismatch() {
assert_eq!(NativeValue::Int32(42).as_bool(), None);
assert_eq!(NativeValue::Bool(true).as_i32(), None);
}
#[test]
fn test_into_native_args() {
let args: Vec<NativeValue> = ().into_native_args();
assert!(args.is_empty());
let args: Vec<NativeValue> = (42i32,).into_native_args();
assert_eq!(args.len(), 1);
assert_eq!(args[0].as_i32(), Some(42));
let args: Vec<NativeValue> = (1i32, 2i32).into_native_args();
assert_eq!(args.len(), 2);
}
#[test]
fn test_from_native_value() {
assert_eq!(i32::from_native_value(NativeValue::Int32(42)).unwrap(), 42);
assert_eq!(
String::from_native_value(NativeValue::String("test".to_string())).unwrap(),
"test"
);
assert!(bool::from_native_value(NativeValue::Int32(42)).is_err());
}
#[test]
fn test_parse_native_result_json() {
let success = r#"{"success":true,"value":"hello"}"#;
let result = parse_native_result_json(success).unwrap();
assert_eq!(result.as_str(), Some("hello"));
let error = r#"{"success":false,"errorType":"NotRegistered","errorMessage":"not found"}"#;
let result = parse_native_result_json(error);
assert!(result.is_err());
}
}