1use bevy_reflect::{Reflect, Struct, ReflectMut, List};
7use std::num::ParseFloatError;
8use web_sys::{ Document };
9use substring::*;
10use regex::Regex;
11
12pub trait ExtractNums {
14
15 fn try_extract_nums(&self) -> Option<String>;
17
18 fn try_to_f64(&self) -> Result<f64, ParseFloatError>;
20}
21
22impl ExtractNums for String {
23 fn try_extract_nums(&self) -> Option<String> {
24 Some( self.chars().filter(|c| c.is_digit(10) || c == &'.' || c == &'-').collect() )
25 }
26
27 fn try_to_f64(&self) -> Result<f64, ParseFloatError> {
28 self.try_extract_nums().unwrap().parse::<f64>()
29 }
30}
31
32pub trait Style: Reflect + Struct {
34
35 fn create() -> Self;
37
38 fn set_string_reflect(string_reflect: &mut dyn Reflect, value: &str) where Self: Sized {
39 *string_reflect.downcast_mut::<String>().unwrap() = value.to_string();
40 }
41
42 fn set_list_reflect(list_reflect: &mut dyn List, value: &str) where Self: Sized {
43 if let Some(string_vec) = list_reflect.as_reflect_mut().downcast_mut::<Vec<String>>() {
44 let string_vec_value = value.split(",").map(|v| {
45 v.to_string()
46 }).collect::<Vec<String>>();
47 *string_vec = string_vec_value;
48 } else {
49 let new_structs_values = value
53 .split_inclusive("),") .map(|s| { s.strip_suffix(",").unwrap_or(s) }) .collect::<Vec<&str>>();
56 for i in 0..list_reflect.len() {
57 match list_reflect.get_mut(i).unwrap().reflect_mut() {
58 ReflectMut::Struct(struct_reflect) => {
59 Self::set_struct_reflect(struct_reflect, new_structs_values[i]);
60 }
61 ReflectMut::TupleStruct(_) => todo!(),
62 ReflectMut::Tuple(_) => todo!(),
63 ReflectMut::List(_) => todo!(),
64 ReflectMut::Array(_) => todo!(),
65 ReflectMut::Map(_) => todo!(),
66 ReflectMut::Enum(_) => todo!(),
67 ReflectMut::Value(_) => todo!(),
68 }
69 }
70 }
71 }
72
73 fn set_struct_reflect(struct_reflect: &mut dyn Struct, value: &str) where Self: Sized {
85 let value = value.replace(" ", ""); let func_rest= value.split_once("("); if func_rest.is_none() {
89 let warn_msg = format!(" rusty-css:\n Warning: parsing for the struct \n{:?}\n failed. \n There might be some missplaced parentheses.", struct_reflect.get_type_info());
90 log::warn!("{}", warn_msg); println!("{}", warn_msg);
91 return;
92 }
93 let func_rest = func_rest.unwrap();
94 let func_name = &func_rest.0.replace("_","-");
95
96 if let Some(nested_struct_field) = struct_reflect.field_mut(func_name) {
97 let new_value: (&str, &str);
98 match nested_struct_field.reflect_mut() {
99 ReflectMut::List(nested_srtruct_field_list_ref) => {
101 new_value = func_rest.1.split_once(")").unwrap(); Self::set_list_reflect(nested_srtruct_field_list_ref, new_value.0);
103 }
104 ReflectMut::Struct(nested_srtruct_field_struct_ref) => {
105 let split_at = Regex::new(r"\)\)([a-zA-Z_-]|$)").unwrap(); let split_pos = split_at.find(func_rest.1).unwrap();
108 let new_val_left = func_rest.1.split_at(split_pos.start() + 1_usize).0;
109 let mut new_val_right = func_rest.1.split_at(split_pos.end() - 1_usize).1;
110 if new_val_right == ")" { new_val_right = "" } new_value = (new_val_left, new_val_right); Self::set_struct_reflect(nested_srtruct_field_struct_ref, new_value.0);
113 },
114 ReflectMut::TupleStruct(_) => todo!(),
115 ReflectMut::Tuple(_) => todo!(),
116 ReflectMut::Array(_) => todo!(),
117 ReflectMut::Map(_) => todo!(),
118 ReflectMut::Enum(_) => todo!(),
119 ReflectMut::Value(nested_srtruct_field_value_ref) => {
121 new_value = func_rest.1.split_once(")").unwrap(); *nested_srtruct_field_value_ref.downcast_mut::<String>().unwrap() = new_value.0.to_string();
123 }
124 }
125
126 if !new_value.1.is_empty() && !new_value.1.starts_with(",") {
128 Self::set_struct_reflect(struct_reflect, new_value.1);
129 }
130 }
131 }
132
133 fn set_from_inline_string(&mut self, style: String) -> &Self where Self: Sized {
135 let prop_value = style.split(";"); prop_value.into_iter().for_each(|pv| {
137 let prop_value_vec = pv.split(":").collect::<Vec<&str>>();
138 let field_name = prop_value_vec[0].replace("-", "_").replace(" ", "").replace("\n", "");
139
140 if let Some(field) = self.field_mut(&field_name) {
142 match field.reflect_mut() {
144 ReflectMut::Struct(struct_reflect) => {
145 Self::set_struct_reflect(struct_reflect, prop_value_vec[1])
146 },
147 ReflectMut::List(list_reflect) => {
148 Self::set_list_reflect(list_reflect, prop_value_vec[1])
149 },
150 ReflectMut::Array(_) => todo!(),
151 ReflectMut::TupleStruct(_) => todo!(),
152 ReflectMut::Tuple(_) => todo!(),
153 ReflectMut::Map(_) => todo!(),
154 ReflectMut::Enum(_) => todo!(),
155 ReflectMut::Value(string_reflect) => {
156 Self::set_string_reflect(string_reflect, prop_value_vec[1])
157 }
158 }
159 }
160
161 });
182 self
183 }
184
185 fn inline(&self) -> String where Self: Sized {
187 let mut style_string = "".to_owned();
188
189 for (i, value_reflect) in self.iter_fields().enumerate() {
191
192 let mut property_name = self.name_at(i).unwrap().to_owned();
194
195 if property_name != "append" {
200 property_name = property_name.replace("_", "-");
201
202 let value = Self::create_value_string(value_reflect);
204
205 style_string.push_str( &format!("{property}:{value}; ", property = property_name, value = value) );
206 }
207 }
208
209 style_string
210 }
211
212 fn create_value_string(reflect: &dyn Reflect) -> String {
214 let mut value = "".to_owned();
215
216 match &reflect.reflect_ref() {
217 bevy_reflect::ReflectRef::Value(v) => {
219 let value_string = " ".to_owned() + v.downcast_ref::<String>().unwrap();
220 value.push_str( &value_string );
221 }
222
223 bevy_reflect::ReflectRef::Struct(fields) => {
225
226 for (i, value_reflect) in fields.iter_fields().enumerate() {
228 let function_name = fields.name_at(i).unwrap();
230 let function_param = Self::create_value_string(value_reflect);
231 let value_string = format!(" {function}({parameter})", function = &function_name, parameter = &function_param);
232 value.push_str(&value_string);
233 }
234 },
235 bevy_reflect::ReflectRef::Array(arr) => {
236 for (i, value_reflect) in arr.iter().enumerate() {
238 let mut comma = "".to_string();
240 if i != 0 {
241 comma = ",".to_string();
242 }
243
244 let value_string = format!("{comma}{value}", value = Self::create_value_string(value_reflect), comma = comma);
245 value.push_str(&value_string);
246 }
247 },
248 _ => {
249 log::warn!("The given Object is only allowed to have String fields or Structs with String fields. \nGot: {:?}", &reflect.get_type_info());
250 }
251 }
252
253 value
254 }
255
256
257 fn as_class_string(&self, class_name: &String) -> Result<String, &'static str> where Self: Sized {
258
259 let mut class_name_appended = class_name.clone();
260 let append = self.field("append");
262 if !append.is_none() {
263 class_name_appended.push_str(
264 append.unwrap().downcast_ref::<String>().unwrap()
265 );
266 }
267
268 Ok( format!(".{} {{ {}}}", class_name_appended, self.inline()) )
269 }
270
271 fn as_class(&self, document: &Document) -> Result<String, &'static str> where Self: Sized {
272
273 let class_name = self.get_struct_name().unwrap();
275
276 let class_string = self.as_class_string(&class_name).expect("Class string could not be created");
278
279 self.append_to_head(&document, &class_name, &class_string);
281
282 Ok(class_name)
284 }
285
286 fn append_to_head(&self, document: &Document, class_name: &String, class_string: &String) where Self: Sized {
287 let head = document.head().expect("No <head> element found in the document");
288 let new_style_element = document.create_element("style").expect("couldn't create <style> element in this document");
289 let style_id = format!("rusty-css-{}", class_name.replace(":", "_"));
290 new_style_element.set_attribute("id", &style_id ).expect("couldn't set attribute of internally created style tag");
291 new_style_element.set_text_content(Some(&class_string));
292
293 if let Some(existent_style) = head.query_selector(&format!("#{}", style_id) ).expect("an error occured while trying to fetch the element with id `rusty-css` in head") {
294 head.remove_child(&existent_style).expect("couldn't remove child element with id `rusty-css` in head");
295 }
296
297 head.append_child(&new_style_element).expect("couldn't append internally created `style` element with id `rusty-css` to head");
298 }
299
300 fn add_as_pseudo_class(&self, document: &Document) where Self: Sized {
301
302 let mut class_name = self.get_struct_name().unwrap();
303 class_name = class_name.replace("_", ":");
304
305 let class_string = self.as_class_string(&class_name).expect("Class string could not be created");
306
307 self.append_to_head(document, &class_name, &class_string);
308 }
309
310 fn get_struct_name(&self) -> Result<String, &'static str> where Self: Sized {
311
312 let class_name_pos = self.type_name().rfind("::").expect("(Internal Error) couldn't find position of `::` in type_name");
314 let class_name_slice = self.type_name().substring(class_name_pos + 2, self.type_name().len());
315 if class_name_slice == "" { return Err("(Internal Error) couldn't strip arbitrary_caller_name:: prefix"); }
316
317 Ok(class_name_slice.to_owned())
318 }
319
320 fn debug(self) -> Self where Self: Sized {
321
322 wasm_logger::init(wasm_logger::Config::default());
323
324 for (_i, value_reflect) in self.iter_fields().enumerate() {
325 log::info!("{:?}", value_reflect.get_type_info());
326 }
327
328 self
329 }
330}