form.rs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. use std::{any::Any, collections::HashMap, fmt::Debug};
  2. use dioxus_core::Event;
  3. pub type FormEvent = Event<FormData>;
  4. /* DOMEvent: Send + SyncTarget relatedTarget */
  5. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  6. #[derive(Clone)]
  7. pub struct FormData {
  8. pub value: String,
  9. pub values: HashMap<String, Vec<String>>,
  10. #[cfg_attr(
  11. feature = "serialize",
  12. serde(
  13. default,
  14. skip_serializing,
  15. deserialize_with = "deserialize_file_engine"
  16. )
  17. )]
  18. pub files: Option<std::sync::Arc<dyn FileEngine>>,
  19. }
  20. #[cfg(feature = "serialize")]
  21. #[derive(serde::Serialize, serde::Deserialize)]
  22. struct SerializedFileEngine {
  23. files: HashMap<String, Vec<u8>>,
  24. }
  25. #[cfg(feature = "serialize")]
  26. #[async_trait::async_trait(?Send)]
  27. impl FileEngine for SerializedFileEngine {
  28. fn files(&self) -> Vec<String> {
  29. self.files.keys().cloned().collect()
  30. }
  31. async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
  32. self.files.get(file).cloned()
  33. }
  34. async fn read_file_to_string(&self, file: &str) -> Option<String> {
  35. self.read_file(file)
  36. .await
  37. .map(|bytes| String::from_utf8_lossy(&bytes).to_string())
  38. }
  39. async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
  40. self.read_file(file)
  41. .await
  42. .map(|val| Box::new(val) as Box<dyn Any>)
  43. }
  44. }
  45. #[cfg(feature = "serialize")]
  46. fn deserialize_file_engine<'de, D>(
  47. deserializer: D,
  48. ) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
  49. where
  50. D: serde::Deserializer<'de>,
  51. {
  52. use serde::Deserialize;
  53. let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else {
  54. return Ok(None);
  55. };
  56. let file_engine = std::sync::Arc::new(file_engine);
  57. Ok(Some(file_engine))
  58. }
  59. impl PartialEq for FormData {
  60. fn eq(&self, other: &Self) -> bool {
  61. self.value == other.value && self.values == other.values
  62. }
  63. }
  64. impl Debug for FormData {
  65. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  66. f.debug_struct("FormEvent")
  67. .field("value", &self.value)
  68. .field("values", &self.values)
  69. .finish()
  70. }
  71. }
  72. #[async_trait::async_trait(?Send)]
  73. pub trait FileEngine {
  74. // get a list of file names
  75. fn files(&self) -> Vec<String>;
  76. // read a file to bytes
  77. async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
  78. // read a file to string
  79. async fn read_file_to_string(&self, file: &str) -> Option<String>;
  80. // returns a file in platform's native representation
  81. async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
  82. }
  83. impl_event! {
  84. FormData;
  85. /// onchange
  86. onchange
  87. /// oninput handler
  88. oninput
  89. /// oninvalid
  90. oninvalid
  91. /// onreset
  92. onreset
  93. /// onsubmit
  94. onsubmit
  95. }