lib.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. use captuered_context::CapturedContext;
  2. use dioxus_core::{NodeFactory, SchedulerMsg, VNode};
  3. use dioxus_hooks::UnboundedSender;
  4. use error::{Error, ParseError};
  5. use interperter::build;
  6. use lazy_static::lazy_static;
  7. use serde::{Deserialize, Serialize};
  8. use std::collections::HashMap;
  9. use std::sync::{RwLock, RwLockReadGuard};
  10. use syn::parse_str;
  11. mod attributes;
  12. pub mod captuered_context;
  13. mod elements;
  14. pub mod error;
  15. mod interperter;
  16. lazy_static! {
  17. /// This a a global store of the current rsx text for each call to rsx
  18. // Global mutable data is genrally not great, but it allows users to not worry about passing down the text RsxContex every time they switch to hot reloading.
  19. pub static ref RSX_CONTEXT: RsxContext = RsxContext::new(RsxData::default());
  20. }
  21. // the location of the code relative to the current crate based on [std::panic::Location]
  22. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  23. pub struct CodeLocation {
  24. pub crate_path: String,
  25. pub file_path: String,
  26. pub line: u32,
  27. pub column: u32,
  28. }
  29. /// Get the resolved rsx given the origional rsx, a captured context of dynamic components, and a factory to build the resulting node
  30. #[cfg_attr(
  31. not(debug_assertions),
  32. deprecated(
  33. note = "The hot reload feature is enabled in release mode. This feature should be disabled for production builds."
  34. )
  35. )]
  36. pub fn resolve_scope<'a>(
  37. location: CodeLocation,
  38. rsx: &'static str,
  39. captured: CapturedContext<'a>,
  40. factory: NodeFactory<'a>,
  41. ) -> VNode<'a> {
  42. let rsx_text_index = &*RSX_CONTEXT;
  43. // only the insert the rsx text once
  44. if !rsx_text_index.read().hm.contains_key(&location) {
  45. rsx_text_index.insert(location.clone(), rsx.to_string());
  46. }
  47. if let Some(text) = {
  48. let read = rsx_text_index.read();
  49. // clone prevents deadlock on nested rsx calls
  50. read.hm.get(&location).cloned()
  51. } {
  52. match interpert_rsx(factory, &text, captured) {
  53. Ok(vnode) => vnode,
  54. Err(err) => {
  55. rsx_text_index.report_error(err);
  56. factory.text(format_args!(""))
  57. }
  58. }
  59. } else {
  60. panic!("rsx: line number {:?} not found in rsx index", location);
  61. }
  62. }
  63. fn interpert_rsx<'a>(
  64. factory: dioxus_core::NodeFactory<'a>,
  65. text: &str,
  66. context: captuered_context::CapturedContext<'a>,
  67. ) -> Result<VNode<'a>, Error> {
  68. build(
  69. parse_str(text)
  70. .map_err(|err| Error::ParseError(ParseError::new(err, context.location.clone())))?,
  71. context,
  72. &factory,
  73. )
  74. }
  75. /// get the code location of the code that called this function
  76. #[macro_export]
  77. macro_rules! get_line_num {
  78. () => {{
  79. let line = line!();
  80. let column = column!();
  81. let file_path = file!().to_string();
  82. let crate_path = env!("CARGO_MANIFEST_DIR").to_string();
  83. CodeLocation {
  84. crate_path,
  85. file_path,
  86. line: line,
  87. column: column,
  88. }
  89. }};
  90. }
  91. /// A handle to the rsx context with interior mutability
  92. #[derive(Debug)]
  93. pub struct RsxContext {
  94. data: RwLock<RsxData>,
  95. }
  96. /// A store of the text for the rsx macro for each call to rsx
  97. #[derive(Default)]
  98. pub struct RsxData {
  99. pub hm: HashMap<CodeLocation, String>,
  100. pub error_handler: Option<Box<dyn ErrorHandler>>,
  101. pub scheduler_channel: Option<UnboundedSender<SchedulerMsg>>,
  102. }
  103. impl std::fmt::Debug for RsxData {
  104. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  105. f.debug_struct("RsxData").field("hm", &self.hm).finish()
  106. }
  107. }
  108. impl RsxContext {
  109. pub fn new(data: RsxData) -> Self {
  110. Self {
  111. data: RwLock::new(data),
  112. }
  113. }
  114. /// Set the text for an rsx call at some location
  115. pub fn insert(&self, loc: CodeLocation, text: String) {
  116. let mut write = self.data.write().unwrap();
  117. write.hm.insert(loc, text);
  118. if let Some(channel) = &mut write.scheduler_channel {
  119. channel.unbounded_send(SchedulerMsg::DirtyAll).unwrap()
  120. }
  121. }
  122. /// Set the text for many rsx calls
  123. pub fn extend(&self, msg: SetManyRsxMessage) {
  124. let mut write = self.data.write().unwrap();
  125. for rsx in msg.0 {
  126. write.hm.insert(rsx.location, rsx.new_text);
  127. }
  128. if let Some(channel) = &mut write.scheduler_channel {
  129. channel.unbounded_send(SchedulerMsg::DirtyAll).unwrap()
  130. }
  131. }
  132. fn read(&self) -> RwLockReadGuard<RsxData> {
  133. self.data.read().unwrap()
  134. }
  135. fn report_error(&self, error: Error) {
  136. if let Some(handler) = &self.data.write().unwrap().error_handler {
  137. handler.handle_error(error)
  138. } else {
  139. panic!("no error handler set for this platform...\n{}", error);
  140. }
  141. }
  142. /// Set the handler for errors interperting the rsx
  143. pub fn set_error_handler(&self, handler: impl ErrorHandler + 'static) {
  144. self.data.write().unwrap().error_handler = Some(Box::new(handler));
  145. }
  146. /// Provide the scduler channel from [dioxus_code::VirtualDom::get_scheduler_channel].
  147. /// The channel allows the interpreter to force re-rendering of the dom when the rsx is changed.
  148. pub fn provide_scheduler_channel(&self, channel: UnboundedSender<SchedulerMsg>) {
  149. self.data.write().unwrap().scheduler_channel = Some(channel)
  150. }
  151. }
  152. /// A error handler for errors reported by the rsx interperter
  153. pub trait ErrorHandler: Send + Sync {
  154. fn handle_error(&self, err: Error);
  155. }
  156. #[derive(Serialize, Deserialize, Clone, Debug)]
  157. pub struct SetRsxMessage {
  158. pub location: CodeLocation,
  159. pub new_text: String,
  160. }
  161. /// Set many rsx texts at once to avoid duplicate errors
  162. #[derive(Serialize, Deserialize, Clone, Debug)]
  163. pub struct SetManyRsxMessage(pub Vec<SetRsxMessage>);