1use super::*;
2use omena_cascade::{
3 DomClassTokenizationV0, OrderedTokenWordV0, TokenSupportV0, tokenize_dom_class_attribute_v0,
4};
5use omena_query_core::{
6 AbstractClassValueV0, ClassBoundaryEffectV0, ExternalStringTypeFactsV0, FirstWitnessErrorV0,
7 GuardAtomV0, GuardedTokenInputV0, GuardedTokenLanguageV0, GuardedTokenMapInputV0,
8 GuardedTokenMapV0, StringTypeFactsV2, TokenObserverProjectionV0,
9 abstract_class_value_from_facts, abstract_class_value_kind, join_abstract_class_values,
10 top_class_value,
11};
12use omena_syntax::ident::CanonicalClassKeyV0;
13use serde::{Deserialize, Serialize, Serializer};
14use std::collections::{BTreeMap, BTreeSet, VecDeque};
15
16pub type OmenaQueryTsconfigPathMappingV0 = omena_resolver::OmenaResolverTsconfigPathMappingV0;
17pub type OmenaQueryBundlerPathAliasMappingV0 =
18 omena_resolver::OmenaResolverBundlerPathAliasMappingV0;
19pub type OmenaQueryStyleModuleDiskCandidateIdentityV0 =
20 omena_resolver::OmenaResolverStyleModuleDiskCandidateIdentityV0;
21
22#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
23#[serde(default, rename_all = "camelCase")]
24pub struct OmenaQueryStyleResolutionInputsV0 {
25 pub package_manifests: Vec<OmenaQueryStylePackageManifestV0>,
26 pub tsconfig_path_mappings: Vec<OmenaQueryTsconfigPathMappingV0>,
27 pub bundler_path_mappings: Vec<OmenaQueryBundlerPathAliasMappingV0>,
28 #[serde(skip_serializing_if = "Vec::is_empty")]
29 pub disk_style_path_identities: Vec<OmenaQueryStyleModuleDiskCandidateIdentityV0>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub external_sif_cache_fingerprint: Option<String>,
32}
33
34pub fn summarize_omena_query_source_import_declarations(
35 source: &str,
36) -> OmenaQuerySourceImportDeclarationSummaryV0 {
37 omena_bridge::summarize_omena_bridge_source_import_declarations(source)
38}
39
40pub fn summarize_omena_query_source_import_declarations_for_source_language(
41 source_path: &str,
42 source: &str,
43 source_language: Option<&str>,
44) -> OmenaQuerySourceImportDeclarationSummaryV0 {
45 omena_bridge::summarize_omena_bridge_source_import_declarations_for_source_language(
46 source_path,
47 source,
48 source_language,
49 )
50}
51
52pub fn resolve_omena_query_style_uri_for_specifier(
53 base_document_uri: &str,
54 workspace_folder_uri: Option<&str>,
55 specifier: &str,
56) -> Option<String> {
57 omena_bridge::resolve_omena_bridge_style_uri_for_specifier(
58 base_document_uri,
59 workspace_folder_uri,
60 specifier,
61 )
62}
63
64pub fn resolve_omena_query_style_uri_for_specifier_with_package_manifests(
65 base_document_uri: &str,
66 workspace_folder_uri: Option<&str>,
67 specifier: &str,
68 package_manifests: &[OmenaQueryStylePackageManifestV0],
69) -> Option<String> {
70 let resolver_package_manifests = package_manifests
71 .iter()
72 .map(|manifest| OmenaResolverStylePackageManifestV0 {
73 package_json_path: manifest.package_json_path.clone(),
74 package_json_source: manifest.package_json_source.clone(),
75 })
76 .collect::<Vec<_>>();
77 omena_bridge::resolve_omena_bridge_style_uri_for_specifier_with_package_manifests(
78 base_document_uri,
79 workspace_folder_uri,
80 specifier,
81 resolver_package_manifests.as_slice(),
82 )
83}
84
85pub fn resolve_omena_query_style_uri_for_specifier_with_resolution_inputs(
86 base_document_uri: &str,
87 workspace_folder_uri: Option<&str>,
88 specifier: &str,
89 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
90) -> Option<String> {
91 let bridge_inputs = omena_bridge::OmenaBridgeStyleResolutionInputsV0 {
92 package_manifests: resolution_inputs
93 .package_manifests
94 .iter()
95 .map(|manifest| OmenaResolverStylePackageManifestV0 {
96 package_json_path: manifest.package_json_path.clone(),
97 package_json_source: manifest.package_json_source.clone(),
98 })
99 .collect(),
100 tsconfig_path_mappings: resolution_inputs.tsconfig_path_mappings.clone(),
101 bundler_path_mappings: resolution_inputs.bundler_path_mappings.clone(),
102 disk_style_path_identities: resolution_inputs.disk_style_path_identities.clone(),
103 };
104 omena_bridge::resolve_omena_bridge_style_uri_for_specifier_with_resolution_inputs(
105 base_document_uri,
106 workspace_folder_uri,
107 specifier,
108 &bridge_inputs,
109 )
110}
111
112pub fn load_omena_query_workspace_style_resolution_inputs(
113 workspace_folder_uri: Option<&str>,
114 configured_package_manifests: &[OmenaQueryStylePackageManifestV0],
115) -> OmenaQueryStyleResolutionInputsV0 {
116 let resolver_package_manifests = configured_package_manifests
117 .iter()
118 .map(|manifest| OmenaResolverStylePackageManifestV0 {
119 package_json_path: manifest.package_json_path.clone(),
120 package_json_source: manifest.package_json_source.clone(),
121 })
122 .collect::<Vec<_>>();
123 let bridge_inputs = omena_bridge::load_omena_bridge_workspace_style_resolution_inputs(
124 workspace_folder_uri,
125 resolver_package_manifests.as_slice(),
126 );
127 OmenaQueryStyleResolutionInputsV0 {
128 package_manifests: bridge_inputs
129 .package_manifests
130 .into_iter()
131 .map(|manifest| OmenaQueryStylePackageManifestV0 {
132 package_json_path: manifest.package_json_path,
133 package_json_source: manifest.package_json_source,
134 })
135 .collect(),
136 tsconfig_path_mappings: bridge_inputs.tsconfig_path_mappings,
137 bundler_path_mappings: bridge_inputs.bundler_path_mappings,
138 disk_style_path_identities: bridge_inputs.disk_style_path_identities,
139 external_sif_cache_fingerprint: None,
140 }
141}
142
143#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
144#[serde(rename_all = "camelCase")]
145pub struct OmenaQueryBridgeExternalSifResolutionV0 {
146 pub external_sifs: Vec<OmenaQueryExternalSifInputV0>,
147 pub bridge_urls: Vec<String>,
148 pub generation_count: usize,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
152#[serde(rename_all = "camelCase")]
153pub enum OmenaQueryExternalSifTrustSourceV1 {
154 RecordedVerdict,
155 UnsignedLegacy,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
159#[serde(rename_all = "camelCase")]
160pub struct OmenaQueryExternalSifTrustV1 {
161 pub canonical_url: String,
162 pub trust_tier: omena_sif::OmenaSifTrustTierV1,
163 pub trust_source: OmenaQueryExternalSifTrustSourceV1,
164}
165
166#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
167#[serde(rename_all = "camelCase")]
168pub struct OmenaQueryBridgeExternalSifTrustedResolutionV1 {
169 pub resolution: OmenaQueryBridgeExternalSifResolutionV0,
170 pub trust_records: Vec<OmenaQueryExternalSifTrustV1>,
171}
172
173pub fn resolve_omena_query_bridge_external_sifs_for_style_sources(
174 style_sources: &[OmenaQueryStyleSourceInputV0],
175 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
176 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
177) -> OmenaQueryBridgeExternalSifResolutionV0 {
178 resolve_omena_query_bridge_external_sifs_for_style_sources_with_trust(
179 style_sources,
180 existing_external_sifs,
181 resolution_inputs,
182 )
183 .resolution
184}
185
186pub fn resolve_omena_query_bridge_external_sifs_for_style_sources_with_trust(
187 style_sources: &[OmenaQueryStyleSourceInputV0],
188 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
189 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
190) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
191 resolve_omena_query_bridge_external_sifs_for_style_sources_with_optional_cache_storage(
192 style_sources,
193 existing_external_sifs,
194 resolution_inputs,
195 None,
196 )
197}
198
199pub fn resolve_omena_query_bridge_external_sifs_for_style_sources_with_cache_storage(
200 style_sources: &[OmenaQueryStyleSourceInputV0],
201 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
202 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
203 cache_storage: &omena_bridge::OmenaBridgeExternalSifStorageV0,
204) -> OmenaQueryBridgeExternalSifResolutionV0 {
205 resolve_omena_query_bridge_external_sifs_for_style_sources_with_cache_storage_and_trust(
206 style_sources,
207 existing_external_sifs,
208 resolution_inputs,
209 cache_storage,
210 )
211 .resolution
212}
213
214pub fn resolve_omena_query_bridge_external_sifs_for_style_sources_with_cache_storage_and_trust(
215 style_sources: &[OmenaQueryStyleSourceInputV0],
216 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
217 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
218 cache_storage: &omena_bridge::OmenaBridgeExternalSifStorageV0,
219) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
220 resolve_omena_query_bridge_external_sifs_for_style_sources_with_optional_cache_storage(
221 style_sources,
222 existing_external_sifs,
223 resolution_inputs,
224 Some(cache_storage),
225 )
226}
227
228fn resolve_omena_query_bridge_external_sifs_for_style_sources_with_optional_cache_storage(
229 style_sources: &[OmenaQueryStyleSourceInputV0],
230 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
231 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
232 cache_storage: Option<&omena_bridge::OmenaBridgeExternalSifStorageV0>,
233) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
234 let seeds = style_sources
235 .iter()
236 .flat_map(|source| bridge_external_sif_seeds_for_style_source(source, resolution_inputs))
237 .collect::<BTreeSet<_>>();
238 resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_optional_cache_storage(
239 seeds.into_iter(),
240 existing_external_sifs,
241 resolution_inputs,
242 cache_storage,
243 )
244}
245
246pub fn resolve_omena_query_bridge_external_sifs_for_seed_pairs(
247 seeds: impl Iterator<Item = (String, String)>,
248 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
249 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
250) -> OmenaQueryBridgeExternalSifResolutionV0 {
251 resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_trust(
252 seeds,
253 existing_external_sifs,
254 resolution_inputs,
255 )
256 .resolution
257}
258
259pub fn resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_trust(
260 seeds: impl Iterator<Item = (String, String)>,
261 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
262 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
263) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
264 resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_optional_cache_storage(
265 seeds,
266 existing_external_sifs,
267 resolution_inputs,
268 None,
269 )
270}
271
272pub fn resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_cache_storage(
273 seeds: impl Iterator<Item = (String, String)>,
274 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
275 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
276 cache_storage: &omena_bridge::OmenaBridgeExternalSifStorageV0,
277) -> OmenaQueryBridgeExternalSifResolutionV0 {
278 resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_cache_storage_and_trust(
279 seeds,
280 existing_external_sifs,
281 resolution_inputs,
282 cache_storage,
283 )
284 .resolution
285}
286
287pub fn resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_cache_storage_and_trust(
288 seeds: impl Iterator<Item = (String, String)>,
289 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
290 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
291 cache_storage: &omena_bridge::OmenaBridgeExternalSifStorageV0,
292) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
293 resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_optional_cache_storage(
294 seeds,
295 existing_external_sifs,
296 resolution_inputs,
297 Some(cache_storage),
298 )
299}
300
301fn resolve_omena_query_bridge_external_sifs_for_seed_pairs_with_optional_cache_storage(
302 seeds: impl Iterator<Item = (String, String)>,
303 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
304 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
305 cache_storage: Option<&omena_bridge::OmenaBridgeExternalSifStorageV0>,
306) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
307 let mut state = BridgeExternalSifResolutionState::new(
308 existing_external_sifs,
309 resolution_inputs,
310 cache_storage,
311 );
312
313 for (verbatim_source, resolved_url) in seeds {
314 state.enqueue_alias(verbatim_source, resolved_url);
315 }
316
317 while let Some(sif) = state.worklist.pop_front() {
318 let base_file_uri = sif.canonical_url.clone();
319 for forward in &sif.exports.forwards {
320 let specifier = forward.canonical_url.as_str();
321 if !bridge_external_sif_specifier_is_readable(specifier) {
322 continue;
323 }
324 let Some(child_url) =
325 resolve_omena_query_style_uri_for_specifier_with_resolution_inputs(
326 base_file_uri.as_str(),
327 None,
328 specifier,
329 state.resolution_inputs,
330 )
331 .filter(|uri| uri.starts_with("file://"))
332 else {
333 continue;
334 };
335 let alias_key = if specifier.starts_with('.') || specifier.starts_with("file://") {
336 child_url.clone()
337 } else {
338 specifier.to_string()
339 };
340 state.enqueue_alias(alias_key, child_url);
341 }
342 }
343
344 state.into_resolution()
345}
346
347struct BridgeExternalSifResolutionState<'a> {
348 resolution_inputs: &'a OmenaQueryStyleResolutionInputsV0,
349 cache_storage: Option<&'a omena_bridge::OmenaBridgeExternalSifStorageV0>,
350 emitted_keys: BTreeSet<String>,
351 generated_by_resolved_url: BTreeMap<String, omena_sif::OmenaSifV1>,
352 bridge_urls: BTreeSet<String>,
353 external_sifs: Vec<OmenaQueryExternalSifInputV0>,
354 trust_records: BTreeMap<String, OmenaQueryExternalSifTrustV1>,
355 worklist: VecDeque<omena_sif::OmenaSifV1>,
356 generation_count: usize,
357}
358
359impl<'a> BridgeExternalSifResolutionState<'a> {
360 fn new(
361 existing_external_sifs: &[OmenaQueryExternalSifInputV0],
362 resolution_inputs: &'a OmenaQueryStyleResolutionInputsV0,
363 cache_storage: Option<&'a omena_bridge::OmenaBridgeExternalSifStorageV0>,
364 ) -> Self {
365 Self {
366 resolution_inputs,
367 cache_storage,
368 emitted_keys: existing_external_sifs
369 .iter()
370 .flat_map(|input| [input.canonical_url.clone(), input.sif.canonical_url.clone()])
371 .collect(),
372 generated_by_resolved_url: existing_external_sifs
373 .iter()
374 .map(|input| (input.sif.canonical_url.clone(), input.sif.clone()))
375 .collect(),
376 bridge_urls: BTreeSet::new(),
377 external_sifs: Vec::new(),
378 trust_records: BTreeMap::new(),
379 worklist: VecDeque::new(),
380 generation_count: 0,
381 }
382 }
383
384 fn into_resolution(self) -> OmenaQueryBridgeExternalSifTrustedResolutionV1 {
385 OmenaQueryBridgeExternalSifTrustedResolutionV1 {
386 resolution: OmenaQueryBridgeExternalSifResolutionV0 {
387 external_sifs: self.external_sifs,
388 bridge_urls: self.bridge_urls.into_iter().collect(),
389 generation_count: self.generation_count,
390 },
391 trust_records: self.trust_records.into_values().collect(),
392 }
393 }
394
395 fn enqueue_alias(&mut self, alias_key: String, resolved_url: String) {
396 if self.emitted_keys.contains(alias_key.as_str()) {
397 return;
398 }
399 self.bridge_urls.insert(alias_key.clone());
400 self.bridge_urls.insert(resolved_url.clone());
401 if let Some(sif) = self
402 .generated_by_resolved_url
403 .get(resolved_url.as_str())
404 .cloned()
405 {
406 self.emitted_keys.insert(alias_key.clone());
407 self.emitted_keys.insert(sif.canonical_url.clone());
408 self.external_sifs.push(OmenaQueryExternalSifInputV0 {
409 canonical_url: alias_key,
410 sif,
411 });
412 return;
413 }
414 let cache_context = omena_bridge::OmenaBridgeExternalSifCacheContextV0 {
415 freshness_fingerprint: self
416 .resolution_inputs
417 .external_sif_cache_fingerprint
418 .clone(),
419 };
420 let result = if alias_key.starts_with("pkg:") {
421 omena_bridge::generate_omena_bridge_sif_for_resolved_style_path_with_canonical_url_cache_context_storage_and_trust(
422 resolved_url.as_str(),
423 alias_key.as_str(),
424 &cache_context,
425 self.cache_storage,
426 )
427 } else {
428 omena_bridge::generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
429 resolved_url.as_str(),
430 &cache_context,
431 self.cache_storage,
432 )
433 };
434 let Ok(result) = result else {
435 return;
436 };
437 let sif = result.sif;
438 let trust_source = match result.trust_source {
439 omena_bridge::OmenaBridgeExternalSifTrustSourceV1::RecordedVerdict => {
440 OmenaQueryExternalSifTrustSourceV1::RecordedVerdict
441 }
442 omena_bridge::OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy => {
443 OmenaQueryExternalSifTrustSourceV1::UnsignedLegacy
444 }
445 };
446 self.trust_records.insert(
447 sif.canonical_url.clone(),
448 OmenaQueryExternalSifTrustV1 {
449 canonical_url: sif.canonical_url.clone(),
450 trust_tier: result.trust_envelope.trust_tier,
451 trust_source,
452 },
453 );
454 self.generation_count = self.generation_count.saturating_add(1);
455 self.generated_by_resolved_url
456 .insert(sif.canonical_url.clone(), sif.clone());
457 self.emitted_keys.insert(alias_key.clone());
458 self.emitted_keys.insert(sif.canonical_url.clone());
459 self.bridge_urls.insert(sif.canonical_url.clone());
460 self.worklist.push_back(sif.clone());
461 self.external_sifs.push(OmenaQueryExternalSifInputV0 {
462 canonical_url: alias_key,
463 sif,
464 });
465 }
466}
467
468fn bridge_external_sif_seeds_for_style_source(
469 source: &OmenaQueryStyleSourceInputV0,
470 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
471) -> Vec<(String, String)> {
472 let Some(module_sources) =
473 summarize_omena_query_sass_module_sources(&source.style_path, &source.style_source)
474 else {
475 return Vec::new();
476 };
477 let base_uri = style_source_path_as_file_uri(source.style_path.as_str());
478 module_sources
479 .module_use_edges
480 .iter()
481 .map(|edge| edge.source.as_str())
482 .chain(
483 module_sources
484 .module_forward_sources
485 .iter()
486 .map(String::as_str),
487 )
488 .filter_map(|specifier| {
489 if !bridge_external_sif_specifier_is_readable(specifier) {
490 return None;
491 }
492 let resolved_url = if specifier.starts_with("file://") {
493 specifier.to_string()
494 } else {
495 resolve_omena_query_style_uri_for_specifier_with_resolution_inputs(
496 base_uri.as_str(),
497 None,
498 specifier,
499 resolution_inputs,
500 )?
501 };
502 resolved_url
503 .starts_with("file://")
504 .then(|| (specifier.to_string(), resolved_url))
505 })
506 .collect()
507}
508
509fn bridge_external_sif_specifier_is_readable(specifier: &str) -> bool {
510 !specifier.starts_with("sass:")
511 && !specifier.starts_with("http://")
512 && !specifier.starts_with("https://")
513}
514
515fn style_source_path_as_file_uri(path: &str) -> String {
516 if path.starts_with("file://") {
517 path.to_string()
518 } else {
519 format!("file://{path}")
520 }
521}
522
523pub fn summarize_omena_query_source_syntax_index(
524 source: &str,
525 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
526 classnames_bind_bindings: Vec<String>,
527) -> OmenaQuerySourceSyntaxIndexV0 {
528 omena_bridge::summarize_omena_bridge_source_syntax_index(
529 source,
530 imported_style_bindings,
531 classnames_bind_bindings,
532 )
533}
534
535pub fn summarize_omena_query_source_syntax_index_with_type_fact_attempts(
536 source: &str,
537 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
538 classnames_bind_bindings: Vec<String>,
539) -> OmenaQuerySourceSyntaxIndexWithTypeFactAttemptsV0 {
540 omena_bridge::summarize_omena_bridge_source_syntax_index_with_type_fact_attempts(
541 source,
542 imported_style_bindings,
543 classnames_bind_bindings,
544 )
545}
546
547pub fn summarize_omena_query_source_syntax_index_for_source_language(
548 source_path: &str,
549 source: &str,
550 source_language: Option<&str>,
551 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
552 classnames_bind_bindings: Vec<String>,
553) -> OmenaQuerySourceSyntaxIndexV0 {
554 omena_bridge::summarize_omena_bridge_source_syntax_index_for_source_language(
555 source_path,
556 source,
557 source_language,
558 imported_style_bindings,
559 classnames_bind_bindings,
560 )
561}
562
563pub fn summarize_omena_query_source_syntax_index_for_source_language_with_type_fact_attempts(
564 source_path: &str,
565 source: &str,
566 source_language: Option<&str>,
567 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
568 classnames_bind_bindings: Vec<String>,
569) -> OmenaQuerySourceSyntaxIndexWithTypeFactAttemptsV0 {
570 omena_bridge::summarize_omena_bridge_source_syntax_index_for_source_language_with_type_fact_attempts(
571 source_path,
572 source,
573 source_language,
574 imported_style_bindings,
575 classnames_bind_bindings,
576 )
577}
578
579pub fn summarize_omena_query_source_binding_index(
580 source: &str,
581 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
582 classnames_bind_bindings: Vec<String>,
583) -> OmenaQuerySourceBindingIndexV0 {
584 omena_bridge::summarize_omena_bridge_source_binding_index(
585 source,
586 imported_style_bindings,
587 classnames_bind_bindings,
588 )
589}
590
591pub fn summarize_omena_query_source_binding_index_for_source_language(
592 source_path: &str,
593 source: &str,
594 source_language: Option<&str>,
595 imported_style_bindings: Vec<OmenaQuerySourceImportedStyleBindingV0>,
596 classnames_bind_bindings: Vec<String>,
597) -> OmenaQuerySourceBindingIndexV0 {
598 omena_bridge::summarize_omena_bridge_source_binding_index_for_source_language(
599 source_path,
600 source,
601 source_language,
602 imported_style_bindings,
603 classnames_bind_bindings,
604 )
605}
606
607pub fn summarize_omena_query_source_control_flow_graph_for_source_language(
608 source_path: &str,
609 source: &str,
610 source_language: Option<&str>,
611 variable_name: &str,
612 reference_byte_offset: usize,
613) -> Option<crate::OmenaQuerySourceControlFlowGraphCaptureV0> {
614 omena_bridge::summarize_omena_bridge_source_control_flow_graph_for_source_language(
615 source_path,
616 source,
617 source_language,
618 variable_name,
619 reference_byte_offset,
620 )
621}
622
623#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
624#[serde(rename_all = "camelCase")]
625pub struct OmenaQuerySourcePrecisionReferenceV0 {
626 pub schema_version: &'static str,
627 pub product: &'static str,
628 pub source_path: String,
629 #[serde(skip_serializing_if = "Option::is_none")]
630 pub source_language: Option<String>,
631 pub variable_name: String,
632 pub reference_byte_offset: usize,
633 pub resolved_tier: &'static str,
634 pub resolved_value: AbstractClassValueV0,
635 pub precision: OmenaQueryAnalysisPrecisionV0,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub top_cause: Option<&'static str>,
638}
639
640pub fn resolve_omena_query_source_precision_for_source(
641 source_path: &str,
642 source: &str,
643 source_language: Option<&str>,
644 variable_name: &str,
645 reference_byte_offset: usize,
646) -> OmenaQuerySourcePrecisionReferenceV0 {
647 let precision =
648 source_diagnostic_precision("classValueResolution", "sourceControlFlow", "sameFile");
649 let Some(capture) = summarize_omena_query_source_control_flow_graph_for_source_language(
650 source_path,
651 source,
652 source_language,
653 variable_name,
654 reference_byte_offset,
655 ) else {
656 return source_precision_reference(
657 source_path,
658 source_language,
659 variable_name,
660 reference_byte_offset,
661 top_class_value(),
662 precision,
663 Some("noFlowCapture"),
664 );
665 };
666
667 let resolved_flow = resolve_source_precision_flow_from_snapshot(
668 &capture.snapshot,
669 capture.binding.symbol_ordinal,
670 )
671 .unwrap_or(ResolvedSourcePrecisionFlowV0 {
672 value: top_class_value(),
673 top_cause: Some("ambiguousFlowSnapshot"),
674 });
675 let top_cause = if abstract_class_value_kind(&resolved_flow.value) == "top" {
676 resolved_flow.top_cause
677 } else {
678 None
679 };
680
681 source_precision_reference(
682 source_path,
683 source_language,
684 variable_name,
685 reference_byte_offset,
686 resolved_flow.value,
687 precision,
688 top_cause,
689 )
690}
691
692#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
693#[serde(rename_all = "camelCase")]
694pub enum OmenaQueryClassSitePlaneV0 {
695 Cfg,
696 TypeFact,
697 Joined,
698}
699
700#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
701#[serde(rename_all = "camelCase")]
702pub enum OmenaQueryClassSiteUnknownCauseV0 {
703 SiteNotEnumerated,
704 SourceValueUnavailable,
705 NonFiniteRawLanguage,
706 TypeFactNotProvided,
707 TypeFactRefused,
708}
709
710#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
711#[serde(rename_all = "camelCase")]
712pub struct OmenaQueryClassSiteTypeFactInputV0 {
713 pub site_byte_span: ParserByteSpanV0,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub facts: Option<StringTypeFactsV2>,
716 #[serde(skip_serializing_if = "Option::is_none")]
717 pub refusal_cause: Option<String>,
718}
719
720#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
721#[serde(rename_all = "camelCase")]
722pub struct OmenaQueryClassSiteTokenProvenanceV0 {
723 pub token: OmenaQueryCanonicalClassTokenV0,
724 pub must: bool,
725 pub may: bool,
726 pub planes: Vec<OmenaQueryClassSitePlaneV0>,
727 pub boundary_provenance: Vec<String>,
728 #[serde(skip_serializing_if = "Vec::is_empty")]
729 pub guard_conditions: Vec<String>,
730}
731
732#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
733pub struct OmenaQueryCanonicalClassTokenV0(CanonicalClassKeyV0);
734
735impl OmenaQueryCanonicalClassTokenV0 {
736 pub fn as_str(&self) -> &str {
737 self.0.as_str()
738 }
739}
740
741impl Serialize for OmenaQueryCanonicalClassTokenV0 {
742 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
743 where
744 S: Serializer,
745 {
746 serializer.serialize_str(self.as_str())
747 }
748}
749
750#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
751#[serde(rename_all = "camelCase")]
752pub struct OmenaQueryClassSiteValueV0 {
753 pub schema_version: &'static str,
754 pub product: &'static str,
755 pub source_path: String,
756 #[serde(skip_serializing_if = "Option::is_none")]
757 pub source_language: Option<String>,
758 pub attribute_name: String,
759 pub site_byte_span: ParserByteSpanV0,
760 #[serde(skip_serializing_if = "Option::is_none")]
761 pub value_byte_span: Option<ParserByteSpanV0>,
762 #[serde(skip_serializing_if = "Option::is_none")]
763 pub raw_value: Option<String>,
764 pub target_style_uris: Vec<String>,
765 #[serde(skip_serializing_if = "Option::is_none")]
766 pub ordered_word: Option<OrderedTokenWordV0>,
767 #[serde(skip_serializing_if = "Option::is_none")]
768 pub support: Option<TokenSupportV0>,
769 pub token_provenance: Vec<OmenaQueryClassSiteTokenProvenanceV0>,
770 pub contributing_planes: Vec<OmenaQueryClassSitePlaneV0>,
771 pub precision_tier: &'static str,
772 #[serde(skip_serializing_if = "Option::is_none")]
773 pub unknown_cause: Option<OmenaQueryClassSiteUnknownCauseV0>,
774 #[serde(skip_serializing_if = "Option::is_none")]
775 pub type_fact_cause: Option<String>,
776}
777
778#[derive(Debug, Clone, PartialEq, Eq)]
779struct ClassSitePlaneProjectionV0 {
780 ordered_word: Option<OrderedTokenWordV0>,
781 support: TokenSupportV0,
782 precision_tier: &'static str,
783}
784
785pub fn resolve_omena_query_class_site_values_for_source(
786 source_path: &str,
787 source: &str,
788 source_language: Option<&str>,
789) -> Vec<OmenaQueryClassSiteValueV0> {
790 resolve_omena_query_class_site_values_for_source_with_type_facts(
791 source_path,
792 source,
793 source_language,
794 &[],
795 )
796}
797
798pub fn resolve_omena_query_class_site_values_for_source_with_type_facts(
799 source_path: &str,
800 source: &str,
801 source_language: Option<&str>,
802 type_facts: &[OmenaQueryClassSiteTypeFactInputV0],
803) -> Vec<OmenaQueryClassSiteValueV0> {
804 let imports = summarize_omena_query_source_import_declarations_for_source_language(
805 source_path,
806 source,
807 source_language,
808 );
809 let base_document_uri = style_source_path_as_file_uri(source_path);
810 let imported_style_bindings = imports
811 .imports
812 .iter()
813 .filter(|import| source_specifier_is_style_module(&import.specifier))
814 .filter_map(|import| {
815 resolve_omena_query_style_uri_for_specifier(&base_document_uri, None, &import.specifier)
816 .map(|style_uri| OmenaQuerySourceImportedStyleBindingV0 {
817 binding: import.binding.clone(),
818 style_uri,
819 })
820 })
821 .collect::<Vec<_>>();
822 let classnames_bindings = imports
823 .imports
824 .iter()
825 .filter(|import| import.specifier == "classnames/bind")
826 .map(|import| import.binding.clone())
827 .collect::<Vec<_>>();
828 let binding_index = summarize_omena_query_source_binding_index_for_source_language(
829 source_path,
830 source,
831 source_language,
832 imported_style_bindings,
833 classnames_bindings,
834 );
835 binding_index
836 .class_attribute_sites
837 .iter()
838 .map(|site| {
839 let type_fact = type_facts
840 .iter()
841 .find(|fact| fact.site_byte_span == site.site_byte_span);
842 class_site_value_from_binding_fact(
843 source_path,
844 source,
845 source_language,
846 site,
847 type_fact,
848 )
849 })
850 .collect()
851}
852
853pub fn resolve_omena_query_class_site_value_for_source(
854 source_path: &str,
855 source: &str,
856 source_language: Option<&str>,
857 site_byte_span: ParserByteSpanV0,
858) -> Option<OmenaQueryClassSiteValueV0> {
859 resolve_omena_query_class_site_values_for_source(source_path, source, source_language)
860 .into_iter()
861 .find(|site| site.site_byte_span == site_byte_span)
862}
863
864pub fn build_omena_query_guarded_token_map_for_site(
865 site: &OmenaQueryClassSiteValueV0,
866) -> Result<GuardedTokenMapV0, FirstWitnessErrorV0> {
867 let mut tokens = Vec::new();
868 for provenance in &site.token_provenance {
869 let token = GuardedTokenLanguageV0::concrete(provenance.token.as_str());
870 let observers = TokenObserverProjectionV0::exact(&token);
871 if provenance.guard_conditions.is_empty() {
872 tokens.push(GuardedTokenInputV0 {
873 token,
874 guards: Vec::new(),
875 observers,
876 });
877 continue;
878 }
879 tokens.extend(
880 provenance
881 .guard_conditions
882 .iter()
883 .map(|condition| GuardedTokenInputV0 {
884 token: token.clone(),
885 guards: vec![guard_atom_from_source_condition(condition)],
886 observers: observers.clone(),
887 }),
888 );
889 }
890 if tokens.is_empty()
891 && let Some(raw_language) = site
892 .raw_value
893 .as_ref()
894 .filter(|raw| raw.starts_with('`') && raw.contains("${"))
895 {
896 let token = GuardedTokenLanguageV0::symbolic(raw_language.clone());
897 tokens.push(GuardedTokenInputV0 {
898 observers: TokenObserverProjectionV0::exact(&token),
899 token,
900 guards: Vec::new(),
901 });
902 }
903 GuardedTokenMapV0::build(GuardedTokenMapInputV0 {
904 tokens,
905 site_usage_guards: Vec::new(),
906 })
907}
908
909fn guard_atom_from_source_condition(condition: &str) -> GuardAtomV0 {
910 let condition = condition.trim();
911 let negated = condition
912 .strip_prefix("!(")
913 .and_then(|condition| condition.strip_suffix(')'));
914 GuardAtomV0 {
915 atom: negated.unwrap_or(condition).trim().to_string(),
916 polarity: negated.is_none(),
917 }
918}
919
920fn class_site_value_from_binding_fact(
921 source_path: &str,
922 source: &str,
923 source_language: Option<&str>,
924 site: &crate::OmenaQuerySourceClassAttributeSiteFactV0,
925 type_fact: Option<&OmenaQueryClassSiteTypeFactInputV0>,
926) -> OmenaQueryClassSiteValueV0 {
927 let cfg_projection = site
928 .source_facts
929 .as_ref()
930 .and_then(|facts| class_site_projection_from_values(facts.values.as_deref()));
931 let type_projection = type_fact
932 .and_then(|input| input.facts.as_ref())
933 .and_then(|facts| class_site_projection_from_values(facts.values.as_deref()));
934
935 let mut must = BTreeSet::new();
936 let mut may = BTreeSet::new();
937 let mut contributing_planes = Vec::new();
938 for (plane, projection) in [
939 (OmenaQueryClassSitePlaneV0::Cfg, cfg_projection.as_ref()),
940 (
941 OmenaQueryClassSitePlaneV0::TypeFact,
942 type_projection.as_ref(),
943 ),
944 ] {
945 if let Some(projection) = projection {
946 contributing_planes.push(plane);
947 must.extend(projection.support.must().iter().cloned());
948 may.extend(projection.support.may().iter().cloned());
949 }
950 }
951 let mut guard_conditions_by_token = BTreeMap::<CanonicalClassKeyV0, Vec<String>>::new();
952 for guarded in &site.guarded_tokens {
953 if let DomClassTokenizationV0::Known { word, .. } =
954 tokenize_dom_class_attribute_v0(Some(&guarded.token))
955 {
956 for token in word.tokens() {
957 may.insert(token.clone());
958 guard_conditions_by_token
959 .entry(token.clone())
960 .or_default()
961 .push(guarded.condition.clone());
962 }
963 }
964 }
965 if !site.guarded_tokens.is_empty()
966 && !contributing_planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
967 {
968 contributing_planes.push(OmenaQueryClassSitePlaneV0::Cfg);
969 }
970 if contributing_planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
971 && contributing_planes.contains(&OmenaQueryClassSitePlaneV0::TypeFact)
972 {
973 contributing_planes.push(OmenaQueryClassSitePlaneV0::Joined);
974 }
975 let support = (!may.is_empty())
976 .then(|| TokenSupportV0::new(must.iter().cloned(), may.iter().cloned()))
977 .flatten();
978 let ordered_word = match (cfg_projection.as_ref(), type_projection.as_ref()) {
979 (Some(cfg), Some(type_fact)) if cfg.ordered_word == type_fact.ordered_word => {
980 cfg.ordered_word.clone()
981 }
982 (Some(cfg), None) => cfg.ordered_word.clone(),
983 (None, Some(type_fact)) => type_fact.ordered_word.clone(),
984 _ => site.ordered_word.clone(),
985 };
986 let mut token_provenance = may
987 .iter()
988 .map(|token| {
989 let mut planes = [
990 (OmenaQueryClassSitePlaneV0::Cfg, cfg_projection.as_ref()),
991 (
992 OmenaQueryClassSitePlaneV0::TypeFact,
993 type_projection.as_ref(),
994 ),
995 ]
996 .into_iter()
997 .filter_map(|(plane, projection)| {
998 projection
999 .is_some_and(|projection| projection.support.may().contains(token))
1000 .then_some(plane)
1001 })
1002 .collect::<Vec<_>>();
1003 if guard_conditions_by_token.contains_key(token)
1004 && !planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
1005 {
1006 planes.push(OmenaQueryClassSitePlaneV0::Cfg);
1007 }
1008 if planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
1009 && planes.contains(&OmenaQueryClassSitePlaneV0::TypeFact)
1010 {
1011 planes.push(OmenaQueryClassSitePlaneV0::Joined);
1012 }
1013 let mut boundary_provenance = Vec::new();
1014 if planes.contains(&OmenaQueryClassSitePlaneV0::Cfg) {
1015 boundary_provenance.push(class_boundary_effect_label(site.boundary_effect));
1016 }
1017 if guard_conditions_by_token.contains_key(token) {
1018 boundary_provenance.push("guardedClassToken".to_string());
1019 }
1020 OmenaQueryClassSiteTokenProvenanceV0 {
1021 token: OmenaQueryCanonicalClassTokenV0(token.clone()),
1022 must: must.contains(token),
1023 may: true,
1024 planes,
1025 boundary_provenance,
1026 guard_conditions: guard_conditions_by_token
1027 .get(token)
1028 .cloned()
1029 .unwrap_or_default(),
1030 }
1031 })
1032 .collect::<Vec<_>>();
1033 token_provenance.sort_by(|left, right| left.token.cmp(&right.token));
1034
1035 let precision_tier = cfg_projection
1036 .as_ref()
1037 .map(|projection| projection.precision_tier)
1038 .or_else(|| {
1039 type_projection
1040 .as_ref()
1041 .map(|projection| projection.precision_tier)
1042 })
1043 .or_else(|| (!site.guarded_tokens.is_empty()).then_some("finiteSet"))
1044 .unwrap_or("top");
1045 let unknown_cause = if support.is_some() && !may.is_empty() {
1046 None
1047 } else if site.source_facts.is_some()
1048 || type_fact.and_then(|input| input.facts.as_ref()).is_some()
1049 {
1050 Some(OmenaQueryClassSiteUnknownCauseV0::NonFiniteRawLanguage)
1051 } else {
1052 Some(OmenaQueryClassSiteUnknownCauseV0::SourceValueUnavailable)
1053 };
1054 let type_fact_cause = match type_fact {
1055 None => Some("typeFactNotProvided".to_string()),
1056 Some(input) if input.facts.is_none() => Some(
1057 input
1058 .refusal_cause
1059 .clone()
1060 .unwrap_or_else(|| "typeFactRefused".to_string()),
1061 ),
1062 Some(_) => None,
1063 };
1064
1065 OmenaQueryClassSiteValueV0 {
1066 schema_version: "0",
1067 product: "omena-query.class-site-value",
1068 source_path: source_path.to_string(),
1069 source_language: source_language.map(str::to_string),
1070 attribute_name: site.attribute_name.clone(),
1071 site_byte_span: site.site_byte_span,
1072 value_byte_span: site.value_byte_span,
1073 raw_value: site
1074 .value_byte_span
1075 .and_then(|span| source.get(span.start..span.end))
1076 .map(str::to_string),
1077 target_style_uris: site.target_style_uris.clone(),
1078 ordered_word,
1079 support,
1080 token_provenance,
1081 contributing_planes,
1082 precision_tier,
1083 unknown_cause,
1084 type_fact_cause,
1085 }
1086}
1087
1088fn class_site_projection_from_values(
1089 values: Option<&[String]>,
1090) -> Option<ClassSitePlaneProjectionV0> {
1091 let values = values?;
1092 if values.is_empty() {
1093 return None;
1094 }
1095 let mut words = Vec::with_capacity(values.len());
1096 for value in values {
1097 let DomClassTokenizationV0::Known { word, .. } =
1098 tokenize_dom_class_attribute_v0(Some(value))
1099 else {
1100 return None;
1101 };
1102 words.push(word);
1103 }
1104 let may = words
1105 .iter()
1106 .flat_map(|word| word.tokens().iter().cloned())
1107 .collect::<BTreeSet<_>>();
1108 let mut must = words
1109 .first()
1110 .map(|word| word.tokens().iter().cloned().collect::<BTreeSet<_>>())?;
1111 for word in words.iter().skip(1) {
1112 let word = word.tokens().iter().cloned().collect::<BTreeSet<_>>();
1113 must = must.intersection(&word).cloned().collect();
1114 }
1115 let ordered_word = words
1116 .iter()
1117 .all(|word| word == &words[0])
1118 .then(|| words[0].clone());
1119 Some(ClassSitePlaneProjectionV0 {
1120 ordered_word,
1121 support: TokenSupportV0::new(must, may)?,
1122 precision_tier: if values.len() == 1 {
1123 "exact"
1124 } else {
1125 "finiteSet"
1126 },
1127 })
1128}
1129
1130fn class_boundary_effect_label(effect: ClassBoundaryEffectV0) -> String {
1131 match effect {
1132 ClassBoundaryEffectV0::ConcatInsideToken => "concatInsideToken",
1133 ClassBoundaryEffectV0::ConcatAtTokenBoundary => "concatAtTokenBoundary",
1134 ClassBoundaryEffectV0::UnknownBoundary => "unknownBoundary",
1135 }
1136 .to_string()
1137}
1138
1139fn source_specifier_is_style_module(specifier: &str) -> bool {
1140 [".css", ".scss", ".sass", ".less"]
1141 .iter()
1142 .any(|extension| specifier.ends_with(extension))
1143}
1144
1145#[derive(Clone, PartialEq, Eq)]
1146struct ResolvedSourcePrecisionFlowV0 {
1147 value: AbstractClassValueV0,
1148 top_cause: Option<&'static str>,
1149}
1150
1151fn resolve_source_precision_flow_from_snapshot(
1152 snapshot: &crate::OmenaQuerySourceFlowBlockGraphSnapshotV0,
1153 symbol_ordinal: usize,
1154) -> Option<ResolvedSourcePrecisionFlowV0> {
1155 let predecessors = source_precision_predecessor_block_ids(&snapshot.blocks);
1156 let mut states = snapshot
1157 .blocks
1158 .iter()
1159 .map(|block| (block.id.clone(), None::<ResolvedSourcePrecisionFlowV0>))
1160 .collect::<BTreeMap<_, _>>();
1161
1162 for _ in 0..std::cmp::max(snapshot.blocks.len() * 2, 1) {
1163 let mut changed = false;
1164 for block in &snapshot.blocks {
1165 let incoming = source_precision_incoming_state(block, &predecessors, &states);
1166 let next = apply_source_precision_block(block, symbol_ordinal, incoming);
1167 if states.get(&block.id).and_then(Clone::clone) != next {
1168 states.insert(block.id.clone(), next);
1169 changed = true;
1170 }
1171 }
1172 if !changed {
1173 break;
1174 }
1175 }
1176
1177 let exit = snapshot
1178 .blocks
1179 .iter()
1180 .find(|block| block.id == "exit")
1181 .or_else(|| snapshot.blocks.last())?;
1182 states.get(&exit.id).and_then(Clone::clone)
1183}
1184
1185fn source_precision_predecessor_block_ids(
1186 blocks: &[crate::OmenaQuerySourceFlowBlockSnapshotV0],
1187) -> BTreeMap<String, Vec<String>> {
1188 let mut predecessors = BTreeMap::<String, Vec<String>>::new();
1189 for block in blocks {
1190 for successor in &block.successor_block_ids {
1191 predecessors
1192 .entry(successor.clone())
1193 .or_default()
1194 .push(block.id.clone());
1195 }
1196 }
1197 predecessors
1198}
1199
1200fn source_precision_incoming_state(
1201 block: &crate::OmenaQuerySourceFlowBlockSnapshotV0,
1202 predecessors: &BTreeMap<String, Vec<String>>,
1203 states: &BTreeMap<String, Option<ResolvedSourcePrecisionFlowV0>>,
1204) -> Option<ResolvedSourcePrecisionFlowV0> {
1205 predecessors
1206 .get(&block.id)
1207 .into_iter()
1208 .flat_map(|ids| ids.iter())
1209 .filter_map(|id| states.get(id).and_then(Clone::clone))
1210 .reduce(join_source_precision_flows)
1211}
1212
1213fn apply_source_precision_block(
1214 block: &crate::OmenaQuerySourceFlowBlockSnapshotV0,
1215 symbol_ordinal: usize,
1216 incoming: Option<ResolvedSourcePrecisionFlowV0>,
1217) -> Option<ResolvedSourcePrecisionFlowV0> {
1218 if block.symbol_ordinal != Some(symbol_ordinal)
1219 || !matches!(block.transfer_kind, "assignFacts" | "concatFacts")
1220 {
1221 return incoming;
1222 }
1223
1224 let Some(facts) = block.facts.as_ref() else {
1225 return Some(ResolvedSourcePrecisionFlowV0 {
1226 value: top_class_value(),
1227 top_cause: Some("missingValueFacts"),
1228 });
1229 };
1230
1231 let external_facts = ExternalStringTypeFactsV0 {
1232 kind: facts.kind.clone(),
1233 constraint_kind: facts.constraint_kind.clone(),
1234 values: facts.values.clone(),
1235 prefix: facts.prefix.clone(),
1236 suffix: facts.suffix.clone(),
1237 min_len: facts.min_len,
1238 max_len: facts.max_len,
1239 char_must: facts.char_must.clone(),
1240 char_may: facts.char_may.clone(),
1241 may_include_other_chars: facts.may_include_other_chars,
1242 };
1243
1244 Some(ResolvedSourcePrecisionFlowV0 {
1245 value: abstract_class_value_from_facts(&external_facts),
1246 top_cause: None,
1247 })
1248}
1249
1250fn join_source_precision_flows(
1251 left: ResolvedSourcePrecisionFlowV0,
1252 right: ResolvedSourcePrecisionFlowV0,
1253) -> ResolvedSourcePrecisionFlowV0 {
1254 let value = join_abstract_class_values(&left.value, &right.value);
1255 let top_cause = if abstract_class_value_kind(&value) == "top" {
1256 left.top_cause.or(right.top_cause).or(Some("joinedTop"))
1257 } else {
1258 None
1259 };
1260 ResolvedSourcePrecisionFlowV0 { value, top_cause }
1261}
1262
1263fn source_precision_reference(
1264 source_path: &str,
1265 source_language: Option<&str>,
1266 variable_name: &str,
1267 reference_byte_offset: usize,
1268 resolved_value: AbstractClassValueV0,
1269 precision: OmenaQueryAnalysisPrecisionV0,
1270 top_cause: Option<&'static str>,
1271) -> OmenaQuerySourcePrecisionReferenceV0 {
1272 let resolved_tier = abstract_class_value_kind(&resolved_value);
1273 OmenaQuerySourcePrecisionReferenceV0 {
1274 schema_version: "0",
1275 product: "omena-query.source-precision-reference",
1276 source_path: source_path.to_string(),
1277 source_language: source_language.map(str::to_string),
1278 variable_name: variable_name.to_string(),
1279 reference_byte_offset,
1280 resolved_tier,
1281 resolved_value,
1282 precision,
1283 top_cause,
1284 }
1285}
1286
1287pub fn summarize_omena_query_source_type_fact_control_flow_graph_for_source_language(
1288 source_path: &str,
1289 source: &str,
1290 source_language: Option<&str>,
1291 variable_name: &str,
1292 reference_byte_offset: usize,
1293) -> Option<crate::OmenaQuerySourceTypeFactControlFlowGraphV0> {
1294 omena_bridge::summarize_omena_bridge_source_type_fact_control_flow_graph_for_source_language(
1295 source_path,
1296 source,
1297 source_language,
1298 variable_name,
1299 reference_byte_offset,
1300 )
1301}
1302
1303pub fn collect_omena_query_vue_style_module_bindings(
1304 source_path: &str,
1305 source: &str,
1306 source_language: Option<&str>,
1307) -> Vec<String> {
1308 omena_bridge::collect_omena_bridge_vue_style_module_bindings(
1309 source_path,
1310 source,
1311 source_language,
1312 )
1313}
1314
1315pub fn canonicalize_omena_query_source_selector_references(
1316 references: &mut Vec<OmenaQuerySourceSelectorReferenceFactV0>,
1317) {
1318 omena_bridge::canonicalize_source_selector_references(references);
1319}