stylex_transform/transform/styleq/
common.rs1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use indexmap::IndexMap;
5use log::error;
6use std::collections::hash_map::DefaultHasher;
7use std::hash::{Hash, Hasher};
8use stylex_macros::{stylex_panic, stylex_unimplemented, stylex_unreachable};
9
10use crate::shared::enums::data_structures::flat_compiled_styles_value::FlatCompiledStylesValue;
11use crate::shared::structures::types::FlatCompiledStyles;
12use crate::shared::utils::core::parse_nullable_style::{ResolvedArg, StyleObject};
13use stylex_constants::constants::common::COMPILED_KEY;
14
15pub(crate) struct StyleQResult {
16 pub(crate) class_name: String,
17 pub(crate) inline_style: Option<FlatCompiledStyles>,
18 pub(crate) data_style_src: Option<String>,
19}
20
21fn get_hash<T>(obj: T) -> u64
22where
23 T: Hash,
24{
25 let mut hasher = DefaultHasher::new();
26 obj.hash(&mut hasher);
27 hasher.finish()
28}
29
30pub(crate) fn styleq(arguments: &[ResolvedArg]) -> StyleQResult {
31 let mut class_name = String::default();
32 let mut debug_string = String::default();
33
34 if arguments.is_empty() {
35 return StyleQResult {
36 class_name,
37 inline_style: None,
38 data_style_src: None,
39 };
40 }
41
42 let mut defined_properties: Vec<String> = vec![];
43 let mut inline_style: Option<FlatCompiledStyles> = None;
44 let mut next_cache: Option<IndexMap<u64, (String, Vec<String>, String)>> = Some(IndexMap::new());
45 let mut styles = arguments.iter().collect::<Vec<_>>();
46
47 while let Some(possible_style) = styles.pop() {
49 let possible_style = match possible_style {
50 ResolvedArg::StyleObject(_, _, _) => possible_style,
51 ResolvedArg::ConditionalStyle(_, value, _, _, _) => {
52 if value.is_some() {
53 possible_style
54 } else {
55 continue;
56 }
57 },
58 };
59
60 match possible_style {
61 ResolvedArg::StyleObject(style, _, _) => match style {
62 StyleObject::Style(style) => {
63 if let Some(compiled_key) = style.get(COMPILED_KEY) {
64 if let FlatCompiledStylesValue::Bool(_) | FlatCompiledStylesValue::String(_) =
66 compiled_key.as_ref()
67 {
68 let mut class_name_chunk = String::default();
69
70 let cache_hit = if let Some(ref next_cache) = next_cache {
72 let btree_map: BTreeMap<_, _> = style.iter().collect();
73 let style_hash = get_hash(btree_map);
74
75 next_cache.get(&style_hash).map(|entry| {
76 (
77 style_hash,
78 entry.0.clone(),
79 entry.1.clone(),
80 entry.2.clone(),
81 )
82 })
83 } else {
84 None
85 };
86
87 if let Some((_hash, cached_class_name, cached_properties, cached_debug_string)) =
88 cache_hit
89 {
90 class_name_chunk = cached_class_name;
92 defined_properties.extend(cached_properties);
93 debug_string = cached_debug_string;
94 } else {
95 let mut defined_properties_chunk: Vec<String> = vec![];
97
98 for (prop, value) in style.iter() {
99 if prop.eq(COMPILED_KEY) {
100 let compiled_key_value = &style[prop];
101
102 let mut compiled_key_value_is_true = false;
103
104 if let FlatCompiledStylesValue::Bool(value) = compiled_key_value.as_ref()
105 && *value
106 {
107 compiled_key_value_is_true = true;
108 }
109
110 if !compiled_key_value_is_true {
111 let compiled_key_string_value = match compiled_key_value.as_ref() {
112 FlatCompiledStylesValue::String(strng) => strng.clone(),
113 other => {
114 let other_debug_info = format!("{:?}", other);
115 let variant_name =
116 other_debug_info.split("::").last().unwrap_or("unknown");
117
118 stylex_unimplemented!(
119 "String conversion not implemented for FlatCompiledStylesValue::{}",
120 variant_name
121 )
122 },
123 };
124
125 debug_string = if !debug_string.is_empty() {
126 format!("{}; {}", compiled_key_string_value, debug_string)
127 } else {
128 compiled_key_string_value
129 };
130 }
131
132 continue;
133 }
134
135 match value.as_ref() {
137 FlatCompiledStylesValue::String(_) | FlatCompiledStylesValue::Null => {
138 if !defined_properties.contains(prop) {
139 defined_properties.push(prop.clone());
140 if next_cache.is_some() {
141 defined_properties_chunk.push(prop.clone());
142 }
143
144 if let FlatCompiledStylesValue::String(value) = value.as_ref() {
145 class_name_chunk = if class_name_chunk.is_empty() {
146 value.to_string()
147 } else {
148 format!("{} {}", class_name_chunk, value)
149 };
150 }
151 }
154 },
155 _ => {
156 error!(
157 "styleq: {} typeof {:?} is not \"string\" or \"null\".",
158 prop, value
159 );
160 },
161 }
162 }
163
164 if let Some(ref mut cache) = next_cache {
166 let btree_map: BTreeMap<_, _> = style.iter().collect();
167 let style_hash = get_hash(btree_map);
168 cache.insert(
169 style_hash,
170 (
171 class_name_chunk.clone(),
172 defined_properties_chunk,
173 debug_string.clone(),
174 ),
175 );
176 }
177 }
178
179 if !class_name_chunk.is_empty() {
180 class_name = if class_name.is_empty() {
181 class_name_chunk
182 } else if !class_name.contains(class_name_chunk.as_str()) {
183 format!("{} {}", class_name_chunk, class_name)
184 } else {
185 class_name
186 };
187 }
188 } else {
189 stylex_panic!(
190 "styleq: {:#?} typeof {:?} is not \"string\" or \"null\".",
191 compiled_key,
192 "Bool"
193 )
194 }
195 } else {
196 let mut sub_style: Option<FlatCompiledStyles> = None;
198
199 for (prop, value) in style.iter() {
200 if !defined_properties.contains(prop) {
201 match value.as_ref() {
202 FlatCompiledStylesValue::Null => {
203 defined_properties.push(prop.clone());
206 },
207 _ => {
208 if sub_style.is_none() {
209 sub_style = Some(IndexMap::new());
210 }
211 if let Some(ref mut sub) = sub_style {
212 sub.insert(prop.clone(), Rc::new(value.as_ref().clone()));
213 }
214 defined_properties.push(prop.clone());
215 },
216 }
217 }
218 }
219
220 if let Some(sub) = sub_style {
221 inline_style = if let Some(existing) = inline_style {
224 let mut merged = sub;
225 for (k, v) in existing {
226 merged.entry(k).or_insert(v);
227 }
228 Some(merged)
229 } else {
230 Some(sub)
231 };
232 }
233
234 next_cache = None;
236 }
237 },
238 StyleObject::Nullable => {},
239 StyleObject::Other => {
240 stylex_panic!("Only compiled StyleX style objects are allowed in styleq().")
241 },
242 StyleObject::Unreachable => {
243 stylex_unreachable!(
244 "Encountered an unexpected style object variant in styleq processing."
245 )
246 },
247 },
248 _ => stylex_unreachable!("Unexpected ResolvedArg variant in styleq loop"),
249 };
250 }
251
252 StyleQResult {
253 class_name,
254 inline_style,
255 data_style_src: Some(debug_string),
256 }
257}