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    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
526) -> OmenaQuerySourceSyntaxIndexV0 {
527    omena_bridge::summarize_omena_bridge_source_syntax_index(source, style_import_resolutions)
528}
529
530pub fn summarize_omena_query_source_syntax_index_with_type_fact_attempts(
531    source: &str,
532    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
533) -> OmenaQuerySourceSyntaxIndexWithTypeFactAttemptsV0 {
534    omena_bridge::summarize_omena_bridge_source_syntax_index_with_type_fact_attempts(
535        source,
536        style_import_resolutions,
537    )
538}
539
540pub fn summarize_omena_query_source_syntax_index_for_source_language(
541    source_path: &str,
542    source: &str,
543    source_language: Option<&str>,
544    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
545) -> OmenaQuerySourceSyntaxIndexV0 {
546    omena_bridge::summarize_omena_bridge_source_syntax_index_for_source_language(
547        source_path,
548        source,
549        source_language,
550        style_import_resolutions,
551    )
552}
553
554pub fn summarize_omena_query_source_syntax_index_for_source_language_with_type_fact_attempts(
555    source_path: &str,
556    source: &str,
557    source_language: Option<&str>,
558    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
559) -> OmenaQuerySourceSyntaxIndexWithTypeFactAttemptsV0 {
560    omena_bridge::summarize_omena_bridge_source_syntax_index_for_source_language_with_type_fact_attempts(
561        source_path,
562        source,
563        source_language,
564        style_import_resolutions,
565    )
566}
567
568pub fn summarize_omena_query_source_binding_index(
569    source: &str,
570    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
571) -> OmenaQuerySourceBindingIndexV0 {
572    omena_bridge::summarize_omena_bridge_source_binding_index(source, style_import_resolutions)
573}
574
575pub fn summarize_omena_query_source_binding_index_for_source_language(
576    source_path: &str,
577    source: &str,
578    source_language: Option<&str>,
579    style_import_resolutions: Vec<OmenaQuerySourceStyleImportResolutionV0>,
580) -> OmenaQuerySourceBindingIndexV0 {
581    omena_bridge::summarize_omena_bridge_source_binding_index_for_source_language(
582        source_path,
583        source,
584        source_language,
585        style_import_resolutions,
586    )
587}
588
589pub fn summarize_omena_query_source_control_flow_graph_for_source_language(
590    source_path: &str,
591    source: &str,
592    source_language: Option<&str>,
593    variable_name: &str,
594    reference_byte_offset: usize,
595) -> Option<crate::OmenaQuerySourceControlFlowGraphCaptureV0> {
596    omena_bridge::summarize_omena_bridge_source_control_flow_graph_for_source_language(
597        source_path,
598        source,
599        source_language,
600        variable_name,
601        reference_byte_offset,
602    )
603}
604
605#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
606#[serde(rename_all = "camelCase")]
607pub struct OmenaQuerySourcePrecisionReferenceV0 {
608    pub schema_version: &'static str,
609    pub product: &'static str,
610    pub source_path: String,
611    #[serde(skip_serializing_if = "Option::is_none")]
612    pub source_language: Option<String>,
613    pub variable_name: String,
614    pub reference_byte_offset: usize,
615    pub resolved_tier: &'static str,
616    pub resolved_value: AbstractClassValueV0,
617    pub precision: OmenaQueryAnalysisPrecisionV0,
618    #[serde(skip_serializing_if = "Option::is_none")]
619    pub top_cause: Option<&'static str>,
620}
621
622pub fn resolve_omena_query_source_precision_for_source(
623    source_path: &str,
624    source: &str,
625    source_language: Option<&str>,
626    variable_name: &str,
627    reference_byte_offset: usize,
628) -> OmenaQuerySourcePrecisionReferenceV0 {
629    let precision =
630        source_diagnostic_precision("classValueResolution", "sourceControlFlow", "sameFile");
631    let Some(capture) = summarize_omena_query_source_control_flow_graph_for_source_language(
632        source_path,
633        source,
634        source_language,
635        variable_name,
636        reference_byte_offset,
637    ) else {
638        return source_precision_reference(
639            source_path,
640            source_language,
641            variable_name,
642            reference_byte_offset,
643            top_class_value(),
644            precision,
645            Some("noFlowCapture"),
646        );
647    };
648
649    let resolved_flow = resolve_source_precision_flow_from_snapshot(
650        &capture.snapshot,
651        capture.binding.symbol_ordinal,
652    )
653    .unwrap_or(ResolvedSourcePrecisionFlowV0 {
654        value: top_class_value(),
655        top_cause: Some("ambiguousFlowSnapshot"),
656    });
657    let top_cause = if abstract_class_value_kind(&resolved_flow.value) == "top" {
658        resolved_flow.top_cause
659    } else {
660        None
661    };
662
663    source_precision_reference(
664        source_path,
665        source_language,
666        variable_name,
667        reference_byte_offset,
668        resolved_flow.value,
669        precision,
670        top_cause,
671    )
672}
673
674#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
675#[serde(rename_all = "camelCase")]
676pub enum OmenaQueryClassSitePlaneV0 {
677    Cfg,
678    TypeFact,
679    Joined,
680}
681
682#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
683#[serde(rename_all = "camelCase")]
684pub enum OmenaQueryClassSiteUnknownCauseV0 {
685    SiteNotEnumerated,
686    SourceValueUnavailable,
687    NonFiniteRawLanguage,
688    TypeFactNotProvided,
689    TypeFactRefused,
690}
691
692#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
693#[serde(rename_all = "camelCase")]
694pub struct OmenaQueryClassSiteTypeFactInputV0 {
695    pub site_byte_span: ParserByteSpanV0,
696    #[serde(skip_serializing_if = "Option::is_none")]
697    pub facts: Option<StringTypeFactsV2>,
698    #[serde(skip_serializing_if = "Option::is_none")]
699    pub refusal_cause: Option<String>,
700}
701
702#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
703#[serde(rename_all = "camelCase")]
704pub struct OmenaQueryClassSiteTokenProvenanceV0 {
705    pub token: OmenaQueryCanonicalClassTokenV0,
706    pub must: bool,
707    pub may: bool,
708    pub planes: Vec<OmenaQueryClassSitePlaneV0>,
709    pub boundary_provenance: Vec<String>,
710    #[serde(skip_serializing_if = "Vec::is_empty")]
711    pub guard_conditions: Vec<String>,
712}
713
714#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
715pub struct OmenaQueryCanonicalClassTokenV0(CanonicalClassKeyV0);
716
717impl OmenaQueryCanonicalClassTokenV0 {
718    pub fn as_str(&self) -> &str {
719        self.0.as_str()
720    }
721}
722
723impl Serialize for OmenaQueryCanonicalClassTokenV0 {
724    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
725    where
726        S: Serializer,
727    {
728        serializer.serialize_str(self.as_str())
729    }
730}
731
732#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
733#[serde(rename_all = "camelCase")]
734pub struct OmenaQueryClassSiteValueV0 {
735    pub schema_version: &'static str,
736    pub product: &'static str,
737    pub source_path: String,
738    #[serde(skip_serializing_if = "Option::is_none")]
739    pub source_language: Option<String>,
740    pub attribute_name: String,
741    pub site_byte_span: ParserByteSpanV0,
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub value_byte_span: Option<ParserByteSpanV0>,
744    #[serde(skip_serializing_if = "Option::is_none")]
745    pub raw_value: Option<String>,
746    pub target_style_uris: Vec<String>,
747    #[serde(skip_serializing_if = "Option::is_none")]
748    pub ordered_word: Option<OrderedTokenWordV0>,
749    #[serde(skip_serializing_if = "Option::is_none")]
750    pub support: Option<TokenSupportV0>,
751    pub token_provenance: Vec<OmenaQueryClassSiteTokenProvenanceV0>,
752    pub contributing_planes: Vec<OmenaQueryClassSitePlaneV0>,
753    pub precision_tier: &'static str,
754    #[serde(skip_serializing_if = "Option::is_none")]
755    pub unknown_cause: Option<OmenaQueryClassSiteUnknownCauseV0>,
756    #[serde(skip_serializing_if = "Option::is_none")]
757    pub type_fact_cause: Option<String>,
758}
759
760#[derive(Debug, Clone, PartialEq, Eq)]
761struct ClassSitePlaneProjectionV0 {
762    ordered_word: Option<OrderedTokenWordV0>,
763    support: TokenSupportV0,
764    precision_tier: &'static str,
765}
766
767pub fn resolve_omena_query_class_site_values_for_source(
768    source_path: &str,
769    source: &str,
770    source_language: Option<&str>,
771) -> Vec<OmenaQueryClassSiteValueV0> {
772    resolve_omena_query_class_site_values_for_source_with_type_facts(
773        source_path,
774        source,
775        source_language,
776        &[],
777    )
778}
779
780pub fn resolve_omena_query_class_site_values_for_source_with_type_facts(
781    source_path: &str,
782    source: &str,
783    source_language: Option<&str>,
784    type_facts: &[OmenaQueryClassSiteTypeFactInputV0],
785) -> Vec<OmenaQueryClassSiteValueV0> {
786    let imports = summarize_omena_query_source_import_declarations_for_source_language(
787        source_path,
788        source,
789        source_language,
790    );
791    let base_document_uri = style_source_path_as_file_uri(source_path);
792    let style_import_resolutions = imports
793        .imports
794        .iter()
795        .filter(|import| source_specifier_is_style_module(&import.specifier))
796        .filter_map(|import| {
797            resolve_omena_query_style_uri_for_specifier(&base_document_uri, None, &import.specifier)
798                .map(|style_uri| import.style_resolution(style_uri.as_str()))
799        })
800        .collect::<Vec<_>>();
801    let binding_index = summarize_omena_query_source_binding_index_for_source_language(
802        source_path,
803        source,
804        source_language,
805        style_import_resolutions,
806    );
807    binding_index
808        .class_attribute_sites
809        .iter()
810        .map(|site| {
811            let type_fact = type_facts
812                .iter()
813                .find(|fact| fact.site_byte_span == site.site_byte_span);
814            class_site_value_from_binding_fact(
815                source_path,
816                source,
817                source_language,
818                site,
819                type_fact,
820            )
821        })
822        .collect()
823}
824
825pub fn resolve_omena_query_class_site_value_for_source(
826    source_path: &str,
827    source: &str,
828    source_language: Option<&str>,
829    site_byte_span: ParserByteSpanV0,
830) -> Option<OmenaQueryClassSiteValueV0> {
831    resolve_omena_query_class_site_values_for_source(source_path, source, source_language)
832        .into_iter()
833        .find(|site| site.site_byte_span == site_byte_span)
834}
835
836pub fn build_omena_query_guarded_token_map_for_site(
837    site: &OmenaQueryClassSiteValueV0,
838) -> Result<GuardedTokenMapV0, FirstWitnessErrorV0> {
839    let mut tokens = Vec::new();
840    for provenance in &site.token_provenance {
841        let token = GuardedTokenLanguageV0::concrete(provenance.token.as_str());
842        let observers = TokenObserverProjectionV0::exact(&token);
843        if provenance.guard_conditions.is_empty() {
844            tokens.push(GuardedTokenInputV0 {
845                token,
846                guards: Vec::new(),
847                observers,
848            });
849            continue;
850        }
851        tokens.extend(
852            provenance
853                .guard_conditions
854                .iter()
855                .map(|condition| GuardedTokenInputV0 {
856                    token: token.clone(),
857                    guards: vec![guard_atom_from_source_condition(condition)],
858                    observers: observers.clone(),
859                }),
860        );
861    }
862    if tokens.is_empty()
863        && let Some(raw_language) = site
864            .raw_value
865            .as_ref()
866            .filter(|raw| raw.starts_with('`') && raw.contains("${"))
867    {
868        let token = GuardedTokenLanguageV0::symbolic(raw_language.clone());
869        tokens.push(GuardedTokenInputV0 {
870            observers: TokenObserverProjectionV0::exact(&token),
871            token,
872            guards: Vec::new(),
873        });
874    }
875    GuardedTokenMapV0::build(GuardedTokenMapInputV0 {
876        tokens,
877        site_usage_guards: Vec::new(),
878    })
879}
880
881fn guard_atom_from_source_condition(condition: &str) -> GuardAtomV0 {
882    let condition = condition.trim();
883    let negated = condition
884        .strip_prefix("!(")
885        .and_then(|condition| condition.strip_suffix(')'));
886    GuardAtomV0 {
887        atom: negated.unwrap_or(condition).trim().to_string(),
888        polarity: negated.is_none(),
889    }
890}
891
892fn class_site_value_from_binding_fact(
893    source_path: &str,
894    source: &str,
895    source_language: Option<&str>,
896    site: &crate::OmenaQuerySourceClassAttributeSiteFactV0,
897    type_fact: Option<&OmenaQueryClassSiteTypeFactInputV0>,
898) -> OmenaQueryClassSiteValueV0 {
899    let cfg_projection = site
900        .source_facts
901        .as_ref()
902        .and_then(|facts| class_site_projection_from_values(facts.values.as_deref()));
903    let type_projection = type_fact
904        .and_then(|input| input.facts.as_ref())
905        .and_then(|facts| class_site_projection_from_values(facts.values.as_deref()));
906
907    let mut must = BTreeSet::new();
908    let mut may = BTreeSet::new();
909    let mut contributing_planes = Vec::new();
910    for (plane, projection) in [
911        (OmenaQueryClassSitePlaneV0::Cfg, cfg_projection.as_ref()),
912        (
913            OmenaQueryClassSitePlaneV0::TypeFact,
914            type_projection.as_ref(),
915        ),
916    ] {
917        if let Some(projection) = projection {
918            contributing_planes.push(plane);
919            must.extend(projection.support.must().iter().cloned());
920            may.extend(projection.support.may().iter().cloned());
921        }
922    }
923    let mut guard_conditions_by_token = BTreeMap::<CanonicalClassKeyV0, Vec<String>>::new();
924    for guarded in &site.guarded_tokens {
925        if let DomClassTokenizationV0::Known { word, .. } =
926            tokenize_dom_class_attribute_v0(Some(&guarded.token))
927        {
928            for token in word.tokens() {
929                may.insert(token.clone());
930                guard_conditions_by_token
931                    .entry(token.clone())
932                    .or_default()
933                    .push(guarded.condition.clone());
934            }
935        }
936    }
937    if !site.guarded_tokens.is_empty()
938        && !contributing_planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
939    {
940        contributing_planes.push(OmenaQueryClassSitePlaneV0::Cfg);
941    }
942    if contributing_planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
943        && contributing_planes.contains(&OmenaQueryClassSitePlaneV0::TypeFact)
944    {
945        contributing_planes.push(OmenaQueryClassSitePlaneV0::Joined);
946    }
947    let support = (!may.is_empty())
948        .then(|| TokenSupportV0::new(must.iter().cloned(), may.iter().cloned()))
949        .flatten();
950    let ordered_word = match (cfg_projection.as_ref(), type_projection.as_ref()) {
951        (Some(cfg), Some(type_fact)) if cfg.ordered_word == type_fact.ordered_word => {
952            cfg.ordered_word.clone()
953        }
954        (Some(cfg), None) => cfg.ordered_word.clone(),
955        (None, Some(type_fact)) => type_fact.ordered_word.clone(),
956        _ => site.ordered_word.clone(),
957    };
958    let mut token_provenance = may
959        .iter()
960        .map(|token| {
961            let mut planes = [
962                (OmenaQueryClassSitePlaneV0::Cfg, cfg_projection.as_ref()),
963                (
964                    OmenaQueryClassSitePlaneV0::TypeFact,
965                    type_projection.as_ref(),
966                ),
967            ]
968            .into_iter()
969            .filter_map(|(plane, projection)| {
970                projection
971                    .is_some_and(|projection| projection.support.may().contains(token))
972                    .then_some(plane)
973            })
974            .collect::<Vec<_>>();
975            if guard_conditions_by_token.contains_key(token)
976                && !planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
977            {
978                planes.push(OmenaQueryClassSitePlaneV0::Cfg);
979            }
980            if planes.contains(&OmenaQueryClassSitePlaneV0::Cfg)
981                && planes.contains(&OmenaQueryClassSitePlaneV0::TypeFact)
982            {
983                planes.push(OmenaQueryClassSitePlaneV0::Joined);
984            }
985            let mut boundary_provenance = Vec::new();
986            if planes.contains(&OmenaQueryClassSitePlaneV0::Cfg) {
987                boundary_provenance.push(class_boundary_effect_label(site.boundary_effect));
988            }
989            if guard_conditions_by_token.contains_key(token) {
990                boundary_provenance.push("guardedClassToken".to_string());
991            }
992            OmenaQueryClassSiteTokenProvenanceV0 {
993                token: OmenaQueryCanonicalClassTokenV0(token.clone()),
994                must: must.contains(token),
995                may: true,
996                planes,
997                boundary_provenance,
998                guard_conditions: guard_conditions_by_token
999                    .get(token)
1000                    .cloned()
1001                    .unwrap_or_default(),
1002            }
1003        })
1004        .collect::<Vec<_>>();
1005    token_provenance.sort_by(|left, right| left.token.cmp(&right.token));
1006
1007    let precision_tier = cfg_projection
1008        .as_ref()
1009        .map(|projection| projection.precision_tier)
1010        .or_else(|| {
1011            type_projection
1012                .as_ref()
1013                .map(|projection| projection.precision_tier)
1014        })
1015        .or_else(|| (!site.guarded_tokens.is_empty()).then_some("finiteSet"))
1016        .unwrap_or("top");
1017    let unknown_cause = if support.is_some() && !may.is_empty() {
1018        None
1019    } else if site.source_facts.is_some()
1020        || type_fact.and_then(|input| input.facts.as_ref()).is_some()
1021    {
1022        Some(OmenaQueryClassSiteUnknownCauseV0::NonFiniteRawLanguage)
1023    } else {
1024        Some(OmenaQueryClassSiteUnknownCauseV0::SourceValueUnavailable)
1025    };
1026    let type_fact_cause = match type_fact {
1027        None => Some("typeFactNotProvided".to_string()),
1028        Some(input) if input.facts.is_none() => Some(
1029            input
1030                .refusal_cause
1031                .clone()
1032                .unwrap_or_else(|| "typeFactRefused".to_string()),
1033        ),
1034        Some(_) => None,
1035    };
1036
1037    OmenaQueryClassSiteValueV0 {
1038        schema_version: "0",
1039        product: "omena-query.class-site-value",
1040        source_path: source_path.to_string(),
1041        source_language: source_language.map(str::to_string),
1042        attribute_name: site.attribute_name.clone(),
1043        site_byte_span: site.site_byte_span,
1044        value_byte_span: site.value_byte_span,
1045        raw_value: site
1046            .value_byte_span
1047            .and_then(|span| source.get(span.start..span.end))
1048            .map(str::to_string),
1049        target_style_uris: site.target_style_uris.clone(),
1050        ordered_word,
1051        support,
1052        token_provenance,
1053        contributing_planes,
1054        precision_tier,
1055        unknown_cause,
1056        type_fact_cause,
1057    }
1058}
1059
1060fn class_site_projection_from_values(
1061    values: Option<&[String]>,
1062) -> Option<ClassSitePlaneProjectionV0> {
1063    let values = values?;
1064    if values.is_empty() {
1065        return None;
1066    }
1067    let mut words = Vec::with_capacity(values.len());
1068    for value in values {
1069        let DomClassTokenizationV0::Known { word, .. } =
1070            tokenize_dom_class_attribute_v0(Some(value))
1071        else {
1072            return None;
1073        };
1074        words.push(word);
1075    }
1076    let may = words
1077        .iter()
1078        .flat_map(|word| word.tokens().iter().cloned())
1079        .collect::<BTreeSet<_>>();
1080    let mut must = words
1081        .first()
1082        .map(|word| word.tokens().iter().cloned().collect::<BTreeSet<_>>())?;
1083    for word in words.iter().skip(1) {
1084        let word = word.tokens().iter().cloned().collect::<BTreeSet<_>>();
1085        must = must.intersection(&word).cloned().collect();
1086    }
1087    let ordered_word = words
1088        .iter()
1089        .all(|word| word == &words[0])
1090        .then(|| words[0].clone());
1091    Some(ClassSitePlaneProjectionV0 {
1092        ordered_word,
1093        support: TokenSupportV0::new(must, may)?,
1094        precision_tier: if values.len() == 1 {
1095            "exact"
1096        } else {
1097            "finiteSet"
1098        },
1099    })
1100}
1101
1102fn class_boundary_effect_label(effect: ClassBoundaryEffectV0) -> String {
1103    match effect {
1104        ClassBoundaryEffectV0::ConcatInsideToken => "concatInsideToken",
1105        ClassBoundaryEffectV0::ConcatAtTokenBoundary => "concatAtTokenBoundary",
1106        ClassBoundaryEffectV0::UnknownBoundary => "unknownBoundary",
1107    }
1108    .to_string()
1109}
1110
1111fn source_specifier_is_style_module(specifier: &str) -> bool {
1112    [".css", ".scss", ".sass", ".less"]
1113        .iter()
1114        .any(|extension| specifier.ends_with(extension))
1115}
1116
1117#[derive(Clone, PartialEq, Eq)]
1118struct ResolvedSourcePrecisionFlowV0 {
1119    value: AbstractClassValueV0,
1120    top_cause: Option<&'static str>,
1121}
1122
1123fn resolve_source_precision_flow_from_snapshot(
1124    snapshot: &crate::OmenaQuerySourceFlowBlockGraphSnapshotV0,
1125    symbol_ordinal: usize,
1126) -> Option<ResolvedSourcePrecisionFlowV0> {
1127    let predecessors = source_precision_predecessor_block_ids(&snapshot.blocks);
1128    let mut states = snapshot
1129        .blocks
1130        .iter()
1131        .map(|block| (block.id.clone(), None::<ResolvedSourcePrecisionFlowV0>))
1132        .collect::<BTreeMap<_, _>>();
1133
1134    for _ in 0..std::cmp::max(snapshot.blocks.len() * 2, 1) {
1135        let mut changed = false;
1136        for block in &snapshot.blocks {
1137            let incoming = source_precision_incoming_state(block, &predecessors, &states);
1138            let next = apply_source_precision_block(block, symbol_ordinal, incoming);
1139            if states.get(&block.id).and_then(Clone::clone) != next {
1140                states.insert(block.id.clone(), next);
1141                changed = true;
1142            }
1143        }
1144        if !changed {
1145            break;
1146        }
1147    }
1148
1149    let exit = snapshot
1150        .blocks
1151        .iter()
1152        .find(|block| block.id == "exit")
1153        .or_else(|| snapshot.blocks.last())?;
1154    states.get(&exit.id).and_then(Clone::clone)
1155}
1156
1157fn source_precision_predecessor_block_ids(
1158    blocks: &[crate::OmenaQuerySourceFlowBlockSnapshotV0],
1159) -> BTreeMap<String, Vec<String>> {
1160    let mut predecessors = BTreeMap::<String, Vec<String>>::new();
1161    for block in blocks {
1162        for successor in &block.successor_block_ids {
1163            predecessors
1164                .entry(successor.clone())
1165                .or_default()
1166                .push(block.id.clone());
1167        }
1168    }
1169    predecessors
1170}
1171
1172fn source_precision_incoming_state(
1173    block: &crate::OmenaQuerySourceFlowBlockSnapshotV0,
1174    predecessors: &BTreeMap<String, Vec<String>>,
1175    states: &BTreeMap<String, Option<ResolvedSourcePrecisionFlowV0>>,
1176) -> Option<ResolvedSourcePrecisionFlowV0> {
1177    predecessors
1178        .get(&block.id)
1179        .into_iter()
1180        .flat_map(|ids| ids.iter())
1181        .filter_map(|id| states.get(id).and_then(Clone::clone))
1182        .reduce(join_source_precision_flows)
1183}
1184
1185fn apply_source_precision_block(
1186    block: &crate::OmenaQuerySourceFlowBlockSnapshotV0,
1187    symbol_ordinal: usize,
1188    incoming: Option<ResolvedSourcePrecisionFlowV0>,
1189) -> Option<ResolvedSourcePrecisionFlowV0> {
1190    if block.symbol_ordinal != Some(symbol_ordinal)
1191        || !matches!(block.transfer_kind, "assignFacts" | "concatFacts")
1192    {
1193        return incoming;
1194    }
1195
1196    let Some(facts) = block.facts.as_ref() else {
1197        return Some(ResolvedSourcePrecisionFlowV0 {
1198            value: top_class_value(),
1199            top_cause: Some("missingValueFacts"),
1200        });
1201    };
1202
1203    let external_facts = ExternalStringTypeFactsV0 {
1204        kind: facts.kind.clone(),
1205        constraint_kind: facts.constraint_kind.clone(),
1206        values: facts.values.clone(),
1207        prefix: facts.prefix.clone(),
1208        suffix: facts.suffix.clone(),
1209        min_len: facts.min_len,
1210        max_len: facts.max_len,
1211        char_must: facts.char_must.clone(),
1212        char_may: facts.char_may.clone(),
1213        may_include_other_chars: facts.may_include_other_chars,
1214    };
1215
1216    Some(ResolvedSourcePrecisionFlowV0 {
1217        value: abstract_class_value_from_facts(&external_facts),
1218        top_cause: None,
1219    })
1220}
1221
1222fn join_source_precision_flows(
1223    left: ResolvedSourcePrecisionFlowV0,
1224    right: ResolvedSourcePrecisionFlowV0,
1225) -> ResolvedSourcePrecisionFlowV0 {
1226    let value = join_abstract_class_values(&left.value, &right.value);
1227    let top_cause = if abstract_class_value_kind(&value) == "top" {
1228        left.top_cause.or(right.top_cause).or(Some("joinedTop"))
1229    } else {
1230        None
1231    };
1232    ResolvedSourcePrecisionFlowV0 { value, top_cause }
1233}
1234
1235fn source_precision_reference(
1236    source_path: &str,
1237    source_language: Option<&str>,
1238    variable_name: &str,
1239    reference_byte_offset: usize,
1240    resolved_value: AbstractClassValueV0,
1241    precision: OmenaQueryAnalysisPrecisionV0,
1242    top_cause: Option<&'static str>,
1243) -> OmenaQuerySourcePrecisionReferenceV0 {
1244    let resolved_tier = abstract_class_value_kind(&resolved_value);
1245    OmenaQuerySourcePrecisionReferenceV0 {
1246        schema_version: "0",
1247        product: "omena-query.source-precision-reference",
1248        source_path: source_path.to_string(),
1249        source_language: source_language.map(str::to_string),
1250        variable_name: variable_name.to_string(),
1251        reference_byte_offset,
1252        resolved_tier,
1253        resolved_value,
1254        precision,
1255        top_cause,
1256    }
1257}
1258
1259pub fn summarize_omena_query_source_type_fact_control_flow_graph_for_source_language(
1260    source_path: &str,
1261    source: &str,
1262    source_language: Option<&str>,
1263    variable_name: &str,
1264    reference_byte_offset: usize,
1265) -> Option<crate::OmenaQuerySourceTypeFactControlFlowGraphV0> {
1266    omena_bridge::summarize_omena_bridge_source_type_fact_control_flow_graph_for_source_language(
1267        source_path,
1268        source,
1269        source_language,
1270        variable_name,
1271        reference_byte_offset,
1272    )
1273}
1274
1275pub fn collect_omena_query_vue_style_module_bindings(
1276    source_path: &str,
1277    source: &str,
1278    source_language: Option<&str>,
1279) -> Vec<String> {
1280    omena_bridge::collect_omena_bridge_vue_style_module_bindings(
1281        source_path,
1282        source,
1283        source_language,
1284    )
1285}
1286
1287pub fn canonicalize_omena_query_source_selector_references(
1288    references: &mut Vec<OmenaQuerySourceSelectorReferenceFactV0>,
1289) {
1290    omena_bridge::canonicalize_source_selector_references(references);
1291}