hot_reloading_file_map.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. use crate::{CallBody, HotReloadingContext};
  2. use dioxus_core::Template;
  3. pub use proc_macro2::TokenStream;
  4. pub use std::collections::HashMap;
  5. use std::path::PathBuf;
  6. pub use std::sync::Mutex;
  7. pub use std::time::SystemTime;
  8. pub use std::{fs, io, path::Path};
  9. pub use std::{fs::File, io::Read};
  10. pub use syn::__private::ToTokens;
  11. use syn::spanned::Spanned;
  12. use super::hot_reload_diff::{find_rsx, DiffResult};
  13. pub enum UpdateResult {
  14. UpdatedRsx(Vec<Template<'static>>),
  15. NeedsRebuild,
  16. }
  17. pub struct FileMap<Ctx: HotReloadingContext> {
  18. pub map: HashMap<PathBuf, (String, Option<Template<'static>>)>,
  19. phantom: std::marker::PhantomData<Ctx>,
  20. }
  21. impl<Ctx: HotReloadingContext> FileMap<Ctx> {
  22. /// Create a new FileMap from a crate directory
  23. pub fn new(path: PathBuf) -> Self {
  24. fn find_rs_files(
  25. root: PathBuf,
  26. ) -> io::Result<HashMap<PathBuf, (String, Option<Template<'static>>)>> {
  27. let mut files = HashMap::new();
  28. if root.is_dir() {
  29. for entry in (fs::read_dir(root)?).flatten() {
  30. let path = entry.path();
  31. files.extend(find_rs_files(path)?);
  32. }
  33. } else if root.extension().and_then(|s| s.to_str()) == Some("rs") {
  34. if let Ok(mut file) = File::open(root.clone()) {
  35. let mut src = String::new();
  36. file.read_to_string(&mut src).expect("Unable to read file");
  37. files.insert(root, (src, None));
  38. }
  39. }
  40. Ok(files)
  41. }
  42. let result = Self {
  43. map: find_rs_files(path).unwrap(),
  44. phantom: std::marker::PhantomData,
  45. };
  46. result
  47. }
  48. /// Try to update the rsx in a file
  49. pub fn update_rsx(&mut self, file_path: &Path, crate_dir: &Path) -> UpdateResult {
  50. let mut file = File::open(file_path).unwrap();
  51. let mut src = String::new();
  52. file.read_to_string(&mut src).expect("Unable to read file");
  53. if let Ok(syntax) = syn::parse_file(&src) {
  54. if let Some((old_src, template_slot)) = self.map.get_mut(file_path) {
  55. if let Ok(old) = syn::parse_file(old_src) {
  56. match find_rsx(&syntax, &old) {
  57. DiffResult::CodeChanged => {
  58. self.map.insert(file_path.to_path_buf(), (src, None));
  59. }
  60. DiffResult::RsxChanged(changed) => {
  61. let mut messages: Vec<Template<'static>> = Vec::new();
  62. for (old, new) in changed.into_iter() {
  63. let old_start = old.span().start();
  64. if let (Ok(old_call_body), Ok(new_call_body)) = (
  65. syn::parse2::<CallBody>(old.tokens),
  66. syn::parse2::<CallBody>(new),
  67. ) {
  68. if let Ok(file) = file_path.strip_prefix(crate_dir) {
  69. let line = old_start.line;
  70. let column = old_start.column + 1;
  71. let location = file.display().to_string()
  72. + ":"
  73. + &line.to_string()
  74. + ":"
  75. + &column.to_string()
  76. // the byte index doesn't matter, but dioxus needs it
  77. + ":0";
  78. if let Some(template) = new_call_body
  79. .update_template::<Ctx>(
  80. Some(old_call_body),
  81. Box::leak(location.into_boxed_str()),
  82. )
  83. {
  84. // dioxus cannot handle empty templates
  85. if template.roots.is_empty() {
  86. return UpdateResult::NeedsRebuild;
  87. } else {
  88. // if the template is the same, don't send it
  89. if let Some(old_template) = template_slot {
  90. if old_template == &template {
  91. continue;
  92. }
  93. }
  94. *template_slot = Some(template);
  95. messages.push(template);
  96. }
  97. } else {
  98. return UpdateResult::NeedsRebuild;
  99. }
  100. }
  101. }
  102. }
  103. return UpdateResult::UpdatedRsx(messages);
  104. }
  105. }
  106. }
  107. } else {
  108. // if this is a new file, rebuild the project
  109. *self = FileMap::new(crate_dir.to_path_buf());
  110. }
  111. }
  112. UpdateResult::NeedsRebuild
  113. }
  114. }