use std::cell::{Cell, RefCell};
use std::io::Cursor;
use dioxus_core::CapturedError;
use serde::de::DeserializeOwned;
thread_local! {
static SERVER_DATA: RefCell<Option<HTMLDataCursor>> = const { RefCell::new(None) };
}
pub fn take_server_data<T: DeserializeOwned>() -> Result<Option<T>, TakeDataError> {
SERVER_DATA.with_borrow(|data| match data.as_ref() {
Some(data) => data.take(),
None => Err(TakeDataError::DataNotAvailable),
})
}
pub(crate) fn with_server_data<O>(server_data: HTMLDataCursor, f: impl FnOnce() -> O) -> O {
set_server_data(server_data);
let out = f();
remove_server_data();
out
}
fn set_server_data(data: HTMLDataCursor) {
SERVER_DATA.with_borrow_mut(|server_data| *server_data = Some(data));
}
fn remove_server_data() {
SERVER_DATA.with_borrow_mut(|server_data| server_data.take());
}
pub(crate) struct HTMLDataCursor {
error: Option<CapturedError>,
data: Vec<Option<Vec<u8>>>,
index: Cell<usize>,
}
impl HTMLDataCursor {
pub(crate) fn from_serialized(data: &[u8]) -> Self {
let deserialized = ciborium::from_reader(Cursor::new(data)).unwrap();
Self::new(deserialized)
}
pub(crate) fn error(&self) -> Option<CapturedError> {
self.error.clone()
}
fn new(data: Vec<Option<Vec<u8>>>) -> Self {
let mut myself = Self {
error: None,
data,
index: Cell::new(0),
};
let error = myself
.take::<Option<CapturedError>>()
.ok()
.flatten()
.flatten();
myself.error = error;
myself
}
pub fn take<T: DeserializeOwned>(&self) -> Result<Option<T>, TakeDataError> {
let current = self.index.get();
if current >= self.data.len() {
tracing::trace!(
"Tried to take more data than was available, len: {}, index: {}; This is normal if the server function was started on the client, but may indicate a bug if the server function result should be deserialized from the server",
self.data.len(),
current
);
return Err(TakeDataError::DataNotAvailable);
}
let bytes = self.data[current].as_ref();
self.index.set(current + 1);
match bytes {
Some(bytes) => match ciborium::from_reader(Cursor::new(bytes)) {
Ok(x) => Ok(Some(x)),
Err(e) => {
tracing::error!("Error deserializing data: {:?}", e);
Err(TakeDataError::DeserializationError(e))
}
},
None => Ok(None),
}
}
}
#[derive(Debug)]
pub enum TakeDataError {
DeserializationError(ciborium::de::Error<std::io::Error>),
DataNotAvailable,
}
impl std::fmt::Display for TakeDataError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
Self::DataNotAvailable => write!(f, "DataNotAvailable"),
}
}
}
impl std::error::Error for TakeDataError {}