Skip to main content

omena_query/
source.rs

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}