form.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. use std::{any::Any, collections::HashMap, fmt::Debug};
  2. use dioxus_core::Event;
  3. use serde::{de::Error, Deserialize, Serialize};
  4. pub type FormEvent = Event<FormData>;
  5. #[derive(Serialize, Deserialize, Debug)]
  6. #[serde(untagged)] // this will serialize Text(String) -> String and VecText(Vec<String>) to Vec<String>
  7. enum ValueType {
  8. Text(String),
  9. VecText(Vec<String>),
  10. }
  11. /* DOMEvent: Send + SyncTarget relatedTarget */
  12. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  13. #[derive(Clone)]
  14. pub struct FormData {
  15. pub value: String,
  16. pub values: HashMap<String, Vec<String>>,
  17. pub value_types: HashMap<String, String>,
  18. #[cfg_attr(
  19. feature = "serialize",
  20. serde(
  21. default,
  22. skip_serializing,
  23. deserialize_with = "deserialize_file_engine"
  24. )
  25. )]
  26. pub files: Option<std::sync::Arc<dyn FileEngine>>,
  27. }
  28. fn convert_hashmap_to_json<K, V>(hashmap: &HashMap<K, V>) -> serde_json::Result<String>
  29. where
  30. K: Serialize + std::hash::Hash + Eq,
  31. V: Serialize,
  32. {
  33. serde_json::to_string(hashmap)
  34. }
  35. impl FormData {
  36. // ***** function to parse the 'values' to make it ready to use*******
  37. // e.g - self.values = { username: ["rust"], password: ["dioxus"]}
  38. // what we need it to be: { username: "rust", password: "dioxus"}
  39. fn get_parsed_values(&self) -> Result<String, serde_json::Error> {
  40. if self.values.is_empty() {
  41. return Err(serde_json::Error::custom("Values array is empty"));
  42. }
  43. let raw_values = self.values.clone();
  44. let mut parsed_values: HashMap<String, ValueType> = HashMap::new();
  45. for (fieldname, values) in raw_values.into_iter() {
  46. // check if the fieldname can hold multiple values based on its types
  47. let field_type = self
  48. .value_types
  49. .get(&fieldname)
  50. .expect("Provided invalid field");
  51. let is_multi_valued_input = match field_type.as_str() {
  52. "select" | "checkbox" => true,
  53. _ => false,
  54. };
  55. /*
  56. case 1 - multiple values, example { driving_types: ["manual", "automatic"] }
  57. In this case we want to return the values as it is, NO point in making
  58. driving_types: "manual, automatic"
  59. case 2 - single value, example { favourite_language: ["rust"] }
  60. In this case we would want to deserialize the value as follows
  61. favourite_language: "rust"
  62. */
  63. parsed_values.insert(
  64. fieldname,
  65. if is_multi_valued_input {
  66. // handling multiple values - case 1
  67. ValueType::VecText(values)
  68. } else {
  69. // handle single value - case 2
  70. ValueType::Text(
  71. values
  72. .into_iter()
  73. .next()
  74. .ok_or_else(|| serde_json::Error::custom("Values array is empty"))?,
  75. )
  76. },
  77. );
  78. }
  79. // convert HashMap to JSON string
  80. convert_hashmap_to_json(&parsed_values)
  81. }
  82. pub fn parse_json<T>(&self) -> Result<T, serde_json::Error>
  83. where
  84. T: serde::de::DeserializeOwned,
  85. {
  86. let parsed_json = self
  87. .get_parsed_values()
  88. .expect("Failed to parse values to JSON");
  89. serde_json::from_str(&parsed_json)
  90. }
  91. }
  92. #[cfg(feature = "serialize")]
  93. #[derive(serde::Serialize, serde::Deserialize)]
  94. struct SerializedFileEngine {
  95. files: HashMap<String, Vec<u8>>,
  96. }
  97. #[cfg(feature = "serialize")]
  98. #[async_trait::async_trait(?Send)]
  99. impl FileEngine for SerializedFileEngine {
  100. fn files(&self) -> Vec<String> {
  101. self.files.keys().cloned().collect()
  102. }
  103. async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
  104. self.files.get(file).cloned()
  105. }
  106. async fn read_file_to_string(&self, file: &str) -> Option<String> {
  107. self.read_file(file)
  108. .await
  109. .map(|bytes| String::from_utf8_lossy(&bytes).to_string())
  110. }
  111. async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
  112. self.read_file(file)
  113. .await
  114. .map(|val| Box::new(val) as Box<dyn Any>)
  115. }
  116. }
  117. #[cfg(feature = "serialize")]
  118. fn deserialize_file_engine<'de, D>(
  119. deserializer: D,
  120. ) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
  121. where
  122. D: serde::Deserializer<'de>,
  123. {
  124. let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else {
  125. return Ok(None);
  126. };
  127. let file_engine = std::sync::Arc::new(file_engine);
  128. Ok(Some(file_engine))
  129. }
  130. impl PartialEq for FormData {
  131. fn eq(&self, other: &Self) -> bool {
  132. self.value == other.value && self.values == other.values
  133. }
  134. }
  135. impl Debug for FormData {
  136. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  137. f.debug_struct("FormEvent")
  138. .field("value", &self.value)
  139. .field("values", &self.values)
  140. .finish()
  141. }
  142. }
  143. #[async_trait::async_trait(?Send)]
  144. pub trait FileEngine {
  145. // get a list of file names
  146. fn files(&self) -> Vec<String>;
  147. // read a file to bytes
  148. async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
  149. // read a file to string
  150. async fn read_file_to_string(&self, file: &str) -> Option<String>;
  151. // returns a file in platform's native representation
  152. async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
  153. }
  154. impl_event! {
  155. FormData;
  156. /// onchange
  157. onchange
  158. /// oninput handler
  159. oninput
  160. /// oninvalid
  161. oninvalid
  162. /// onreset
  163. onreset
  164. /// onsubmit
  165. onsubmit
  166. }