hotreload_utils.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. use std::{
  2. any::{Any, TypeId},
  3. hash::{Hash, Hasher},
  4. };
  5. #[cfg(feature = "serialize")]
  6. use crate::nodes::deserialize_string_leaky;
  7. use crate::{
  8. Attribute, AttributeValue, DynamicNode, Template, TemplateAttribute, TemplateNode, VNode, VText,
  9. };
  10. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  11. #[doc(hidden)]
  12. #[derive(Debug, PartialEq, Clone)]
  13. pub struct HotreloadedLiteral {
  14. pub name: String,
  15. pub value: HotReloadLiteral,
  16. }
  17. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  18. #[doc(hidden)]
  19. #[derive(Debug, PartialEq, Clone)]
  20. pub enum HotReloadLiteral {
  21. Fmted(FmtedSegments),
  22. Float(f64),
  23. Int(i64),
  24. Bool(bool),
  25. }
  26. impl HotReloadLiteral {
  27. pub fn as_fmted(&self) -> Option<&FmtedSegments> {
  28. match self {
  29. Self::Fmted(segments) => Some(segments),
  30. _ => None,
  31. }
  32. }
  33. pub fn as_float(&self) -> Option<f64> {
  34. match self {
  35. Self::Float(f) => Some(*f),
  36. _ => None,
  37. }
  38. }
  39. pub fn as_int(&self) -> Option<i64> {
  40. match self {
  41. Self::Int(i) => Some(*i),
  42. _ => None,
  43. }
  44. }
  45. pub fn as_bool(&self) -> Option<bool> {
  46. match self {
  47. Self::Bool(b) => Some(*b),
  48. _ => None,
  49. }
  50. }
  51. }
  52. impl Hash for HotReloadLiteral {
  53. fn hash<H: Hasher>(&self, state: &mut H) {
  54. match self {
  55. Self::Fmted(segments) => segments.hash(state),
  56. Self::Float(f) => f.to_bits().hash(state),
  57. Self::Int(i) => i.hash(state),
  58. Self::Bool(b) => b.hash(state),
  59. }
  60. }
  61. }
  62. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  63. #[doc(hidden)]
  64. #[derive(Debug, PartialEq, Eq, Clone, Hash)]
  65. pub struct FmtedSegments {
  66. pub(crate) segments: Vec<FmtSegment>,
  67. }
  68. impl FmtedSegments {
  69. pub fn new(segments: Vec<FmtSegment>) -> Self {
  70. Self { segments }
  71. }
  72. /// Render the formatted string by stitching together the segments
  73. pub(crate) fn render_with(&self, dynamic_text: &[String]) -> String {
  74. let mut out = String::new();
  75. for segment in &self.segments {
  76. match segment {
  77. FmtSegment::Literal { value } => out.push_str(value),
  78. FmtSegment::Dynamic { id } => out.push_str(&dynamic_text[*id]),
  79. }
  80. }
  81. out
  82. }
  83. }
  84. type StaticStr = &'static str;
  85. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  86. #[doc(hidden)]
  87. #[derive(Debug, PartialEq, Eq, Clone, Hash)]
  88. pub enum FmtSegment {
  89. Literal {
  90. #[cfg_attr(
  91. feature = "serialize",
  92. serde(deserialize_with = "deserialize_string_leaky")
  93. )]
  94. value: StaticStr,
  95. },
  96. Dynamic {
  97. id: usize,
  98. },
  99. }
  100. // let __pool = DynamicValuePool::new(
  101. // vec![...],
  102. // vec![...],
  103. // vec![...],
  104. // );
  105. // VNode::new(
  106. // None,
  107. // Template {
  108. // name: "...",
  109. // roots: &[...],
  110. // node_paths: &[..],
  111. // attr_paths: &[...],
  112. // },
  113. // Box::new([...]),
  114. // Box::new([...]),
  115. // )
  116. // Open questions:
  117. // - How do we handle type coercion for different sized component property integers?
  118. // - Should non-string hot literals go through the centralized pool?
  119. // - Should formatted strings be a runtime concept?
  120. #[doc(hidden)]
  121. pub struct DynamicLiteralPool {
  122. dynamic_text: Box<[String]>,
  123. }
  124. impl DynamicLiteralPool {
  125. pub fn new(dynamic_text: Vec<String>) -> Self {
  126. Self {
  127. dynamic_text: dynamic_text.into_boxed_slice(),
  128. }
  129. }
  130. pub fn get_component_property<'a, T>(
  131. &self,
  132. id: usize,
  133. hot_reload: &'a HotReloadedTemplate,
  134. f: impl FnOnce(&'a HotReloadLiteral) -> Option<T>,
  135. ) -> Option<T> {
  136. f(hot_reload.component_values.get(id)?)
  137. }
  138. /// Get a component property of a specific type at the component property index
  139. pub fn component_property<T: 'static>(
  140. &mut self,
  141. id: usize,
  142. hot_reload: &HotReloadedTemplate,
  143. // We pass in the original value for better type inference
  144. // For example, if the original literal is `0i128`, we know the output must be the type `i128`
  145. _coherse_type: T,
  146. ) -> T {
  147. fn assert_type<T: 'static, T2: 'static>(t: T) -> T2 {
  148. *(Box::new(t) as Box<dyn Any>).downcast::<T2>().unwrap()
  149. }
  150. let grab_float = || {
  151. self.get_component_property(id, hot_reload, HotReloadLiteral::as_float).unwrap_or_else(|| {
  152. tracing::error!("Expected a float component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::<T>(), hot_reload.component_values.get(id));
  153. Default::default()
  154. })
  155. };
  156. let grab_int = || {
  157. self.get_component_property(id, hot_reload, HotReloadLiteral::as_int).unwrap_or_else(|| {
  158. tracing::error!("Expected a integer component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::<T>(), hot_reload.component_values.get(id));
  159. Default::default()
  160. })
  161. };
  162. let grab_bool = || {
  163. self.get_component_property(id, hot_reload, HotReloadLiteral::as_bool).unwrap_or_else(|| {
  164. tracing::error!("Expected a bool component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::<T>(), hot_reload.component_values.get(id));
  165. Default::default()
  166. })
  167. };
  168. let grab_fmted = || {
  169. self.get_component_property(id, hot_reload, |fmted| HotReloadLiteral::as_fmted(fmted).map(|segments| self.render_formatted(segments))).unwrap_or_else(|| {
  170. tracing::error!("Expected a string component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::<T>(), hot_reload.component_values.get(id));
  171. Default::default()
  172. })
  173. };
  174. match TypeId::of::<T>() {
  175. // Any string types that accept a literal
  176. _ if TypeId::of::<String>() == TypeId::of::<T>() => assert_type(grab_fmted()),
  177. _ if TypeId::of::<&str>() == TypeId::of::<T>() => {
  178. assert_type(Box::leak(grab_fmted().into_boxed_str()) as &'static str)
  179. }
  180. // Any integer types that accept a literal
  181. _ if TypeId::of::<i128>() == TypeId::of::<T>() => assert_type(grab_int() as i128),
  182. _ if TypeId::of::<i64>() == TypeId::of::<T>() => assert_type(grab_int()),
  183. _ if TypeId::of::<i32>() == TypeId::of::<T>() => assert_type(grab_int() as i32),
  184. _ if TypeId::of::<i16>() == TypeId::of::<T>() => assert_type(grab_int() as i16),
  185. _ if TypeId::of::<i8>() == TypeId::of::<T>() => assert_type(grab_int() as i8),
  186. _ if TypeId::of::<isize>() == TypeId::of::<T>() => assert_type(grab_int() as isize),
  187. _ if TypeId::of::<u128>() == TypeId::of::<T>() => assert_type(grab_int() as u128),
  188. _ if TypeId::of::<u64>() == TypeId::of::<T>() => assert_type(grab_int() as u64),
  189. _ if TypeId::of::<u32>() == TypeId::of::<T>() => assert_type(grab_int() as u32),
  190. _ if TypeId::of::<u16>() == TypeId::of::<T>() => assert_type(grab_int() as u16),
  191. _ if TypeId::of::<u8>() == TypeId::of::<T>() => assert_type(grab_int() as u8),
  192. _ if TypeId::of::<usize>() == TypeId::of::<T>() => assert_type(grab_int() as usize),
  193. // Any float types that accept a literal
  194. _ if TypeId::of::<f64>() == TypeId::of::<T>() => assert_type(grab_float()),
  195. _ if TypeId::of::<f32>() == TypeId::of::<T>() => assert_type(grab_float() as f32),
  196. // Any bool types that accept a literal
  197. _ if TypeId::of::<bool>() == TypeId::of::<T>() => assert_type(grab_bool()),
  198. _ => panic!("Unsupported component property type"),
  199. }
  200. }
  201. pub fn render_formatted(&self, segments: &FmtedSegments) -> String {
  202. segments.render_with(&self.dynamic_text)
  203. }
  204. }
  205. #[doc(hidden)]
  206. pub struct DynamicValuePool {
  207. dynamic_attributes: Box<[Box<[Attribute]>]>,
  208. dynamic_nodes: Box<[DynamicNode]>,
  209. literal_pool: DynamicLiteralPool,
  210. }
  211. impl DynamicValuePool {
  212. pub fn new(
  213. dynamic_nodes: Vec<DynamicNode>,
  214. dynamic_attributes: Vec<Box<[Attribute]>>,
  215. literal_pool: DynamicLiteralPool,
  216. ) -> Self {
  217. Self {
  218. dynamic_attributes: dynamic_attributes.into_boxed_slice(),
  219. dynamic_nodes: dynamic_nodes.into_boxed_slice(),
  220. literal_pool,
  221. }
  222. }
  223. pub fn render_with(&mut self, hot_reload: &HotReloadedTemplate) -> VNode {
  224. // Get the node_paths from a depth first traversal of the template
  225. let key = hot_reload
  226. .key
  227. .as_ref()
  228. .map(|key| self.literal_pool.render_formatted(key));
  229. let dynamic_nodes = hot_reload
  230. .dynamic_nodes
  231. .iter()
  232. .map(|node| self.render_dynamic_node(node))
  233. .collect();
  234. let dynamic_attrs = hot_reload
  235. .dynamic_attributes
  236. .iter()
  237. .map(|attr| self.render_attribute(attr))
  238. .collect();
  239. VNode::new(key, hot_reload.template, dynamic_nodes, dynamic_attrs)
  240. }
  241. fn render_dynamic_node(&mut self, node: &HotReloadDynamicNode) -> DynamicNode {
  242. match node {
  243. // If the node is dynamic, take it from the pool and return it
  244. HotReloadDynamicNode::Dynamic(id) => self.dynamic_nodes[*id].clone(),
  245. // Otherwise, format the text node and return it
  246. HotReloadDynamicNode::Formatted(segments) => DynamicNode::Text(VText {
  247. value: self.literal_pool.render_formatted(segments),
  248. }),
  249. }
  250. }
  251. fn render_attribute(&mut self, attr: &HotReloadDynamicAttribute) -> Box<[Attribute]> {
  252. match attr {
  253. HotReloadDynamicAttribute::Dynamic(id) => self.dynamic_attributes[*id].clone(),
  254. HotReloadDynamicAttribute::Named(NamedAttribute {
  255. name,
  256. namespace,
  257. value,
  258. }) => Box::new([Attribute {
  259. name,
  260. namespace: *namespace,
  261. value: match value {
  262. HotReloadAttributeValue::Literal(HotReloadLiteral::Fmted(segments)) => {
  263. AttributeValue::Text(self.literal_pool.render_formatted(segments))
  264. }
  265. HotReloadAttributeValue::Literal(HotReloadLiteral::Float(f)) => {
  266. AttributeValue::Float(*f)
  267. }
  268. HotReloadAttributeValue::Literal(HotReloadLiteral::Int(i)) => {
  269. AttributeValue::Int(*i)
  270. }
  271. HotReloadAttributeValue::Literal(HotReloadLiteral::Bool(b)) => {
  272. AttributeValue::Bool(*b)
  273. }
  274. HotReloadAttributeValue::Dynamic(id) => {
  275. self.dynamic_attributes[*id][0].value.clone()
  276. }
  277. },
  278. volatile: false,
  279. }]),
  280. }
  281. }
  282. }
  283. #[doc(hidden)]
  284. #[derive(Debug, Clone, PartialEq)]
  285. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  286. pub struct HotReloadTemplateWithLocation {
  287. pub key: TemplateGlobalKey,
  288. pub template: HotReloadedTemplate,
  289. }
  290. #[doc(hidden)]
  291. #[derive(Debug, Clone, PartialEq, Hash, PartialOrd, Eq, Ord)]
  292. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  293. pub struct TemplateGlobalKey {
  294. pub file: String,
  295. pub line: usize,
  296. pub column: usize,
  297. pub index: usize,
  298. }
  299. type StaticTemplateArray = &'static [TemplateNode];
  300. #[doc(hidden)]
  301. #[derive(Debug, PartialEq, Clone)]
  302. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  303. pub struct HotReloadedTemplate {
  304. pub key: Option<FmtedSegments>,
  305. pub dynamic_nodes: Vec<HotReloadDynamicNode>,
  306. pub dynamic_attributes: Vec<HotReloadDynamicAttribute>,
  307. pub component_values: Vec<HotReloadLiteral>,
  308. #[cfg_attr(
  309. feature = "serialize",
  310. serde(deserialize_with = "crate::nodes::deserialize_leaky")
  311. )]
  312. pub roots: StaticTemplateArray,
  313. /// The template that is computed from the hot reload roots
  314. template: Template,
  315. }
  316. impl HotReloadedTemplate {
  317. pub fn new(
  318. key: Option<FmtedSegments>,
  319. dynamic_nodes: Vec<HotReloadDynamicNode>,
  320. dynamic_attributes: Vec<HotReloadDynamicAttribute>,
  321. component_values: Vec<HotReloadLiteral>,
  322. roots: &'static [TemplateNode],
  323. ) -> Self {
  324. let node_paths = Self::node_paths(roots);
  325. let attr_paths = Self::attr_paths(roots);
  326. let template = Template {
  327. roots,
  328. node_paths,
  329. attr_paths,
  330. };
  331. Self {
  332. key,
  333. dynamic_nodes,
  334. dynamic_attributes,
  335. component_values,
  336. roots,
  337. template,
  338. }
  339. }
  340. fn node_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] {
  341. fn add_node_paths(
  342. roots: &[TemplateNode],
  343. node_paths: &mut Vec<&'static [u8]>,
  344. current_path: Vec<u8>,
  345. ) {
  346. for (idx, node) in roots.iter().enumerate() {
  347. let mut path = current_path.clone();
  348. path.push(idx as u8);
  349. match node {
  350. TemplateNode::Element { children, .. } => {
  351. add_node_paths(children, node_paths, path);
  352. }
  353. TemplateNode::Text { .. } => {}
  354. TemplateNode::Dynamic { id } => {
  355. debug_assert_eq!(node_paths.len(), *id);
  356. node_paths.push(Box::leak(path.into_boxed_slice()));
  357. }
  358. }
  359. }
  360. }
  361. let mut node_paths = Vec::new();
  362. add_node_paths(roots, &mut node_paths, Vec::new());
  363. let leaked: &'static [&'static [u8]] = Box::leak(node_paths.into_boxed_slice());
  364. leaked
  365. }
  366. fn attr_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] {
  367. fn add_attr_paths(
  368. roots: &[TemplateNode],
  369. attr_paths: &mut Vec<&'static [u8]>,
  370. current_path: Vec<u8>,
  371. ) {
  372. for (idx, node) in roots.iter().enumerate() {
  373. let mut path = current_path.clone();
  374. path.push(idx as u8);
  375. if let TemplateNode::Element {
  376. children, attrs, ..
  377. } = node
  378. {
  379. for attr in *attrs {
  380. if let TemplateAttribute::Dynamic { id } = attr {
  381. debug_assert_eq!(attr_paths.len(), *id);
  382. attr_paths.push(Box::leak(path.clone().into_boxed_slice()));
  383. }
  384. }
  385. add_attr_paths(children, attr_paths, path);
  386. }
  387. }
  388. }
  389. let mut attr_paths = Vec::new();
  390. add_attr_paths(roots, &mut attr_paths, Vec::new());
  391. let leaked: &'static [&'static [u8]] = Box::leak(attr_paths.into_boxed_slice());
  392. leaked
  393. }
  394. }
  395. #[doc(hidden)]
  396. #[derive(Debug, PartialEq, Clone, Hash)]
  397. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  398. pub enum HotReloadDynamicNode {
  399. Dynamic(usize),
  400. Formatted(FmtedSegments),
  401. }
  402. #[doc(hidden)]
  403. #[derive(Debug, PartialEq, Clone, Hash)]
  404. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  405. pub enum HotReloadDynamicAttribute {
  406. Dynamic(usize),
  407. Named(NamedAttribute),
  408. }
  409. #[doc(hidden)]
  410. #[derive(Debug, PartialEq, Clone, Hash)]
  411. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  412. pub struct NamedAttribute {
  413. /// The name of this attribute.
  414. #[cfg_attr(
  415. feature = "serialize",
  416. serde(deserialize_with = "crate::nodes::deserialize_string_leaky")
  417. )]
  418. name: StaticStr,
  419. /// The namespace of this attribute. Does not exist in the HTML spec
  420. #[cfg_attr(
  421. feature = "serialize",
  422. serde(deserialize_with = "crate::nodes::deserialize_option_leaky")
  423. )]
  424. namespace: Option<StaticStr>,
  425. value: HotReloadAttributeValue,
  426. }
  427. impl NamedAttribute {
  428. pub fn new(
  429. name: &'static str,
  430. namespace: Option<&'static str>,
  431. value: HotReloadAttributeValue,
  432. ) -> Self {
  433. Self {
  434. name,
  435. namespace,
  436. value,
  437. }
  438. }
  439. }
  440. #[doc(hidden)]
  441. #[derive(Debug, PartialEq, Clone, Hash)]
  442. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  443. pub enum HotReloadAttributeValue {
  444. Literal(HotReloadLiteral),
  445. Dynamic(usize),
  446. }