1use engine_input_producers::{
2 EngineInputV2, ExpressionDomainFlowAnalysisV0, ExpressionSemanticsCanonicalProducerSignalV0,
3 ExpressionSemanticsQueryFragmentsV0, SelectorUsageCanonicalProducerSignalV0,
4 SelectorUsageQueryFragmentsV0, SourceResolutionCanonicalProducerSignalV0,
5 SourceResolutionQueryFragmentsV0, summarize_expression_domain_flow_analysis_input,
6 summarize_expression_semantics_canonical_producer_signal_input,
7 summarize_expression_semantics_query_fragments_input,
8 summarize_selector_usage_canonical_producer_signal_input,
9 summarize_selector_usage_query_fragments_input,
10};
11use omena_abstract_value::{AbstractValueDomainSummaryV0, summarize_omena_abstract_value_domain};
12use omena_bridge::{
13 StyleSemanticGraphSummaryV0, summarize_omena_bridge_style_semantic_graph_from_source,
14};
15use omena_resolver::{
16 summarize_omena_resolver_canonical_producer_signal, summarize_omena_resolver_query_fragments,
17};
18use serde::Serialize;
19
20#[derive(Debug, Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct OmenaQueryBoundarySummaryV0 {
23 pub schema_version: &'static str,
24 pub product: &'static str,
25 pub query_engine_name: &'static str,
26 pub input_version: String,
27 pub abstract_value_domain: AbstractValueDomainSummaryV0,
28 pub selected_query_adapter_capabilities: SelectedQueryAdapterCapabilitiesV0,
29 pub delegated_fragment_products: Vec<&'static str>,
30 pub expression_semantics_query_count: usize,
31 pub source_resolution_query_count: usize,
32 pub selector_usage_query_count: usize,
33 pub total_query_count: usize,
34 pub ready_surfaces: Vec<&'static str>,
35 pub cme_coupled_surfaces: Vec<&'static str>,
36 pub next_decoupling_targets: Vec<&'static str>,
37}
38
39#[derive(Debug, Serialize)]
40#[serde(rename_all = "camelCase")]
41pub struct OmenaQueryFragmentBundleV0 {
42 pub schema_version: &'static str,
43 pub product: &'static str,
44 pub input_version: String,
45 pub expression_semantics: ExpressionSemanticsQueryFragmentsV0,
46 pub source_resolution: SourceResolutionQueryFragmentsV0,
47 pub selector_usage: SelectorUsageQueryFragmentsV0,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
51#[serde(rename_all = "camelCase")]
52pub struct SelectedQueryAdapterCapabilitiesV0 {
53 pub schema_version: &'static str,
54 pub product: &'static str,
55 pub default_candidate_backend: &'static str,
56 pub backend_kinds: Vec<SelectedQueryBackendCapabilityV0>,
57 pub runner_commands: Vec<SelectedQueryRunnerCommandV0>,
58 pub expression_semantics_payload_contracts: Vec<&'static str>,
59 pub required_input_contracts: Vec<&'static str>,
60 pub adapter_readiness: Vec<&'static str>,
61 pub routing_status: &'static str,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
65#[serde(rename_all = "camelCase")]
66pub struct SelectedQueryBackendCapabilityV0 {
67 pub backend_kind: &'static str,
68 pub source_resolution: bool,
69 pub expression_semantics: bool,
70 pub selector_usage: bool,
71 pub style_semantic_graph: bool,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
75#[serde(rename_all = "camelCase")]
76pub struct SelectedQueryRunnerCommandV0 {
77 pub surface: &'static str,
78 pub command: &'static str,
79 pub input_contract: &'static str,
80 pub output_product: &'static str,
81}
82
83#[derive(Debug, Serialize)]
84#[serde(rename_all = "camelCase")]
85pub struct OmenaQueryStyleSemanticGraphBatchOutputV0 {
86 pub schema_version: &'static str,
87 pub product: &'static str,
88 pub graphs: Vec<OmenaQueryStyleSemanticGraphBatchEntryV0>,
89}
90
91#[derive(Debug, Serialize)]
92#[serde(rename_all = "camelCase")]
93pub struct OmenaQueryStyleSemanticGraphBatchEntryV0 {
94 pub style_path: String,
95 pub graph: Option<StyleSemanticGraphSummaryV0>,
96}
97
98pub fn summarize_omena_query_boundary(input: &EngineInputV2) -> OmenaQueryBoundarySummaryV0 {
99 let fragment_bundle = summarize_omena_query_fragment_bundle(input);
100 let expression_semantics_query_count = fragment_bundle.expression_semantics.fragments.len();
101 let source_resolution_query_count = fragment_bundle.source_resolution.fragments.len();
102 let selector_usage_query_count = fragment_bundle.selector_usage.fragments.len();
103
104 OmenaQueryBoundarySummaryV0 {
105 schema_version: "0",
106 product: "omena-query.boundary",
107 query_engine_name: "omena-query",
108 input_version: input.version.clone(),
109 abstract_value_domain: summarize_omena_abstract_value_domain(),
110 selected_query_adapter_capabilities:
111 summarize_omena_query_selected_query_adapter_capabilities(),
112 delegated_fragment_products: vec![
113 "engine-input-producers.expression-semantics-query-fragments",
114 "engine-input-producers.source-resolution-query-fragments",
115 "omena-resolver.boundary",
116 "engine-input-producers.selector-usage-query-fragments",
117 "engine-input-producers.expression-domain-flow-analysis",
118 ],
119 expression_semantics_query_count,
120 source_resolution_query_count,
121 selector_usage_query_count,
122 total_query_count: expression_semantics_query_count
123 + source_resolution_query_count
124 + selector_usage_query_count,
125 ready_surfaces: vec![
126 "queryFragmentBundle",
127 "abstractValueProjectionContract",
128 "sourceResolutionResolverBoundary",
129 "expressionDomainFlowAnalysisBoundary",
130 "queryBoundarySummary",
131 ],
132 cme_coupled_surfaces: vec!["EngineInputV2", "producerQueryFragments"],
133 next_decoupling_targets: vec!["queryEvaluationRuntime", "selectedQueryBackendAdapter"],
134 }
135}
136
137pub fn summarize_omena_query_selected_query_adapter_capabilities()
138-> SelectedQueryAdapterCapabilitiesV0 {
139 SelectedQueryAdapterCapabilitiesV0 {
140 schema_version: "0",
141 product: "omena-query.selected-query-adapter-capabilities",
142 default_candidate_backend: "rust-selected-query",
143 backend_kinds: vec![
144 SelectedQueryBackendCapabilityV0 {
145 backend_kind: "typescript-current",
146 source_resolution: false,
147 expression_semantics: false,
148 selector_usage: false,
149 style_semantic_graph: false,
150 },
151 SelectedQueryBackendCapabilityV0 {
152 backend_kind: "rust-source-resolution",
153 source_resolution: true,
154 expression_semantics: false,
155 selector_usage: false,
156 style_semantic_graph: false,
157 },
158 SelectedQueryBackendCapabilityV0 {
159 backend_kind: "rust-expression-semantics",
160 source_resolution: false,
161 expression_semantics: true,
162 selector_usage: false,
163 style_semantic_graph: false,
164 },
165 SelectedQueryBackendCapabilityV0 {
166 backend_kind: "rust-selector-usage",
167 source_resolution: false,
168 expression_semantics: false,
169 selector_usage: true,
170 style_semantic_graph: false,
171 },
172 SelectedQueryBackendCapabilityV0 {
173 backend_kind: "rust-selected-query",
174 source_resolution: true,
175 expression_semantics: true,
176 selector_usage: true,
177 style_semantic_graph: true,
178 },
179 ],
180 runner_commands: vec![
181 SelectedQueryRunnerCommandV0 {
182 surface: "sourceResolution",
183 command: "input-source-resolution-canonical-producer",
184 input_contract: "EngineInputV2",
185 output_product: "engine-input-producers.source-resolution-canonical-producer",
186 },
187 SelectedQueryRunnerCommandV0 {
188 surface: "expressionSemantics",
189 command: "input-expression-semantics-canonical-producer",
190 input_contract: "EngineInputV2",
191 output_product: "engine-input-producers.expression-semantics-canonical-producer",
192 },
193 SelectedQueryRunnerCommandV0 {
194 surface: "expressionDomainFlowAnalysis",
195 command: "input-expression-domain-flow-analysis",
196 input_contract: "EngineInputV2",
197 output_product: "engine-input-producers.expression-domain-flow-analysis",
198 },
199 SelectedQueryRunnerCommandV0 {
200 surface: "selectorUsage",
201 command: "input-selector-usage-canonical-producer",
202 input_contract: "EngineInputV2",
203 output_product: "engine-input-producers.selector-usage-canonical-producer",
204 },
205 SelectedQueryRunnerCommandV0 {
206 surface: "styleSemanticGraph",
207 command: "style-semantic-graph",
208 input_contract: "StyleSemanticGraphInputV0",
209 output_product: "omena-semantic.style-semantic-graph",
210 },
211 SelectedQueryRunnerCommandV0 {
212 surface: "styleSemanticGraphBatch",
213 command: "style-semantic-graph-batch",
214 input_contract: "StyleSemanticGraphBatchInputV0",
215 output_product: "omena-semantic.style-semantic-graph-batch",
216 },
217 ],
218 expression_semantics_payload_contracts: vec!["valueDomainKind", "valueDomainDerivation"],
219 required_input_contracts: vec![
220 "EngineInputV2",
221 "StyleSemanticGraphInputV0",
222 "StyleSemanticGraphBatchInputV0",
223 ],
224 adapter_readiness: vec![
225 "backendCapabilityMatrix",
226 "canonicalProducerWrapperBoundary",
227 "styleSemanticGraphBridgeBoundary",
228 "runnerCommandContract",
229 "fragmentBundleBoundary",
230 "expressionSemanticsDerivationPayload",
231 "expressionDomainFlowAnalysisRunner",
232 ],
233 routing_status: "declaredOnly",
234 }
235}
236
237pub fn summarize_omena_query_fragment_bundle(input: &EngineInputV2) -> OmenaQueryFragmentBundleV0 {
238 OmenaQueryFragmentBundleV0 {
239 schema_version: "0",
240 product: "omena-query.fragment-bundle",
241 input_version: input.version.clone(),
242 expression_semantics: summarize_omena_query_expression_semantics_query_fragments(input),
243 source_resolution: summarize_omena_query_source_resolution_query_fragments(input),
244 selector_usage: summarize_omena_query_selector_usage_query_fragments(input),
245 }
246}
247
248pub fn summarize_omena_query_expression_semantics_query_fragments(
249 input: &EngineInputV2,
250) -> ExpressionSemanticsQueryFragmentsV0 {
251 summarize_expression_semantics_query_fragments_input(input)
252}
253
254pub fn summarize_omena_query_expression_domain_flow_analysis(
255 input: &EngineInputV2,
256) -> ExpressionDomainFlowAnalysisV0 {
257 summarize_expression_domain_flow_analysis_input(input)
258}
259
260pub fn summarize_omena_query_source_resolution_query_fragments(
261 input: &EngineInputV2,
262) -> SourceResolutionQueryFragmentsV0 {
263 summarize_omena_resolver_query_fragments(input)
264}
265
266pub fn summarize_omena_query_selector_usage_query_fragments(
267 input: &EngineInputV2,
268) -> SelectorUsageQueryFragmentsV0 {
269 summarize_selector_usage_query_fragments_input(input)
270}
271
272pub fn summarize_omena_query_source_resolution_canonical_producer_signal(
273 input: &EngineInputV2,
274) -> SourceResolutionCanonicalProducerSignalV0 {
275 summarize_omena_resolver_canonical_producer_signal(input)
276}
277
278pub fn summarize_omena_query_expression_semantics_canonical_producer_signal(
279 input: &EngineInputV2,
280) -> ExpressionSemanticsCanonicalProducerSignalV0 {
281 summarize_expression_semantics_canonical_producer_signal_input(input)
282}
283
284pub fn summarize_omena_query_selector_usage_canonical_producer_signal(
285 input: &EngineInputV2,
286) -> SelectorUsageCanonicalProducerSignalV0 {
287 summarize_selector_usage_canonical_producer_signal_input(input)
288}
289
290pub fn summarize_omena_query_style_semantic_graph_from_source(
291 style_path: &str,
292 style_source: &str,
293 input: &EngineInputV2,
294) -> Option<StyleSemanticGraphSummaryV0> {
295 summarize_omena_bridge_style_semantic_graph_from_source(style_path, style_source, input)
296}
297
298pub fn summarize_omena_query_style_semantic_graph_batch_from_sources<'a>(
299 styles: impl IntoIterator<Item = (&'a str, &'a str)>,
300 input: &EngineInputV2,
301) -> OmenaQueryStyleSemanticGraphBatchOutputV0 {
302 let graphs = styles
303 .into_iter()
304 .map(
305 |(style_path, style_source)| OmenaQueryStyleSemanticGraphBatchEntryV0 {
306 style_path: style_path.to_string(),
307 graph: summarize_omena_query_style_semantic_graph_from_source(
308 style_path,
309 style_source,
310 input,
311 ),
312 },
313 )
314 .collect::<Vec<_>>();
315
316 OmenaQueryStyleSemanticGraphBatchOutputV0 {
317 schema_version: "0",
318 product: "omena-semantic.style-semantic-graph-batch",
319 graphs,
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use engine_input_producers::{
326 ClassExpressionInputV2, EngineInputV2, PositionV2, RangeV2, SourceAnalysisInputV2,
327 SourceDocumentV2, StringTypeFactsV2, StyleAnalysisInputV2, StyleDocumentV2,
328 StyleSelectorV2, TypeFactEntryV2,
329 };
330
331 use super::{
332 SelectedQueryAdapterCapabilitiesV0, summarize_omena_query_boundary,
333 summarize_omena_query_expression_domain_flow_analysis,
334 summarize_omena_query_expression_semantics_canonical_producer_signal,
335 summarize_omena_query_expression_semantics_query_fragments,
336 summarize_omena_query_fragment_bundle,
337 summarize_omena_query_selected_query_adapter_capabilities,
338 summarize_omena_query_selector_usage_canonical_producer_signal,
339 summarize_omena_query_selector_usage_query_fragments,
340 summarize_omena_query_source_resolution_canonical_producer_signal,
341 summarize_omena_query_source_resolution_query_fragments,
342 summarize_omena_query_style_semantic_graph_batch_from_sources,
343 summarize_omena_query_style_semantic_graph_from_source,
344 };
345
346 #[test]
347 fn summarizes_query_boundary_over_producer_fragments() {
348 let input = sample_input();
349 let summary = summarize_omena_query_boundary(&input);
350
351 assert_eq!(summary.schema_version, "0");
352 assert_eq!(summary.product, "omena-query.boundary");
353 assert_eq!(summary.query_engine_name, "omena-query");
354 assert_eq!(summary.input_version, "2");
355 assert_eq!(
356 summary.abstract_value_domain.product,
357 "omena-abstract-value.domain"
358 );
359 assert_eq!(
360 summary.selected_query_adapter_capabilities.product,
361 "omena-query.selected-query-adapter-capabilities"
362 );
363 assert_eq!(summary.expression_semantics_query_count, 2);
364 assert_eq!(summary.source_resolution_query_count, 2);
365 assert_eq!(summary.selector_usage_query_count, 2);
366 assert_eq!(summary.total_query_count, 6);
367 assert!(
368 summary
369 .ready_surfaces
370 .contains(&"abstractValueProjectionContract")
371 );
372 assert!(
373 summary
374 .ready_surfaces
375 .contains(&"sourceResolutionResolverBoundary")
376 );
377 assert!(
378 summary
379 .delegated_fragment_products
380 .contains(&"omena-resolver.boundary")
381 );
382 assert!(
383 summary
384 .delegated_fragment_products
385 .contains(&"engine-input-producers.expression-domain-flow-analysis")
386 );
387 assert!(
388 summary
389 .ready_surfaces
390 .contains(&"expressionDomainFlowAnalysisBoundary")
391 );
392 assert!(
393 summary
394 .cme_coupled_surfaces
395 .contains(&"producerQueryFragments")
396 );
397 }
398
399 #[test]
400 fn bundles_expression_source_and_selector_query_fragments() {
401 let input = sample_input();
402 let bundle = summarize_omena_query_fragment_bundle(&input);
403
404 assert_eq!(bundle.schema_version, "0");
405 assert_eq!(bundle.product, "omena-query.fragment-bundle");
406 assert_eq!(bundle.input_version, "2");
407 assert_eq!(bundle.expression_semantics.fragments.len(), 2);
408 assert_eq!(bundle.expression_semantics.fragments[0].query_id, "expr-1");
409 assert_eq!(bundle.source_resolution.fragments.len(), 2);
410 assert_eq!(bundle.source_resolution.fragments[1].query_id, "expr-2");
411 assert_eq!(bundle.selector_usage.fragments.len(), 2);
412 assert_eq!(bundle.selector_usage.fragments[0].query_id, "btn-active");
413
414 let expression = summarize_omena_query_expression_semantics_query_fragments(&input);
415 let source = summarize_omena_query_source_resolution_query_fragments(&input);
416 let selector = summarize_omena_query_selector_usage_query_fragments(&input);
417
418 assert_eq!(expression.schema_version, "0");
419 assert_eq!(source.schema_version, "0");
420 assert_eq!(selector.schema_version, "0");
421 assert_eq!(expression.input_version, "2");
422 assert_eq!(source.input_version, "2");
423 assert_eq!(selector.input_version, "2");
424 assert_eq!(
425 expression.fragments.len(),
426 bundle.expression_semantics.fragments.len()
427 );
428 assert_eq!(
429 source.fragments.len(),
430 bundle.source_resolution.fragments.len()
431 );
432 assert_eq!(
433 selector.fragments.len(),
434 bundle.selector_usage.fragments.len()
435 );
436 }
437
438 #[test]
439 fn declares_selected_query_adapter_capabilities_without_flipping_runtime_routing() {
440 let summary = summarize_omena_query_selected_query_adapter_capabilities();
441
442 assert_eq!(summary.schema_version, "0");
443 assert_eq!(
444 summary.product,
445 "omena-query.selected-query-adapter-capabilities"
446 );
447 assert_eq!(summary.default_candidate_backend, "rust-selected-query");
448 assert_eq!(summary.routing_status, "declaredOnly");
449
450 let unified = backend(&summary, "rust-selected-query");
451 assert!(unified.is_some());
452 let Some(unified) = unified else {
453 return;
454 };
455 assert!(unified.source_resolution);
456 assert!(unified.expression_semantics);
457 assert!(unified.selector_usage);
458 assert!(unified.style_semantic_graph);
459
460 let source_only = backend(&summary, "rust-source-resolution");
461 assert!(source_only.is_some());
462 let Some(source_only) = source_only else {
463 return;
464 };
465 assert!(source_only.source_resolution);
466 assert!(!source_only.expression_semantics);
467 assert!(!source_only.selector_usage);
468 assert!(!source_only.style_semantic_graph);
469
470 assert!(
471 summary
472 .runner_commands
473 .iter()
474 .any(|command| command.command == "input-expression-domain-flow-analysis")
475 );
476 assert!(
477 summary
478 .runner_commands
479 .iter()
480 .any(|command| command.command == "style-semantic-graph-batch")
481 );
482 assert!(
483 summary
484 .expression_semantics_payload_contracts
485 .contains(&"valueDomainDerivation")
486 );
487 assert!(summary.adapter_readiness.contains(&"runnerCommandContract"));
488 assert!(
489 summary
490 .adapter_readiness
491 .contains(&"canonicalProducerWrapperBoundary")
492 );
493 assert!(
494 summary
495 .adapter_readiness
496 .contains(&"styleSemanticGraphBridgeBoundary")
497 );
498 assert!(
499 summary
500 .adapter_readiness
501 .contains(&"expressionDomainFlowAnalysisRunner")
502 );
503 }
504
505 #[test]
506 fn owns_expression_domain_flow_analysis_wrapper_without_changing_product() {
507 let input = sample_input();
508 let summary = summarize_omena_query_expression_domain_flow_analysis(&input);
509
510 assert_eq!(summary.schema_version, "0");
511 assert_eq!(
512 summary.product,
513 "engine-input-producers.expression-domain-flow-analysis"
514 );
515 assert_eq!(summary.input_version, "2");
516 assert_eq!(summary.analyses.len(), 2);
517 assert!(
518 summary
519 .analyses
520 .iter()
521 .all(|entry| entry.analysis.product == "omena-abstract-value.flow-analysis")
522 );
523 assert!(
524 summary
525 .analyses
526 .iter()
527 .all(|entry| entry.analysis.converged)
528 );
529 }
530
531 #[test]
532 fn owns_selected_query_canonical_producer_wrappers_without_changing_products() {
533 let input = sample_input();
534
535 let source = summarize_omena_query_source_resolution_canonical_producer_signal(&input);
536 assert_eq!(source.schema_version, "0");
537 assert_eq!(source.input_version, "2");
538 assert_eq!(source.canonical_bundle.query_fragments.len(), 2);
539 assert_eq!(source.evaluator_candidates.results.len(), 2);
540
541 let expression =
542 summarize_omena_query_expression_semantics_canonical_producer_signal(&input);
543 assert_eq!(expression.schema_version, "0");
544 assert_eq!(expression.input_version, "2");
545 assert_eq!(expression.canonical_bundle.query_fragments.len(), 2);
546 assert_eq!(expression.evaluator_candidates.results.len(), 2);
547 assert_eq!(
548 expression.evaluator_candidates.results[0]
549 .payload
550 .value_domain_derivation
551 .product,
552 "omena-abstract-value.reduced-class-value-derivation"
553 );
554 assert_eq!(
555 expression.evaluator_candidates.results[0]
556 .payload
557 .value_domain_derivation
558 .reduced_kind,
559 "prefixSuffix"
560 );
561
562 let selector = summarize_omena_query_selector_usage_canonical_producer_signal(&input);
563 assert_eq!(selector.schema_version, "0");
564 assert_eq!(selector.input_version, "2");
565 assert_eq!(selector.canonical_bundle.query_fragments.len(), 2);
566 assert_eq!(selector.evaluator_candidates.results.len(), 2);
567 }
568
569 #[test]
570 fn owns_style_semantic_graph_adapter_boundary_without_changing_graph_product() {
571 let input = sample_input();
572 let graph = summarize_omena_query_style_semantic_graph_from_source(
573 "/tmp/App.module.scss",
574 ".btn-active { color: red; }",
575 &input,
576 );
577 assert!(graph.is_some());
578 let Some(graph) = graph else {
579 return;
580 };
581 assert_eq!(graph.schema_version, "0");
582 assert_eq!(graph.product, "omena-semantic.style-semantic-graph");
583 assert_eq!(graph.selector_identity_engine.canonical_ids.len(), 1);
584
585 let batch = summarize_omena_query_style_semantic_graph_batch_from_sources(
586 [
587 ("/tmp/App.module.scss", ".btn-active { color: red; }"),
588 ("/tmp/Card.module.scss", ".card-header { color: blue; }"),
589 ],
590 &input,
591 );
592 assert_eq!(batch.schema_version, "0");
593 assert_eq!(batch.product, "omena-semantic.style-semantic-graph-batch");
594 assert_eq!(batch.graphs.len(), 2);
595 assert_eq!(batch.graphs[0].style_path, "/tmp/App.module.scss");
596 assert!(batch.graphs[0].graph.is_some());
597 assert!(batch.graphs[1].graph.is_some());
598 }
599
600 fn backend<'a>(
601 summary: &'a SelectedQueryAdapterCapabilitiesV0,
602 backend_kind: &str,
603 ) -> Option<&'a super::SelectedQueryBackendCapabilityV0> {
604 summary
605 .backend_kinds
606 .iter()
607 .find(|backend| backend.backend_kind == backend_kind)
608 }
609
610 fn sample_input() -> EngineInputV2 {
611 EngineInputV2 {
612 version: "2".to_string(),
613 sources: vec![SourceAnalysisInputV2 {
614 document: SourceDocumentV2 {
615 class_expressions: vec![
616 ClassExpressionInputV2 {
617 id: "expr-1".to_string(),
618 kind: "symbolRef".to_string(),
619 scss_module_path: "/tmp/App.module.scss".to_string(),
620 range: range(4, 12, 4, 16),
621 class_name: None,
622 root_binding_decl_id: Some("decl-1".to_string()),
623 access_path: None,
624 },
625 ClassExpressionInputV2 {
626 id: "expr-2".to_string(),
627 kind: "styleAccess".to_string(),
628 scss_module_path: "/tmp/Card.module.scss".to_string(),
629 range: range(6, 9, 6, 20),
630 class_name: Some("card-header".to_string()),
631 root_binding_decl_id: None,
632 access_path: Some(vec!["card".to_string(), "header".to_string()]),
633 },
634 ],
635 },
636 }],
637 styles: vec![
638 StyleAnalysisInputV2 {
639 file_path: "/tmp/App.module.scss".to_string(),
640 document: StyleDocumentV2 {
641 selectors: vec![StyleSelectorV2 {
642 name: "btn-active".to_string(),
643 view_kind: "canonical".to_string(),
644 canonical_name: Some("btn-active".to_string()),
645 range: range(1, 1, 1, 12),
646 nested_safety: Some("safe".to_string()),
647 composes: None,
648 bem_suffix: None,
649 }],
650 },
651 },
652 StyleAnalysisInputV2 {
653 file_path: "/tmp/Card.module.scss".to_string(),
654 document: StyleDocumentV2 {
655 selectors: vec![StyleSelectorV2 {
656 name: "card-header".to_string(),
657 view_kind: "canonical".to_string(),
658 canonical_name: Some("card-header".to_string()),
659 range: range(3, 1, 3, 13),
660 nested_safety: Some("unsafe".to_string()),
661 composes: None,
662 bem_suffix: None,
663 }],
664 },
665 },
666 ],
667 type_facts: vec![
668 TypeFactEntryV2 {
669 file_path: "/tmp/App.tsx".to_string(),
670 expression_id: "expr-1".to_string(),
671 facts: StringTypeFactsV2 {
672 kind: "constrained".to_string(),
673 constraint_kind: Some("prefixSuffix".to_string()),
674 values: None,
675 prefix: Some("btn-".to_string()),
676 suffix: Some("-active".to_string()),
677 min_len: Some(10),
678 max_len: None,
679 char_must: None,
680 char_may: None,
681 may_include_other_chars: None,
682 },
683 },
684 TypeFactEntryV2 {
685 file_path: "/tmp/Card.tsx".to_string(),
686 expression_id: "expr-2".to_string(),
687 facts: StringTypeFactsV2 {
688 kind: "finiteSet".to_string(),
689 constraint_kind: None,
690 values: Some(vec!["card-header".to_string(), "card-body".to_string()]),
691 prefix: None,
692 suffix: None,
693 min_len: None,
694 max_len: None,
695 char_must: None,
696 char_may: None,
697 may_include_other_chars: None,
698 },
699 },
700 ],
701 }
702 }
703
704 fn range(
705 start_line: usize,
706 start_character: usize,
707 end_line: usize,
708 end_character: usize,
709 ) -> RangeV2 {
710 RangeV2 {
711 start: PositionV2 {
712 line: start_line,
713 character: start_character,
714 },
715 end: PositionV2 {
716 line: end_line,
717 character: end_character,
718 },
719 }
720 }
721}