Skip to main content

omena_bridge/
lib.rs

1use engine_input_producers::EngineInputV2;
2use engine_style_parser::{
3    ParserBoundarySyntaxFactsV0, StyleSemanticFactsV0, Stylesheet, parse_style_module,
4};
5pub use omena_semantic::{
6    DesignTokenExternalDeclarationCandidateScopeV0, DesignTokenWorkspaceDeclarationFactV0,
7};
8use omena_semantic::{
9    DesignTokenSemanticSummaryV0, LosslessCstContractV0, SelectorIdentityEngineSummaryV0,
10};
11use serde::Serialize;
12
13mod promotion_evidence;
14mod selector_references;
15mod source_evidence;
16
17pub use promotion_evidence::{
18    SemanticPromotionEvidenceItemV0, SemanticPromotionEvidenceSummaryV0,
19    summarize_omena_bridge_promotion_evidence_with_source_input,
20    summarize_omena_bridge_semantic_promotion_evidence,
21};
22pub use selector_references::{
23    SelectorEditableDirectReferenceSiteV0, SelectorReferenceEngineSummaryV0,
24    SelectorReferenceSiteV0, SelectorReferenceSummaryV0,
25    summarize_omena_bridge_selector_reference_engine,
26};
27pub use source_evidence::{
28    BindingOriginEvidenceV0, CertaintyReasonEvidenceV0, ReferenceSiteIdentityEvidenceV0,
29    SourceInputPromotionEvidenceSummaryV0, StyleModuleEdgeEvidenceV0,
30    ValueDomainExplanationEvidenceV0, summarize_omena_bridge_source_input_evidence,
31};
32
33pub fn collect_omena_bridge_design_token_workspace_declarations(
34    style_path: &str,
35    sheet: &Stylesheet,
36) -> Vec<DesignTokenWorkspaceDeclarationFactV0> {
37    let parser_facts = omena_semantic::summarize_parser_contract_facts(sheet);
38    omena_semantic::collect_design_token_workspace_declarations(style_path, &parser_facts)
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
42#[serde(rename_all = "camelCase")]
43pub struct OmenaBridgeBoundarySummaryV0 {
44    pub schema_version: &'static str,
45    pub product: &'static str,
46    pub bridge_name: &'static str,
47    pub graph_product: &'static str,
48    pub delegated_semantic_boundary_product: &'static str,
49    pub selector_reference_product: &'static str,
50    pub source_input_evidence_product: &'static str,
51    pub bridge_owned_surfaces: Vec<&'static str>,
52    pub cme_coupled_surfaces: Vec<&'static str>,
53    pub next_decoupling_targets: Vec<&'static str>,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct StyleSemanticGraphSummaryV0 {
59    pub schema_version: &'static str,
60    pub product: &'static str,
61    pub language: &'static str,
62    pub parser_facts: ParserBoundarySyntaxFactsV0,
63    pub semantic_facts: StyleSemanticFactsV0,
64    pub design_token_semantics: DesignTokenSemanticSummaryV0,
65    pub selector_identity_engine: SelectorIdentityEngineSummaryV0,
66    pub selector_reference_engine: SelectorReferenceEngineSummaryV0,
67    pub source_input_evidence: SourceInputPromotionEvidenceSummaryV0,
68    pub promotion_evidence: SemanticPromotionEvidenceSummaryV0,
69    pub lossless_cst_contract: LosslessCstContractV0,
70}
71
72pub fn summarize_omena_bridge_boundary() -> OmenaBridgeBoundarySummaryV0 {
73    OmenaBridgeBoundarySummaryV0 {
74        schema_version: "0",
75        product: "omena-bridge.cme-semantic-bridge",
76        bridge_name: "cme-semantic-bridge",
77        graph_product: "omena-semantic.style-semantic-graph",
78        delegated_semantic_boundary_product: "omena-semantic.style-semantic-boundary",
79        selector_reference_product: "omena-semantic.selector-references",
80        source_input_evidence_product: "omena-semantic.source-input-evidence",
81        bridge_owned_surfaces: vec![
82            "styleSemanticGraph",
83            "styleSemanticGraphFromSource",
84            "selectorReferenceEngine",
85            "sourceInputEvidence",
86            "promotionEvidenceWithSourceInput",
87        ],
88        cme_coupled_surfaces: vec![
89            "EngineInputV2",
90            "sourceInputEvidence",
91            "selectorReferenceEngine",
92            "promotionEvidenceWithSourceInput",
93            "styleSemanticGraphFromSource",
94        ],
95        next_decoupling_targets: Vec::new(),
96    }
97}
98
99pub fn summarize_omena_bridge_style_semantic_graph(
100    sheet: &Stylesheet,
101    input: &EngineInputV2,
102) -> StyleSemanticGraphSummaryV0 {
103    summarize_omena_bridge_style_semantic_graph_for_path(sheet, input, None)
104}
105
106pub fn summarize_omena_bridge_style_semantic_graph_for_path(
107    sheet: &Stylesheet,
108    input: &EngineInputV2,
109    style_path: Option<&str>,
110) -> StyleSemanticGraphSummaryV0 {
111    summarize_omena_bridge_style_semantic_graph_for_path_with_workspace_declarations(
112        sheet,
113        input,
114        style_path,
115        &[],
116    )
117}
118
119pub fn summarize_omena_bridge_style_semantic_graph_for_path_with_workspace_declarations(
120    sheet: &Stylesheet,
121    input: &EngineInputV2,
122    style_path: Option<&str>,
123    workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
124) -> StyleSemanticGraphSummaryV0 {
125    summarize_omena_bridge_style_semantic_graph_for_path_with_scoped_workspace_declarations(
126        sheet,
127        input,
128        style_path,
129        workspace_declarations,
130        DesignTokenExternalDeclarationCandidateScopeV0::Workspace,
131    )
132}
133
134pub fn summarize_omena_bridge_style_semantic_graph_for_path_with_scoped_workspace_declarations(
135    sheet: &Stylesheet,
136    input: &EngineInputV2,
137    style_path: Option<&str>,
138    workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
139    candidate_scope: DesignTokenExternalDeclarationCandidateScopeV0,
140) -> StyleSemanticGraphSummaryV0 {
141    let boundary = omena_semantic::summarize_style_semantic_boundary(sheet);
142    let parser_facts = boundary.parser_facts;
143    let semantic_facts = boundary.semantic_facts;
144    let design_token_semantics =
145        omena_semantic::summarize_design_token_semantics_with_scoped_workspace_declarations(
146            &parser_facts,
147            &semantic_facts,
148            style_path,
149            workspace_declarations,
150            candidate_scope,
151        );
152    let selector_identity_engine = boundary.selector_identity_engine;
153    let selector_reference_engine =
154        summarize_omena_bridge_selector_reference_engine(input, style_path);
155    let source_input_evidence = summarize_omena_bridge_source_input_evidence(input);
156    let promotion_evidence = summarize_omena_bridge_promotion_evidence_with_source_input(
157        &parser_facts,
158        &semantic_facts,
159        input,
160    );
161    let lossless_cst_contract = boundary.lossless_cst_contract;
162
163    StyleSemanticGraphSummaryV0 {
164        schema_version: "0",
165        product: "omena-semantic.style-semantic-graph",
166        language: boundary.language,
167        parser_facts,
168        semantic_facts,
169        design_token_semantics,
170        selector_identity_engine,
171        selector_reference_engine,
172        source_input_evidence,
173        promotion_evidence,
174        lossless_cst_contract,
175    }
176}
177
178pub fn summarize_omena_bridge_style_semantic_graph_from_source(
179    style_path: &str,
180    style_source: &str,
181    input: &EngineInputV2,
182) -> Option<StyleSemanticGraphSummaryV0> {
183    let sheet = parse_style_module(style_path, style_source)?;
184    Some(summarize_omena_bridge_style_semantic_graph_for_path(
185        &sheet,
186        input,
187        Some(style_path),
188    ))
189}
190
191#[cfg(test)]
192mod tests {
193    use engine_input_producers::{
194        ClassExpressionInputV2, EngineInputV2, PositionV2, RangeV2, SourceAnalysisInputV2,
195        SourceDocumentV2, StringTypeFactsV2, StyleAnalysisInputV2, StyleDocumentV2,
196        StyleSelectorV2, TypeFactEntryV2,
197    };
198    use engine_style_parser::parse_style_module;
199
200    use super::{
201        summarize_omena_bridge_boundary,
202        summarize_omena_bridge_promotion_evidence_with_source_input,
203        summarize_omena_bridge_selector_reference_engine,
204        summarize_omena_bridge_source_input_evidence, summarize_omena_bridge_style_semantic_graph,
205        summarize_omena_bridge_style_semantic_graph_from_source,
206    };
207
208    #[test]
209    fn declares_cme_coupled_bridge_boundary() {
210        let boundary = summarize_omena_bridge_boundary();
211
212        assert_eq!(boundary.schema_version, "0");
213        assert_eq!(boundary.product, "omena-bridge.cme-semantic-bridge");
214        assert_eq!(
215            boundary.graph_product,
216            "omena-semantic.style-semantic-graph"
217        );
218        assert_eq!(
219            boundary.delegated_semantic_boundary_product,
220            "omena-semantic.style-semantic-boundary"
221        );
222        assert_eq!(
223            boundary.selector_reference_product,
224            "omena-semantic.selector-references"
225        );
226        assert_eq!(
227            boundary.source_input_evidence_product,
228            "omena-semantic.source-input-evidence"
229        );
230        assert!(
231            boundary
232                .bridge_owned_surfaces
233                .contains(&"styleSemanticGraphFromSource")
234        );
235        assert!(
236            boundary
237                .bridge_owned_surfaces
238                .contains(&"selectorReferenceEngine")
239        );
240        assert!(
241            boundary
242                .bridge_owned_surfaces
243                .contains(&"sourceInputEvidence")
244        );
245        assert!(
246            boundary
247                .bridge_owned_surfaces
248                .contains(&"promotionEvidenceWithSourceInput")
249        );
250        assert!(
251            boundary
252                .cme_coupled_surfaces
253                .contains(&"promotionEvidenceWithSourceInput")
254        );
255        assert!(
256            boundary.next_decoupling_targets.is_empty(),
257            "all current omena-bridge decoupling targets should be bridge-owned"
258        );
259    }
260
261    #[test]
262    fn exposes_source_input_evidence_through_bridge() {
263        let evidence = summarize_omena_bridge_source_input_evidence(&sample_engine_input());
264
265        assert_eq!(evidence.product, "omena-semantic.source-input-evidence");
266        assert_eq!(evidence.reference_site_identity.status, "ready");
267        assert_eq!(evidence.reference_site_identity.reference_site_count, 1);
268        assert_eq!(evidence.certainty_reason.status, "ready");
269        assert_eq!(evidence.binding_origin.status, "ready");
270        assert!(evidence.blocking_gaps.is_empty());
271    }
272
273    #[test]
274    fn exposes_style_semantic_graph_through_bridge() -> Result<(), String> {
275        let sheet = parse_style_module("Component.module.scss", ".button { color: red; }")
276            .ok_or_else(|| "SCSS module path should parse".to_string())?;
277        let graph = summarize_omena_bridge_style_semantic_graph(&sheet, &sample_engine_input());
278
279        assert_eq!(graph.product, "omena-semantic.style-semantic-graph");
280        assert_eq!(graph.selector_reference_engine.selector_count, 1);
281        assert_eq!(graph.selector_reference_engine.referenced_selector_count, 1);
282        assert_eq!(
283            graph.source_input_evidence.product,
284            "omena-semantic.source-input-evidence"
285        );
286        assert!(graph.promotion_evidence.blocking_gaps.is_empty());
287        Ok(())
288    }
289
290    #[test]
291    fn exposes_style_semantic_graph_from_source_through_bridge() -> Result<(), String> {
292        let graph = summarize_omena_bridge_style_semantic_graph_from_source(
293            "/tmp/Component.module.scss",
294            ".button { color: red; }",
295            &sample_engine_input(),
296        )
297        .ok_or_else(|| "bridge should parse SCSS module source".to_string())?;
298
299        assert_eq!(graph.product, "omena-semantic.style-semantic-graph");
300        assert_eq!(
301            graph.selector_reference_engine.style_path,
302            Some("/tmp/Component.module.scss".to_string())
303        );
304        assert_eq!(
305            graph.source_input_evidence.reference_site_identity.status,
306            "ready"
307        );
308        Ok(())
309    }
310
311    #[test]
312    fn owns_selector_reference_engine_without_changing_host_product() {
313        let bridge_references = summarize_omena_bridge_selector_reference_engine(
314            &sample_engine_input(),
315            Some("/tmp/Component.module.scss"),
316        );
317        let semantic_references = omena_semantic::summarize_selector_reference_engine(
318            &sample_engine_input(),
319            Some("/tmp/Component.module.scss"),
320        );
321
322        assert_eq!(
323            bridge_references.product,
324            "omena-semantic.selector-references"
325        );
326        assert_eq!(bridge_references.product, semantic_references.product);
327        assert_eq!(bridge_references.style_path, semantic_references.style_path);
328        assert_eq!(
329            bridge_references.selector_count,
330            semantic_references.selector_count
331        );
332        assert_eq!(
333            bridge_references.referenced_selector_count,
334            semantic_references.referenced_selector_count
335        );
336        assert_eq!(
337            bridge_references.total_reference_sites,
338            semantic_references.total_reference_sites
339        );
340        assert_eq!(
341            bridge_references.selectors[0].canonical_id,
342            semantic_references.selectors[0].canonical_id
343        );
344        assert_eq!(
345            bridge_references.selectors[0].editable_direct_reference_count,
346            semantic_references.selectors[0].editable_direct_reference_count
347        );
348    }
349
350    #[test]
351    fn owns_source_input_evidence_without_changing_host_product() {
352        let bridge_evidence = summarize_omena_bridge_source_input_evidence(&sample_engine_input());
353        let semantic_evidence =
354            omena_semantic::summarize_source_input_evidence(&sample_engine_input());
355
356        assert_bridge_source_input_evidence_matches_semantic(&bridge_evidence, &semantic_evidence);
357    }
358
359    #[test]
360    fn owns_source_backed_promotion_evidence_without_changing_host_product() -> Result<(), String> {
361        let sheet = parse_style_module("Component.module.scss", ".button { color: red; }")
362            .ok_or_else(|| "SCSS module path should parse".to_string())?;
363        let boundary = omena_semantic::summarize_style_semantic_boundary(&sheet);
364        let input = sample_engine_input();
365        let bridge_evidence = summarize_omena_bridge_promotion_evidence_with_source_input(
366            &boundary.parser_facts,
367            &boundary.semantic_facts,
368            &input,
369        );
370        let semantic_evidence =
371            omena_semantic::summarize_semantic_promotion_evidence_with_source_input(
372                &boundary.parser_facts,
373                &boundary.semantic_facts,
374                &input,
375            );
376
377        assert_bridge_promotion_evidence_matches_semantic(&bridge_evidence, &semantic_evidence);
378        Ok(())
379    }
380
381    #[test]
382    fn owns_graph_assembly_without_changing_host_product() -> Result<(), String> {
383        let sheet = parse_style_module("Component.module.scss", ".button { color: red; }")
384            .ok_or_else(|| "SCSS module path should parse".to_string())?;
385        let bridge_graph =
386            summarize_omena_bridge_style_semantic_graph(&sheet, &sample_engine_input());
387        let semantic_graph =
388            omena_semantic::summarize_style_semantic_graph(&sheet, &sample_engine_input());
389
390        assert_eq!(bridge_graph.product, "omena-semantic.style-semantic-graph");
391        assert_eq!(bridge_graph.product, semantic_graph.product);
392        assert_eq!(bridge_graph.language, semantic_graph.language);
393        assert_eq!(
394            bridge_graph.selector_reference_engine.product,
395            semantic_graph.selector_reference_engine.product
396        );
397        assert_eq!(
398            bridge_graph.selector_reference_engine.selector_count,
399            semantic_graph.selector_reference_engine.selector_count
400        );
401        assert_eq!(
402            bridge_graph.selector_reference_engine.total_reference_sites,
403            semantic_graph
404                .selector_reference_engine
405                .total_reference_sites
406        );
407        assert_bridge_source_input_evidence_matches_semantic(
408            &bridge_graph.source_input_evidence,
409            &semantic_graph.source_input_evidence,
410        );
411        assert_eq!(
412            bridge_graph.design_token_semantics,
413            semantic_graph.design_token_semantics
414        );
415        assert_bridge_promotion_evidence_matches_semantic(
416            &bridge_graph.promotion_evidence,
417            &semantic_graph.promotion_evidence,
418        );
419        Ok(())
420    }
421
422    fn assert_bridge_promotion_evidence_matches_semantic(
423        bridge: &super::SemanticPromotionEvidenceSummaryV0,
424        semantic: &omena_semantic::SemanticPromotionEvidenceSummaryV0,
425    ) {
426        assert_eq!(bridge.schema_version, semantic.schema_version);
427        assert_eq!(bridge.product, semantic.product);
428        assert_eq!(bridge.blocking_gaps, semantic.blocking_gaps);
429        assert_eq!(bridge.next_priorities, semantic.next_priorities);
430        assert_eq!(bridge.items.len(), semantic.items.len());
431
432        for (bridge_item, semantic_item) in bridge.items.iter().zip(&semantic.items) {
433            assert_eq!(bridge_item.evidence, semantic_item.evidence);
434            assert_eq!(bridge_item.status, semantic_item.status);
435            assert_eq!(bridge_item.provider, semantic_item.provider);
436            assert_eq!(bridge_item.observed_count, semantic_item.observed_count);
437            assert_eq!(bridge_item.reason, semantic_item.reason);
438        }
439    }
440
441    fn assert_bridge_source_input_evidence_matches_semantic(
442        bridge: &super::SourceInputPromotionEvidenceSummaryV0,
443        semantic: &omena_semantic::SourceInputPromotionEvidenceSummaryV0,
444    ) {
445        assert_eq!(bridge.schema_version, semantic.schema_version);
446        assert_eq!(bridge.product, semantic.product);
447        assert_eq!(bridge.input_version, semantic.input_version);
448        assert_eq!(
449            bridge.reference_site_identity.status,
450            semantic.reference_site_identity.status
451        );
452        assert_eq!(
453            bridge.reference_site_identity.selector_count,
454            semantic.reference_site_identity.selector_count
455        );
456        assert_eq!(
457            bridge.reference_site_identity.reference_site_count,
458            semantic.reference_site_identity.reference_site_count
459        );
460        assert_eq!(
461            bridge.reference_site_identity.direct_reference_site_count,
462            semantic.reference_site_identity.direct_reference_site_count
463        );
464        assert_eq!(
465            bridge.reference_site_identity.expanded_reference_site_count,
466            semantic
467                .reference_site_identity
468                .expanded_reference_site_count
469        );
470        assert_eq!(
471            bridge
472                .reference_site_identity
473                .style_dependency_reference_site_count,
474            semantic
475                .reference_site_identity
476                .style_dependency_reference_site_count
477        );
478        assert_eq!(
479            bridge.reference_site_identity.editable_direct_site_count,
480            semantic.reference_site_identity.editable_direct_site_count
481        );
482        assert_eq!(
483            bridge.reference_site_identity.reference_kind_counts,
484            semantic.reference_site_identity.reference_kind_counts
485        );
486        assert_eq!(
487            bridge.certainty_reason.status,
488            semantic.certainty_reason.status
489        );
490        assert_eq!(
491            bridge.certainty_reason.expression_count,
492            semantic.certainty_reason.expression_count
493        );
494        assert_eq!(
495            bridge.certainty_reason.exact_count,
496            semantic.certainty_reason.exact_count
497        );
498        assert_eq!(
499            bridge.certainty_reason.inferred_count,
500            semantic.certainty_reason.inferred_count
501        );
502        assert_eq!(
503            bridge.certainty_reason.possible_count,
504            semantic.certainty_reason.possible_count
505        );
506        assert_eq!(
507            bridge.certainty_reason.missing_reason_count,
508            semantic.certainty_reason.missing_reason_count
509        );
510        assert_eq!(
511            bridge.certainty_reason.reason_counts,
512            semantic.certainty_reason.reason_counts
513        );
514        assert_eq!(
515            bridge.certainty_reason.shape_kind_counts,
516            semantic.certainty_reason.shape_kind_counts
517        );
518        assert_eq!(
519            bridge.certainty_reason.shape_label_counts,
520            semantic.certainty_reason.shape_label_counts
521        );
522        assert_eq!(bridge.binding_origin.status, semantic.binding_origin.status);
523        assert_eq!(
524            bridge.binding_origin.expression_count,
525            semantic.binding_origin.expression_count
526        );
527        assert_eq!(
528            bridge.binding_origin.direct_class_name_count,
529            semantic.binding_origin.direct_class_name_count
530        );
531        assert_eq!(
532            bridge.binding_origin.root_binding_count,
533            semantic.binding_origin.root_binding_count
534        );
535        assert_eq!(
536            bridge.binding_origin.access_path_count,
537            semantic.binding_origin.access_path_count
538        );
539        assert_eq!(
540            bridge.binding_origin.access_path_segment_count,
541            semantic.binding_origin.access_path_segment_count
542        );
543        assert_eq!(
544            bridge.binding_origin.expression_kind_counts,
545            semantic.binding_origin.expression_kind_counts
546        );
547        assert_eq!(
548            bridge.style_module_edge.status,
549            semantic.style_module_edge.status
550        );
551        assert_eq!(
552            bridge.style_module_edge.source_style_edge_count,
553            semantic.style_module_edge.source_style_edge_count
554        );
555        assert_eq!(
556            bridge.style_module_edge.distinct_style_module_count,
557            semantic.style_module_edge.distinct_style_module_count
558        );
559        assert_eq!(
560            bridge.style_module_edge.missing_style_document_edge_count,
561            semantic.style_module_edge.missing_style_document_edge_count
562        );
563        assert_eq!(
564            bridge.style_module_edge.composed_edge_count,
565            semantic.style_module_edge.composed_edge_count
566        );
567        assert_eq!(
568            bridge.style_module_edge.imported_composed_edge_count,
569            semantic.style_module_edge.imported_composed_edge_count
570        );
571        assert_eq!(
572            bridge.style_module_edge.global_composed_edge_count,
573            semantic.style_module_edge.global_composed_edge_count
574        );
575        assert_eq!(
576            bridge.value_domain_explanation.status,
577            semantic.value_domain_explanation.status
578        );
579        assert_eq!(
580            bridge.value_domain_explanation.expression_count,
581            semantic.value_domain_explanation.expression_count
582        );
583        assert_eq!(
584            bridge.value_domain_explanation.exact_expression_count,
585            semantic.value_domain_explanation.exact_expression_count
586        );
587        assert_eq!(
588            bridge
589                .value_domain_explanation
590                .finite_value_expression_count,
591            semantic
592                .value_domain_explanation
593                .finite_value_expression_count
594        );
595        assert_eq!(
596            bridge.value_domain_explanation.constrained_expression_count,
597            semantic
598                .value_domain_explanation
599                .constrained_expression_count
600        );
601        assert_eq!(
602            bridge.value_domain_explanation.unknown_expression_count,
603            semantic.value_domain_explanation.unknown_expression_count
604        );
605        assert_eq!(
606            bridge.value_domain_explanation.finite_value_count,
607            semantic.value_domain_explanation.finite_value_count
608        );
609        assert_eq!(
610            bridge.value_domain_explanation.derivation_count,
611            semantic.value_domain_explanation.derivation_count
612        );
613        assert_eq!(
614            bridge.value_domain_explanation.derivation_step_count,
615            semantic.value_domain_explanation.derivation_step_count
616        );
617        assert_eq!(
618            bridge.value_domain_explanation.value_domain_kind_counts,
619            semantic.value_domain_explanation.value_domain_kind_counts
620        );
621        assert_eq!(
622            bridge.value_domain_explanation.constraint_kind_counts,
623            semantic.value_domain_explanation.constraint_kind_counts
624        );
625        assert_eq!(
626            bridge.value_domain_explanation.derivation_product_counts,
627            semantic.value_domain_explanation.derivation_product_counts
628        );
629        assert_eq!(
630            bridge
631                .value_domain_explanation
632                .derivation_reduced_kind_counts,
633            semantic
634                .value_domain_explanation
635                .derivation_reduced_kind_counts
636        );
637        assert_eq!(
638            bridge.value_domain_explanation.derivation_operation_counts,
639            semantic
640                .value_domain_explanation
641                .derivation_operation_counts
642        );
643        assert_eq!(bridge.blocking_gaps, semantic.blocking_gaps);
644        assert_eq!(bridge.next_priorities, semantic.next_priorities);
645    }
646
647    fn sample_engine_input() -> EngineInputV2 {
648        EngineInputV2 {
649            version: "2".to_string(),
650            sources: vec![SourceAnalysisInputV2 {
651                document: SourceDocumentV2 {
652                    class_expressions: vec![ClassExpressionInputV2 {
653                        id: "expr-literal".to_string(),
654                        kind: "literal".to_string(),
655                        scss_module_path: "/tmp/Component.module.scss".to_string(),
656                        range: range(4, 12, 4, 18),
657                        class_name: Some("button".to_string()),
658                        root_binding_decl_id: None,
659                        access_path: None,
660                    }],
661                },
662            }],
663            styles: vec![StyleAnalysisInputV2 {
664                file_path: "/tmp/Component.module.scss".to_string(),
665                document: StyleDocumentV2 {
666                    selectors: vec![StyleSelectorV2 {
667                        name: "button".to_string(),
668                        view_kind: "canonical".to_string(),
669                        canonical_name: Some("button".to_string()),
670                        range: range(0, 1, 0, 7),
671                        nested_safety: Some("flat".to_string()),
672                        composes: None,
673                        bem_suffix: None,
674                    }],
675                },
676            }],
677            type_facts: vec![TypeFactEntryV2 {
678                file_path: "/tmp/Component.tsx".to_string(),
679                expression_id: "expr-literal".to_string(),
680                facts: StringTypeFactsV2 {
681                    kind: "exact".to_string(),
682                    constraint_kind: None,
683                    values: Some(vec!["button".to_string()]),
684                    prefix: None,
685                    suffix: None,
686                    min_len: None,
687                    max_len: None,
688                    char_must: None,
689                    char_may: None,
690                    may_include_other_chars: None,
691                },
692            }],
693        }
694    }
695
696    fn range(
697        start_line: usize,
698        start_character: usize,
699        end_line: usize,
700        end_character: usize,
701    ) -> RangeV2 {
702        RangeV2 {
703            start: PositionV2 {
704                line: start_line,
705                character: start_character,
706            },
707            end: PositionV2 {
708                line: end_line,
709                character: end_character,
710            },
711        }
712    }
713}