deserialize.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. use std::cell::{Cell, RefCell};
  2. use std::io::Cursor;
  3. use dioxus_core::CapturedError;
  4. use serde::de::DeserializeOwned;
  5. thread_local! {
  6. static SERVER_DATA: RefCell<Option<HTMLDataCursor>> = const { RefCell::new(None) };
  7. }
  8. /// Try to take the next item from the server data cursor. This will only be set during the first run of a component before hydration.
  9. /// This will return `None` if no data was pushed for this instance or if serialization fails
  10. // TODO: evan better docs
  11. #[track_caller]
  12. pub fn take_server_data<T: DeserializeOwned>() -> Result<Option<T>, TakeDataError> {
  13. SERVER_DATA.with_borrow(|data| match data.as_ref() {
  14. Some(data) => data.take(),
  15. None => Err(TakeDataError::DataNotAvailable),
  16. })
  17. }
  18. /// Run a closure with the server data
  19. pub(crate) fn with_server_data<O>(server_data: HTMLDataCursor, f: impl FnOnce() -> O) -> O {
  20. // Set the server data that will be used during hydration
  21. set_server_data(server_data);
  22. let out = f();
  23. // Hydrating the suspense node **should** eat all the server data, but just in case, remove it
  24. remove_server_data();
  25. out
  26. }
  27. fn set_server_data(data: HTMLDataCursor) {
  28. SERVER_DATA.with_borrow_mut(|server_data| *server_data = Some(data));
  29. }
  30. fn remove_server_data() {
  31. SERVER_DATA.with_borrow_mut(|server_data| server_data.take());
  32. }
  33. /// Data that is deserialized from the server during hydration
  34. pub(crate) struct HTMLDataCursor {
  35. error: Option<CapturedError>,
  36. data: Vec<Option<Vec<u8>>>,
  37. #[cfg(debug_assertions)]
  38. debug_types: Option<Vec<String>>,
  39. #[cfg(debug_assertions)]
  40. debug_locations: Option<Vec<String>>,
  41. index: Cell<usize>,
  42. }
  43. impl HTMLDataCursor {
  44. pub(crate) fn from_serialized(
  45. data: &[u8],
  46. debug_types: Option<Vec<String>>,
  47. debug_locations: Option<Vec<String>>,
  48. ) -> Self {
  49. let deserialized = ciborium::from_reader(Cursor::new(data)).unwrap();
  50. Self::new(deserialized, debug_types, debug_locations)
  51. }
  52. /// Get the error if there is one
  53. pub(crate) fn error(&self) -> Option<CapturedError> {
  54. self.error.clone()
  55. }
  56. fn new(
  57. data: Vec<Option<Vec<u8>>>,
  58. #[allow(unused)] debug_types: Option<Vec<String>>,
  59. #[allow(unused)] debug_locations: Option<Vec<String>>,
  60. ) -> Self {
  61. let mut myself = Self {
  62. index: Cell::new(0),
  63. error: None,
  64. data,
  65. #[cfg(debug_assertions)]
  66. debug_types,
  67. #[cfg(debug_assertions)]
  68. debug_locations,
  69. };
  70. // The first item is always an error if it exists
  71. let error = myself
  72. .take::<Option<CapturedError>>()
  73. .ok()
  74. .flatten()
  75. .flatten();
  76. myself.error = error;
  77. myself
  78. }
  79. #[track_caller]
  80. pub fn take<T: DeserializeOwned>(&self) -> Result<Option<T>, TakeDataError> {
  81. let current = self.index.get();
  82. if current >= self.data.len() {
  83. tracing::trace!(
  84. "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",
  85. self.data.len(),
  86. current
  87. );
  88. return Err(TakeDataError::DataNotAvailable);
  89. }
  90. let bytes = self.data[current].as_ref();
  91. self.index.set(current + 1);
  92. match bytes {
  93. Some(bytes) => match ciborium::from_reader(Cursor::new(bytes)) {
  94. Ok(x) => Ok(Some(x)),
  95. Err(err) => {
  96. #[cfg(debug_assertions)]
  97. {
  98. let debug_type = self
  99. .debug_types
  100. .as_ref()
  101. .and_then(|types| types.get(current));
  102. let debug_locations = self
  103. .debug_locations
  104. .as_ref()
  105. .and_then(|locations| locations.get(current));
  106. if let (Some(debug_type), Some(debug_locations)) =
  107. (debug_type, debug_locations)
  108. {
  109. let client_type = std::any::type_name::<T>();
  110. let client_location = std::panic::Location::caller();
  111. // We we have debug types and a location, we can provide a more helpful error message
  112. tracing::error!(
  113. "Error deserializing data: {err:?}\n\nThis type was serialized on the server at {debug_locations} with the type name {debug_type}. The client failed to deserialize the type {client_type} at {client_location}.",
  114. );
  115. return Err(TakeDataError::DeserializationError(err));
  116. }
  117. }
  118. // Otherwise, just log the generic deserialization error
  119. tracing::error!("Error deserializing data: {:?}", err);
  120. Err(TakeDataError::DeserializationError(err))
  121. }
  122. },
  123. None => Ok(None),
  124. }
  125. }
  126. }
  127. /// An error that can occur when trying to take data from the server
  128. #[derive(Debug)]
  129. pub enum TakeDataError {
  130. /// Deserializing the data failed
  131. DeserializationError(ciborium::de::Error<std::io::Error>),
  132. /// No data was available
  133. DataNotAvailable,
  134. }
  135. impl std::fmt::Display for TakeDataError {
  136. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  137. match self {
  138. Self::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
  139. Self::DataNotAvailable => write!(f, "DataNotAvailable"),
  140. }
  141. }
  142. }
  143. impl std::error::Error for TakeDataError {}