1use std::fmt;
2use std::path::{Component, Path};
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct SemanticId(String);
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct EffectId(SemanticId);
24
25#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
30#[serde(transparent)]
31pub struct EffectStatementId(SemanticId);
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
38#[serde(transparent)]
39pub struct ContextId(SemanticId);
40
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
43#[serde(transparent)]
44pub struct ProviderId(SemanticId);
45
46#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
48#[serde(transparent)]
49pub struct ConsumerId(SemanticId);
50
51#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
53#[serde(transparent)]
54pub struct FormId(SemanticId);
55
56#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
61#[serde(transparent)]
62pub struct FormInstanceId(SemanticId);
63
64#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
69#[serde(transparent)]
70pub struct FormDeclarationCandidateId(SemanticId);
71
72#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
75#[serde(transparent)]
76pub struct FormFieldDeclarationCandidateId(SemanticId);
77
78#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
81#[serde(transparent)]
82pub struct FormFieldBindingCandidateId(SemanticId);
83
84#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
86#[serde(transparent)]
87pub struct FieldId(SemanticId);
88
89#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
92#[serde(transparent)]
93pub struct FieldBindingId(SemanticId);
94
95#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
98#[serde(transparent)]
99pub struct SubmissionHostCandidateId(SemanticId);
100
101#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
103#[serde(transparent)]
104pub struct SubmissionHostId(SemanticId);
105
106#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
109#[serde(transparent)]
110pub struct FormOwnershipGraphId(SemanticId);
111
112#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
114#[serde(transparent)]
115pub struct ValidationRuleCandidateId(SemanticId);
116
117#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
119#[serde(transparent)]
120pub struct ValidationRuleId(SemanticId);
121
122#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
124#[serde(transparent)]
125pub struct ValidationGraphId(SemanticId);
126
127#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
131#[serde(transparent)]
132pub struct ValidationPlanId(SemanticId);
133
134#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
136#[serde(transparent)]
137pub struct FieldDependencyId(SemanticId);
138
139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
141#[serde(transparent)]
142pub struct ValidationDependencyCycleId(SemanticId);
143
144#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
146#[serde(transparent)]
147pub struct DirtyTrackingPlanId(SemanticId);
148
149#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
151#[serde(transparent)]
152pub struct TouchedTrackingPlanId(SemanticId);
153
154#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
157#[serde(transparent)]
158pub struct FieldTrackingId(SemanticId);
159
160#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
162#[serde(transparent)]
163pub struct SubmissionDeclarationCandidateId(SemanticId);
164
165#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
167#[serde(transparent)]
168pub struct SubmissionPlanId(SemanticId);
169
170#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
172#[serde(transparent)]
173pub struct SerializationPlanId(SemanticId);
174
175#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
176#[serde(transparent)]
177pub struct ResetPlanId(SemanticId);
178
179#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
180#[serde(transparent)]
181pub struct FieldResetOperationId(SemanticId);
182
183#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
184#[serde(transparent)]
185pub struct FormFieldValueSlotId(SemanticId);
186#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
187#[serde(transparent)]
188pub struct FormFieldDirtySlotId(SemanticId);
189#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
190#[serde(transparent)]
191pub struct FormFieldTouchedSlotId(SemanticId);
192#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
193#[serde(transparent)]
194pub struct FormFieldValidationSlotId(SemanticId);
195#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
196#[serde(transparent)]
197pub struct FormValidationAggregateSlotId(SemanticId);
198#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
199#[serde(transparent)]
200pub struct FormSubmissionStateSlotId(SemanticId);
201
202#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
204#[serde(transparent)]
205pub struct SlotId(SemanticId);
206
207#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
212#[serde(transparent)]
213pub struct SlotDeclarationCandidateId(SemanticId);
214
215#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
217#[serde(transparent)]
218pub struct ComponentInvocationId(SemanticId);
219
220#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
222#[serde(transparent)]
223pub struct TemplatePositionId(SemanticId);
224
225#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
227#[serde(transparent)]
228pub struct SlotContentFragmentId(SemanticId);
229
230#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
232#[serde(transparent)]
233pub struct SlotOutletId(SemanticId);
234
235#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
237#[serde(transparent)]
238pub struct SlotBindingId(SemanticId);
239
240#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
242#[serde(transparent)]
243pub struct ComponentRootId(SemanticId);
244
245#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
247#[serde(transparent)]
248pub struct ComponentInstanceId(SemanticId);
249
250#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
252#[serde(transparent)]
253pub struct ComponentStructuralRegionId(SemanticId);
254
255#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
260#[serde(transparent)]
261pub struct ResourceId(SemanticId);
262
263#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
265#[serde(transparent)]
266pub struct ResourceActivationId(SemanticId);
267
268#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
273#[serde(transparent)]
274pub struct ContextDeclarationCandidateId(SemanticId);
275
276#[derive(Debug, Clone, PartialEq, Eq)]
278pub enum SemanticOwner {
279 Application,
280 Entity(SemanticId),
281}
282
283impl SemanticOwner {
284 #[must_use]
285 pub fn entity(id: SemanticId) -> Self {
286 Self::Entity(id)
287 }
288
289 #[must_use]
290 pub fn entity_id(&self) -> Option<&SemanticId> {
291 match self {
292 Self::Application => None,
293 Self::Entity(id) => Some(id),
294 }
295 }
296}
297
298impl SemanticId {
299 #[must_use]
300 pub fn component_root(component: &Self) -> Self {
301 Self(format!("root:{}", component.as_str()))
302 }
303 #[must_use]
304 pub fn component(element_name: Option<&str>, class_name: &str) -> Self {
305 Self(format!("component:{}", element_name.unwrap_or(class_name)))
306 }
307
308 #[must_use]
309 pub fn component_in_module(
310 module_path: impl AsRef<Path>,
311 element_name: Option<&str>,
312 class_name: &str,
313 ) -> Self {
314 Self(format!(
315 "module:{}/component:{}",
316 normalized_module_path(module_path.as_ref()),
317 element_name.unwrap_or(class_name)
318 ))
319 }
320
321 #[must_use]
322 pub fn type_alias_in_module(module_path: impl AsRef<Path>, name: &str) -> Self {
323 Self(format!(
324 "module:{}/type-alias:{name}",
325 normalized_module_path(module_path.as_ref())
326 ))
327 }
328
329 #[must_use]
330 pub fn state_field(&self, name: &str) -> Self {
331 self.child("state", name)
332 }
333
334 #[must_use]
335 pub fn method(&self, name: &str) -> Self {
336 self.child("method", name)
337 }
338
339 #[must_use]
340 pub fn computed(&self, name: &str) -> Self {
341 self.child("computed", name)
342 }
343
344 #[must_use]
345 pub fn effect(&self, name: &str) -> Self {
346 self.child("effect", name)
347 }
348
349 #[must_use]
350 pub fn context(&self, name: &str) -> Self {
351 self.child("context", name)
352 }
353
354 #[must_use]
355 pub fn context_field(&self, name: &str) -> Self {
356 self.child("context-field", name)
357 }
358
359 #[must_use]
360 pub fn provider(&self, name: &str) -> Self {
361 self.child("provider", name)
362 }
363
364 #[must_use]
365 pub fn provider_field(&self, name: &str) -> Self {
366 self.child("provider-field", name)
367 }
368
369 #[must_use]
370 pub fn consumer(&self, name: &str) -> Self {
371 self.child("consumer", name)
372 }
373
374 #[must_use]
375 pub fn consumer_field(&self, name: &str) -> Self {
376 self.child("consumer-field", name)
377 }
378
379 #[must_use]
380 pub fn form(&self, name: &str) -> Self {
381 self.child("form", name)
382 }
383
384 #[must_use]
385 pub fn resource(&self, name: &str) -> Self {
386 self.child("resource", name)
387 }
388
389 #[must_use]
390 pub fn route_loader(&self, name: &str) -> Self {
391 self.child("route-loader", name)
392 }
393
394 #[must_use]
395 pub fn server_action(&self, name: &str) -> Self {
396 self.child("server-action", name)
397 }
398
399 #[must_use]
400 pub fn form_field(&self, name: &str) -> Self {
401 self.child("form-field", name)
402 }
403
404 #[must_use]
405 pub fn form_declaration_candidate_in_module(
406 module_path: impl AsRef<Path>,
407 position: usize,
408 ) -> Self {
409 Self(format!(
410 "module:{}/form-declaration:{position}",
411 normalized_module_path(module_path.as_ref())
412 ))
413 }
414
415 #[must_use]
416 pub fn form_field_declaration_candidate_in_module(
417 module_path: impl AsRef<Path>,
418 position: usize,
419 ) -> Self {
420 Self(format!(
421 "module:{}/form-field-declaration:{position}",
422 normalized_module_path(module_path.as_ref())
423 ))
424 }
425
426 #[must_use]
427 pub fn form_field_binding_candidate_in_module(
428 module_path: impl AsRef<Path>,
429 position: usize,
430 ) -> Self {
431 Self(format!(
432 "module:{}/form-field-binding-candidate:{position}",
433 normalized_module_path(module_path.as_ref())
434 ))
435 }
436
437 #[must_use]
438 pub fn validation_rule_candidate_in_module(
439 module_path: impl AsRef<Path>,
440 position: usize,
441 ) -> Self {
442 Self(format!(
443 "module:{}/validation-rule-candidate:{position}",
444 normalized_module_path(module_path.as_ref())
445 ))
446 }
447
448 #[must_use]
449 pub fn submission_declaration_candidate_in_module(
450 module_path: impl AsRef<Path>,
451 position: usize,
452 ) -> Self {
453 Self(format!(
454 "module:{}/submission-declaration-candidate:{position}",
455 normalized_module_path(module_path.as_ref())
456 ))
457 }
458
459 #[must_use]
460 pub fn field(&self, name: &str) -> Self {
461 self.child("field", name)
462 }
463
464 #[must_use]
465 pub fn validation_rule(&self, ordinal: usize) -> Self {
466 self.child("validation-rule", &ordinal.to_string())
467 }
468
469 #[must_use]
470 pub fn validation_plan(&self) -> Self {
471 Self(format!("{}/validation-plan", self.as_str()))
472 }
473
474 #[must_use]
475 pub fn field_dependency(&self, source_field: &Self) -> Self {
476 self.child("field-dependency", source_field.as_str())
477 }
478
479 #[must_use]
480 pub fn dirty_tracking_plan(&self) -> Self {
481 Self(format!("{}/dirty-plan", self.as_str()))
482 }
483
484 #[must_use]
485 pub fn touched_tracking_plan(&self) -> Self {
486 Self(format!("{}/touched-plan", self.as_str()))
487 }
488
489 #[must_use]
490 pub fn field_tracking(&self) -> Self {
491 Self(format!("{}/tracking", self.as_str()))
492 }
493
494 #[must_use]
495 pub fn submission_plan(&self) -> Self {
496 Self(format!("{}/submission-plan", self.as_str()))
497 }
498
499 #[must_use]
500 pub fn serialization_plan(&self) -> Self {
501 Self(format!("{}/serialization-plan", self.as_str()))
502 }
503
504 #[must_use]
505 pub fn reset_plan(&self) -> Self {
506 Self(format!("{}/reset-plan", self.as_str()))
507 }
508
509 #[must_use]
510 pub fn field_reset_operation(&self, field: &Self) -> Self {
511 self.child("field-reset", field.as_str())
512 }
513
514 #[must_use]
515 pub fn form_instance(&self, form: &str) -> Self {
516 self.child("form-instance", form)
517 }
518
519 #[must_use]
520 pub fn slot(&self, name: &str) -> Self {
521 self.child("slot", name)
522 }
523
524 #[must_use]
525 pub fn slot_field(&self, name: &str) -> Self {
526 self.child("slot-field", name)
527 }
528
529 #[must_use]
530 pub fn slot_declaration_candidate(&self, position: usize) -> Self {
531 self.child("slot-declaration", &position.to_string())
532 }
533
534 #[must_use]
535 pub fn context_declaration_candidate(&self, position: usize) -> Self {
536 self.child("context-declaration", &position.to_string())
537 }
538
539 #[must_use]
541 pub fn context_provider_function(&self) -> Self {
542 self.child("context-source", "evaluation")
543 }
544
545 #[must_use]
547 pub fn context_default_function(&self) -> Self {
548 self.child("context-default-source", "evaluation")
549 }
550
551 #[must_use]
553 pub fn context_consumer_load(&self) -> Self {
554 self.child("context-load", "value")
555 }
556
557 #[must_use]
558 pub fn effect_activation_slot(&self, name: &str) -> Self {
559 self.child("effect-activation", name)
560 }
561
562 #[must_use]
563 pub fn effect_statement(&self, index: usize) -> Self {
564 self.child("statement", &index.to_string())
565 }
566
567 #[must_use]
568 pub fn action(&self, method: &str, index: usize) -> Self {
569 self.child("action", &format!("{method}:{index}"))
570 }
571
572 #[must_use]
573 pub fn action_batch(&self, method: &str) -> Self {
574 self.child("action-batch", method)
575 }
576
577 #[must_use]
578 pub fn opaque_activation(&self, method: &str) -> Self {
579 self.child("opaque-activation", method)
580 }
581
582 #[must_use]
583 pub fn local_variable(&self, name: &str, index: usize) -> Self {
584 self.child("local", &format!("{name}:{index}"))
585 }
586
587 #[must_use]
588 pub fn parameter(&self, name: &str, index: usize) -> Self {
589 self.child("parameter", &format!("{name}:{index}"))
590 }
591
592 #[must_use]
593 pub fn expression(&self, path: &str) -> Self {
594 self.child("expression", path)
595 }
596
597 #[must_use]
598 pub fn semantic_type(&self) -> Self {
599 self.child("type", "semantic")
600 }
601
602 #[must_use]
603 pub fn event_handler(&self, event: &str, index: usize) -> Self {
604 self.child("event", &format!("{event}:{index}"))
605 }
606
607 #[must_use]
608 pub fn template(&self) -> Self {
609 self.child("template", "render")
610 }
611
612 #[must_use]
613 pub fn template_entity(&self, kind: &str, path: &str) -> Self {
614 self.child(kind, path)
615 }
616
617 #[must_use]
618 pub fn component_invocation(&self, target: &str) -> Self {
619 self.child("component-invocation", target)
620 }
621
622 #[must_use]
624 pub fn layout_composition_invocation(&self, route: &Self, position: usize) -> Self {
625 self.child(
626 "layout-composition",
627 &format!("{}:{position}", route.as_str()),
628 )
629 }
630
631 #[must_use]
632 pub fn template_position(&self) -> Self {
633 self.child("template-position", "authored")
634 }
635
636 #[must_use]
637 pub fn field_binding(&self, field: &str) -> Self {
638 self.child("field-binding", field)
639 }
640
641 #[must_use]
642 pub fn slot_content_fragment(&self, slot_name: &str) -> Self {
643 self.child("slot-content", slot_name)
644 }
645
646 #[must_use]
647 pub fn slot_outlet(&self, slot_name: &str) -> Self {
648 self.child("slot-outlet", slot_name)
649 }
650
651 #[must_use]
652 pub fn slot_binding(&self, binding_key: &str) -> Self {
653 self.child("slot-binding", binding_key)
654 }
655
656 #[must_use]
657 pub fn component_instance_invocation(&self, invocation: &str) -> Self {
658 self.child("invocation", invocation)
659 }
660
661 #[must_use]
662 pub fn component_structural_region(&self, kind: &str) -> Self {
663 self.child("component-structural-region", kind)
664 }
665
666 #[must_use]
667 pub fn as_str(&self) -> &str {
668 &self.0
669 }
670
671 fn child(&self, kind: &str, name: &str) -> Self {
672 Self(format!("{}/{kind}:{name}", self.0))
673 }
674}
675
676impl EffectId {
677 #[must_use]
678 pub fn from_semantic(id: &SemanticId) -> Self {
679 Self(id.clone())
680 }
681
682 #[must_use]
683 pub fn as_str(&self) -> &str {
684 self.0.as_str()
685 }
686}
687
688impl EffectStatementId {
689 #[must_use]
690 pub fn from_semantic(id: &SemanticId) -> Self {
691 Self(id.clone())
692 }
693
694 #[must_use]
695 pub fn as_str(&self) -> &str {
696 self.0.as_str()
697 }
698}
699
700impl ContextId {
701 #[must_use]
702 pub fn for_component(component: &SemanticId, name: &str) -> Self {
703 Self(component.context(name))
704 }
705
706 #[must_use]
707 pub const fn as_semantic_id(&self) -> &SemanticId {
708 &self.0
709 }
710
711 #[must_use]
712 pub fn as_str(&self) -> &str {
713 self.0.as_str()
714 }
715}
716
717impl ProviderId {
718 #[must_use]
719 pub fn for_component(component: &SemanticId, name: &str) -> Self {
720 Self(component.provider(name))
721 }
722
723 #[must_use]
724 pub const fn as_semantic_id(&self) -> &SemanticId {
725 &self.0
726 }
727
728 #[must_use]
729 pub fn as_str(&self) -> &str {
730 self.0.as_str()
731 }
732}
733
734impl ConsumerId {
735 #[must_use]
736 pub fn for_component(component: &SemanticId, name: &str) -> Self {
737 Self(component.consumer(name))
738 }
739
740 #[must_use]
741 pub const fn as_semantic_id(&self) -> &SemanticId {
742 &self.0
743 }
744
745 #[must_use]
746 pub fn as_str(&self) -> &str {
747 self.0.as_str()
748 }
749}
750
751impl FormId {
752 #[must_use]
753 pub fn for_owner(owner: &SemanticId, name: &str) -> Self {
754 Self(owner.form(name))
755 }
756
757 #[must_use]
758 pub const fn as_semantic_id(&self) -> &SemanticId {
759 &self.0
760 }
761
762 #[must_use]
763 pub fn as_str(&self) -> &str {
764 self.0.as_str()
765 }
766}
767
768impl ResourceId {
769 #[must_use]
770 pub fn for_owner(owner: &SemanticId, name: &str) -> Self {
771 Self(owner.resource(name))
772 }
773
774 #[must_use]
775 pub const fn as_semantic_id(&self) -> &SemanticId {
776 &self.0
777 }
778
779 #[must_use]
780 pub fn as_str(&self) -> &str {
781 self.0.as_str()
782 }
783}
784
785impl ResourceActivationId {
786 #[must_use]
787 pub fn for_component_instance(instance: &ComponentInstanceId, resource: &ResourceId) -> Self {
788 Self(
789 instance
790 .as_semantic_id()
791 .child("resource-activation", resource.as_str()),
792 )
793 }
794
795 #[must_use]
796 pub const fn as_semantic_id(&self) -> &SemanticId {
797 &self.0
798 }
799
800 #[must_use]
801 pub fn as_str(&self) -> &str {
802 self.0.as_str()
803 }
804}
805
806impl FormInstanceId {
807 #[must_use]
808 pub fn for_component_instance(instance: &ComponentInstanceId, form: &FormId) -> Self {
809 Self(instance.as_semantic_id().form_instance(form.as_str()))
810 }
811
812 #[must_use]
813 pub const fn as_semantic_id(&self) -> &SemanticId {
814 &self.0
815 }
816
817 #[must_use]
818 pub fn as_str(&self) -> &str {
819 self.0.as_str()
820 }
821}
822
823impl FormDeclarationCandidateId {
824 #[must_use]
825 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
826 Self(SemanticId::form_declaration_candidate_in_module(
827 module_path,
828 position,
829 ))
830 }
831
832 #[must_use]
833 pub const fn as_semantic_id(&self) -> &SemanticId {
834 &self.0
835 }
836
837 #[must_use]
838 pub fn as_str(&self) -> &str {
839 self.0.as_str()
840 }
841}
842
843impl FormFieldDeclarationCandidateId {
844 #[must_use]
845 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
846 Self(SemanticId::form_field_declaration_candidate_in_module(
847 module_path,
848 position,
849 ))
850 }
851
852 #[must_use]
853 pub const fn as_semantic_id(&self) -> &SemanticId {
854 &self.0
855 }
856
857 #[must_use]
858 pub fn as_str(&self) -> &str {
859 self.0.as_str()
860 }
861}
862
863impl FormFieldBindingCandidateId {
864 #[must_use]
865 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
866 Self(SemanticId::form_field_binding_candidate_in_module(
867 module_path,
868 position,
869 ))
870 }
871
872 #[must_use]
873 pub const fn as_semantic_id(&self) -> &SemanticId {
874 &self.0
875 }
876
877 #[must_use]
878 pub fn as_str(&self) -> &str {
879 self.0.as_str()
880 }
881}
882
883impl FieldId {
884 #[must_use]
885 pub fn for_form(form: &FormId, name: &str) -> Self {
886 Self(form.as_semantic_id().field(name))
887 }
888
889 #[must_use]
890 pub const fn as_semantic_id(&self) -> &SemanticId {
891 &self.0
892 }
893
894 #[must_use]
895 pub fn as_str(&self) -> &str {
896 self.0.as_str()
897 }
898}
899
900impl FieldBindingId {
901 #[must_use]
902 pub fn for_control(control: &SemanticId, field: &FieldId) -> Self {
903 Self(control.field_binding(field.as_str()))
904 }
905
906 #[must_use]
907 pub const fn as_semantic_id(&self) -> &SemanticId {
908 &self.0
909 }
910
911 #[must_use]
912 pub fn as_str(&self) -> &str {
913 self.0.as_str()
914 }
915}
916
917impl SubmissionHostCandidateId {
918 #[must_use]
919 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
920 Self(
921 SemanticId::form_field_binding_candidate_in_module(module_path, position)
922 .child("submission-host-candidate", "host"),
923 )
924 }
925
926 #[must_use]
927 pub const fn as_semantic_id(&self) -> &SemanticId {
928 &self.0
929 }
930
931 #[must_use]
932 pub fn as_str(&self) -> &str {
933 self.0.as_str()
934 }
935}
936
937impl SubmissionHostId {
938 #[must_use]
939 pub fn for_element(element: &SemanticId, form: &FormId) -> Self {
940 Self(element.child("submission-host", form.as_str()))
941 }
942
943 #[must_use]
944 pub const fn as_semantic_id(&self) -> &SemanticId {
945 &self.0
946 }
947
948 #[must_use]
949 pub fn as_str(&self) -> &str {
950 self.0.as_str()
951 }
952}
953
954impl FormOwnershipGraphId {
955 #[must_use]
959 pub fn for_build_roots<'a>(roots: impl IntoIterator<Item = &'a ComponentRootId>) -> Self {
960 let mut roots = roots
961 .into_iter()
962 .map(ComponentRootId::as_str)
963 .collect::<Vec<_>>();
964 roots.sort_unstable();
965 roots.dedup();
966 let authority = if roots.is_empty() {
967 "application".to_string()
968 } else {
969 roots.join("+")
970 };
971 Self(SemanticId(format!("form-ownership-graph:{authority}")))
972 }
973
974 #[must_use]
975 pub const fn as_semantic_id(&self) -> &SemanticId {
976 &self.0
977 }
978
979 #[must_use]
980 pub fn as_str(&self) -> &str {
981 self.0.as_str()
982 }
983}
984
985impl ValidationRuleCandidateId {
986 #[must_use]
987 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
988 Self(SemanticId::validation_rule_candidate_in_module(
989 module_path,
990 position,
991 ))
992 }
993
994 #[must_use]
995 pub const fn as_semantic_id(&self) -> &SemanticId {
996 &self.0
997 }
998
999 #[must_use]
1000 pub fn as_str(&self) -> &str {
1001 self.0.as_str()
1002 }
1003}
1004
1005impl ValidationRuleId {
1006 #[must_use]
1007 pub fn for_field(field: &FieldId, authored_ordinal: usize) -> Self {
1008 Self(field.as_semantic_id().validation_rule(authored_ordinal))
1009 }
1010
1011 #[must_use]
1012 pub const fn as_semantic_id(&self) -> &SemanticId {
1013 &self.0
1014 }
1015
1016 #[must_use]
1017 pub fn as_str(&self) -> &str {
1018 self.0.as_str()
1019 }
1020}
1021
1022impl ValidationGraphId {
1023 #[must_use]
1024 pub fn for_build_roots<'a>(roots: impl IntoIterator<Item = &'a ComponentRootId>) -> Self {
1025 let mut roots = roots
1026 .into_iter()
1027 .map(ComponentRootId::as_str)
1028 .collect::<Vec<_>>();
1029 roots.sort_unstable();
1030 roots.dedup();
1031 let authority = if roots.is_empty() {
1032 "application".to_string()
1033 } else {
1034 roots.join("+")
1035 };
1036 Self(SemanticId(format!("validation-graph:{authority}")))
1037 }
1038
1039 #[must_use]
1040 pub const fn as_semantic_id(&self) -> &SemanticId {
1041 &self.0
1042 }
1043
1044 #[must_use]
1045 pub fn as_str(&self) -> &str {
1046 self.0.as_str()
1047 }
1048}
1049
1050impl ValidationPlanId {
1051 #[must_use]
1052 pub fn for_form(form: &FormId) -> Self {
1053 Self(form.as_semantic_id().validation_plan())
1054 }
1055
1056 #[must_use]
1057 pub const fn as_semantic_id(&self) -> &SemanticId {
1058 &self.0
1059 }
1060
1061 #[must_use]
1062 pub fn as_str(&self) -> &str {
1063 self.0.as_str()
1064 }
1065}
1066
1067impl FieldDependencyId {
1068 #[must_use]
1069 pub fn for_rule_and_source(rule: &ValidationRuleId, source_field: &FieldId) -> Self {
1070 Self(
1071 rule.as_semantic_id()
1072 .field_dependency(source_field.as_semantic_id()),
1073 )
1074 }
1075
1076 #[must_use]
1077 pub const fn as_semantic_id(&self) -> &SemanticId {
1078 &self.0
1079 }
1080
1081 #[must_use]
1082 pub fn as_str(&self) -> &str {
1083 self.0.as_str()
1084 }
1085}
1086
1087impl DirtyTrackingPlanId {
1088 #[must_use]
1089 pub fn for_form(form: &FormId) -> Self {
1090 Self(form.as_semantic_id().dirty_tracking_plan())
1091 }
1092
1093 #[must_use]
1094 pub const fn as_semantic_id(&self) -> &SemanticId {
1095 &self.0
1096 }
1097
1098 #[must_use]
1099 pub fn as_str(&self) -> &str {
1100 self.0.as_str()
1101 }
1102}
1103
1104impl TouchedTrackingPlanId {
1105 #[must_use]
1106 pub fn for_form(form: &FormId) -> Self {
1107 Self(form.as_semantic_id().touched_tracking_plan())
1108 }
1109
1110 #[must_use]
1111 pub const fn as_semantic_id(&self) -> &SemanticId {
1112 &self.0
1113 }
1114
1115 #[must_use]
1116 pub fn as_str(&self) -> &str {
1117 self.0.as_str()
1118 }
1119}
1120
1121impl FieldTrackingId {
1122 #[must_use]
1123 pub fn for_field(field: &FieldId) -> Self {
1124 Self(field.as_semantic_id().field_tracking())
1125 }
1126
1127 #[must_use]
1128 pub const fn as_semantic_id(&self) -> &SemanticId {
1129 &self.0
1130 }
1131
1132 #[must_use]
1133 pub fn as_str(&self) -> &str {
1134 self.0.as_str()
1135 }
1136}
1137
1138impl SubmissionDeclarationCandidateId {
1139 #[must_use]
1140 pub fn for_source_position(module_path: impl AsRef<Path>, position: usize) -> Self {
1141 Self(SemanticId::submission_declaration_candidate_in_module(
1142 module_path,
1143 position,
1144 ))
1145 }
1146
1147 #[must_use]
1148 pub const fn as_semantic_id(&self) -> &SemanticId {
1149 &self.0
1150 }
1151
1152 #[must_use]
1153 pub fn as_str(&self) -> &str {
1154 self.0.as_str()
1155 }
1156}
1157
1158impl SubmissionPlanId {
1159 #[must_use]
1160 pub fn for_form(form: &FormId) -> Self {
1161 Self(form.as_semantic_id().submission_plan())
1162 }
1163
1164 #[must_use]
1165 pub const fn as_semantic_id(&self) -> &SemanticId {
1166 &self.0
1167 }
1168
1169 #[must_use]
1170 pub fn as_str(&self) -> &str {
1171 self.0.as_str()
1172 }
1173}
1174
1175impl SerializationPlanId {
1176 #[must_use]
1177 pub fn for_form(form: &FormId) -> Self {
1178 Self(form.as_semantic_id().serialization_plan())
1179 }
1180
1181 #[must_use]
1182 pub const fn as_semantic_id(&self) -> &SemanticId {
1183 &self.0
1184 }
1185
1186 #[must_use]
1187 pub fn as_str(&self) -> &str {
1188 self.0.as_str()
1189 }
1190}
1191
1192impl ResetPlanId {
1193 #[must_use]
1194 pub fn for_form(form: &FormId) -> Self {
1195 Self(form.as_semantic_id().reset_plan())
1196 }
1197 #[must_use]
1198 pub const fn as_semantic_id(&self) -> &SemanticId {
1199 &self.0
1200 }
1201 #[must_use]
1202 pub fn as_str(&self) -> &str {
1203 self.0.as_str()
1204 }
1205}
1206
1207impl FieldResetOperationId {
1208 #[must_use]
1209 pub fn for_plan_and_field(plan: &ResetPlanId, field: &FieldId) -> Self {
1210 Self(
1211 plan.as_semantic_id()
1212 .field_reset_operation(field.as_semantic_id()),
1213 )
1214 }
1215 #[must_use]
1216 pub const fn as_semantic_id(&self) -> &SemanticId {
1217 &self.0
1218 }
1219 #[must_use]
1220 pub fn as_str(&self) -> &str {
1221 self.0.as_str()
1222 }
1223}
1224
1225macro_rules! impl_form_slot_id {
1226 ($name:ident) => {
1227 impl $name {
1228 #[must_use]
1229 pub fn for_instance(instance: &FormInstanceId, name: &str) -> Self {
1230 Self(
1231 instance
1232 .as_semantic_id()
1233 .child("form-slot", &format!("{}:{name}", stringify!($name))),
1234 )
1235 }
1236
1237 #[must_use]
1238 pub const fn as_semantic_id(&self) -> &SemanticId {
1239 &self.0
1240 }
1241
1242 #[must_use]
1243 pub fn as_str(&self) -> &str {
1244 self.0.as_str()
1245 }
1246 }
1247 };
1248}
1249impl_form_slot_id!(FormFieldValueSlotId);
1250impl_form_slot_id!(FormFieldDirtySlotId);
1251impl_form_slot_id!(FormFieldTouchedSlotId);
1252impl_form_slot_id!(FormFieldValidationSlotId);
1253impl_form_slot_id!(FormValidationAggregateSlotId);
1254impl_form_slot_id!(FormSubmissionStateSlotId);
1255
1256impl ValidationDependencyCycleId {
1257 #[must_use]
1258 pub fn for_fields(form: &FormId, fields: &[FieldId]) -> Self {
1259 let mut fields = fields.iter().map(FieldId::as_str).collect::<Vec<_>>();
1260 fields.sort_unstable();
1261 fields.dedup();
1262 Self(SemanticId(format!(
1263 "validation-cycle:{}/{}",
1264 form.as_str(),
1265 fields.join("+")
1266 )))
1267 }
1268
1269 #[must_use]
1270 pub const fn as_semantic_id(&self) -> &SemanticId {
1271 &self.0
1272 }
1273
1274 #[must_use]
1275 pub fn as_str(&self) -> &str {
1276 self.0.as_str()
1277 }
1278}
1279
1280impl SlotId {
1281 #[must_use]
1282 pub fn for_component(component: &SemanticId, name: &str) -> Self {
1283 Self(component.slot(name))
1284 }
1285
1286 #[must_use]
1287 pub const fn as_semantic_id(&self) -> &SemanticId {
1288 &self.0
1289 }
1290
1291 #[must_use]
1292 pub fn as_str(&self) -> &str {
1293 self.0.as_str()
1294 }
1295}
1296
1297impl SlotDeclarationCandidateId {
1298 #[must_use]
1299 pub fn for_component_position(component: &SemanticId, position: usize) -> Self {
1300 Self(component.slot_declaration_candidate(position))
1301 }
1302
1303 #[must_use]
1304 pub const fn as_semantic_id(&self) -> &SemanticId {
1305 &self.0
1306 }
1307
1308 #[must_use]
1309 pub fn as_str(&self) -> &str {
1310 self.0.as_str()
1311 }
1312}
1313
1314impl ComponentInvocationId {
1315 #[must_use]
1316 pub fn for_template_entity(template_entity: &SemanticId, target: &str) -> Self {
1317 Self(template_entity.component_invocation(target))
1318 }
1319
1320 #[must_use]
1323 pub fn for_layout_composition(
1324 layout: &SemanticId,
1325 route_component: &SemanticId,
1326 position: usize,
1327 ) -> Self {
1328 Self(layout.layout_composition_invocation(route_component, position))
1329 }
1330
1331 #[must_use]
1332 pub const fn as_semantic_id(&self) -> &SemanticId {
1333 &self.0
1334 }
1335
1336 #[must_use]
1337 pub fn as_str(&self) -> &str {
1338 self.0.as_str()
1339 }
1340}
1341
1342impl TemplatePositionId {
1343 #[must_use]
1344 pub fn for_template_entity(template_entity: &SemanticId) -> Self {
1345 Self(template_entity.template_position())
1346 }
1347
1348 #[must_use]
1349 pub const fn as_semantic_id(&self) -> &SemanticId {
1350 &self.0
1351 }
1352
1353 #[must_use]
1354 pub fn as_str(&self) -> &str {
1355 self.0.as_str()
1356 }
1357}
1358
1359impl SlotContentFragmentId {
1360 #[must_use]
1361 pub fn for_invocation(invocation: &ComponentInvocationId, slot_name: &str) -> Self {
1362 Self(invocation.as_semantic_id().slot_content_fragment(slot_name))
1363 }
1364
1365 #[must_use]
1366 pub const fn as_semantic_id(&self) -> &SemanticId {
1367 &self.0
1368 }
1369
1370 #[must_use]
1371 pub fn as_str(&self) -> &str {
1372 self.0.as_str()
1373 }
1374}
1375
1376impl SlotOutletId {
1377 #[must_use]
1378 pub fn for_template_entity(template_entity: &SemanticId, slot_name: &str) -> Self {
1379 Self(template_entity.slot_outlet(slot_name))
1380 }
1381
1382 #[must_use]
1383 pub const fn as_semantic_id(&self) -> &SemanticId {
1384 &self.0
1385 }
1386
1387 #[must_use]
1388 pub fn as_str(&self) -> &str {
1389 self.0.as_str()
1390 }
1391}
1392
1393impl SlotBindingId {
1394 #[must_use]
1395 pub fn for_instance(instance: &ComponentInstanceId, binding_key: &str) -> Self {
1396 Self(instance.as_semantic_id().slot_binding(binding_key))
1397 }
1398
1399 #[must_use]
1400 pub const fn as_semantic_id(&self) -> &SemanticId {
1401 &self.0
1402 }
1403
1404 #[must_use]
1405 pub fn as_str(&self) -> &str {
1406 self.0.as_str()
1407 }
1408}
1409
1410impl ComponentRootId {
1411 #[must_use]
1412 pub fn for_component(component: &SemanticId) -> Self {
1413 Self(SemanticId::component_root(component))
1414 }
1415
1416 #[must_use]
1417 pub const fn as_semantic_id(&self) -> &SemanticId {
1418 &self.0
1419 }
1420
1421 #[must_use]
1422 pub fn as_str(&self) -> &str {
1423 self.0.as_str()
1424 }
1425}
1426
1427impl ComponentInstanceId {
1428 #[must_use]
1429 pub fn for_root(root: &ComponentRootId) -> Self {
1430 Self(root.as_semantic_id().clone())
1431 }
1432
1433 #[must_use]
1434 pub fn for_invocation(parent: &Self, invocation: &ComponentInvocationId) -> Self {
1435 Self(
1436 parent
1437 .as_semantic_id()
1438 .component_instance_invocation(invocation.as_str()),
1439 )
1440 }
1441
1442 #[must_use]
1443 pub const fn as_semantic_id(&self) -> &SemanticId {
1444 &self.0
1445 }
1446
1447 #[must_use]
1448 pub fn as_str(&self) -> &str {
1449 self.0.as_str()
1450 }
1451}
1452
1453impl ComponentStructuralRegionId {
1454 #[must_use]
1455 pub fn for_template_entity(entity: &SemanticId, kind: &str) -> Self {
1456 Self(entity.component_structural_region(kind))
1457 }
1458
1459 #[must_use]
1460 pub const fn as_semantic_id(&self) -> &SemanticId {
1461 &self.0
1462 }
1463
1464 #[must_use]
1465 pub fn as_str(&self) -> &str {
1466 self.0.as_str()
1467 }
1468}
1469
1470impl ContextDeclarationCandidateId {
1471 #[must_use]
1472 pub fn for_component_position(component: &SemanticId, position: usize) -> Self {
1473 Self(component.context_declaration_candidate(position))
1474 }
1475
1476 #[must_use]
1477 pub const fn as_semantic_id(&self) -> &SemanticId {
1478 &self.0
1479 }
1480
1481 #[must_use]
1482 pub fn as_str(&self) -> &str {
1483 self.0.as_str()
1484 }
1485}
1486
1487fn normalized_module_path(path: &Path) -> String {
1488 let mut segments = Vec::new();
1489 let absolute = path.is_absolute();
1490
1491 for component in path.components() {
1492 match component {
1493 Component::CurDir | Component::RootDir | Component::Prefix(_) => {}
1494 Component::ParentDir => {
1495 if segments.last().is_some_and(|segment| *segment != "..") {
1496 segments.pop();
1497 } else {
1498 segments.push("..".to_string());
1499 }
1500 }
1501 Component::Normal(segment) => segments.push(segment.to_string_lossy().into_owned()),
1502 }
1503 }
1504
1505 let path = segments.join("/");
1506 if absolute {
1507 format!("/{path}")
1508 } else {
1509 path
1510 }
1511}
1512
1513impl fmt::Display for SemanticId {
1514 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1515 self.0.fmt(formatter)
1516 }
1517}
1518
1519impl fmt::Display for EffectId {
1520 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1521 self.0.fmt(formatter)
1522 }
1523}
1524
1525impl fmt::Display for EffectStatementId {
1526 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1527 self.0.fmt(formatter)
1528 }
1529}
1530
1531impl fmt::Display for ContextId {
1532 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1533 self.0.fmt(formatter)
1534 }
1535}
1536
1537impl fmt::Display for ProviderId {
1538 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1539 self.0.fmt(formatter)
1540 }
1541}
1542
1543impl fmt::Display for ConsumerId {
1544 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1545 self.0.fmt(formatter)
1546 }
1547}
1548
1549impl fmt::Display for FormId {
1550 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1551 self.0.fmt(formatter)
1552 }
1553}
1554
1555impl fmt::Display for FormInstanceId {
1556 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1557 self.0.fmt(formatter)
1558 }
1559}
1560
1561impl fmt::Display for FormDeclarationCandidateId {
1562 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1563 self.0.fmt(formatter)
1564 }
1565}
1566
1567impl fmt::Display for FormFieldDeclarationCandidateId {
1568 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1569 self.0.fmt(formatter)
1570 }
1571}
1572
1573impl fmt::Display for FormFieldBindingCandidateId {
1574 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1575 self.0.fmt(formatter)
1576 }
1577}
1578
1579impl fmt::Display for FieldId {
1580 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1581 self.0.fmt(formatter)
1582 }
1583}
1584
1585impl fmt::Display for FieldBindingId {
1586 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1587 self.0.fmt(formatter)
1588 }
1589}
1590
1591impl fmt::Display for SubmissionHostCandidateId {
1592 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1593 self.0.fmt(formatter)
1594 }
1595}
1596impl fmt::Display for SubmissionHostId {
1597 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1598 self.0.fmt(formatter)
1599 }
1600}
1601
1602impl fmt::Display for FormOwnershipGraphId {
1603 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1604 self.0.fmt(formatter)
1605 }
1606}
1607
1608impl fmt::Display for ValidationRuleCandidateId {
1609 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1610 self.0.fmt(formatter)
1611 }
1612}
1613
1614impl fmt::Display for ValidationRuleId {
1615 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1616 self.0.fmt(formatter)
1617 }
1618}
1619
1620impl fmt::Display for ValidationGraphId {
1621 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1622 self.0.fmt(formatter)
1623 }
1624}
1625
1626impl fmt::Display for ValidationPlanId {
1627 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1628 self.0.fmt(formatter)
1629 }
1630}
1631
1632impl fmt::Display for FieldDependencyId {
1633 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1634 self.0.fmt(formatter)
1635 }
1636}
1637
1638impl fmt::Display for ValidationDependencyCycleId {
1639 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1640 self.0.fmt(formatter)
1641 }
1642}
1643
1644impl fmt::Display for SlotId {
1645 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1646 self.0.fmt(formatter)
1647 }
1648}
1649
1650impl fmt::Display for SlotDeclarationCandidateId {
1651 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1652 self.0.fmt(formatter)
1653 }
1654}
1655
1656impl fmt::Display for ComponentInvocationId {
1657 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1658 self.0.fmt(formatter)
1659 }
1660}
1661
1662impl fmt::Display for TemplatePositionId {
1663 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1664 self.0.fmt(formatter)
1665 }
1666}
1667
1668impl fmt::Display for SlotContentFragmentId {
1669 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1670 self.0.fmt(formatter)
1671 }
1672}
1673
1674impl fmt::Display for SlotOutletId {
1675 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1676 self.0.fmt(formatter)
1677 }
1678}
1679
1680impl fmt::Display for SlotBindingId {
1681 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1682 self.0.fmt(formatter)
1683 }
1684}
1685
1686impl fmt::Display for ComponentRootId {
1687 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1688 self.0.fmt(formatter)
1689 }
1690}
1691
1692impl fmt::Display for ComponentInstanceId {
1693 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1694 self.0.fmt(formatter)
1695 }
1696}
1697
1698impl fmt::Display for ComponentStructuralRegionId {
1699 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1700 self.0.fmt(formatter)
1701 }
1702}
1703
1704impl fmt::Display for ContextDeclarationCandidateId {
1705 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1706 self.0.fmt(formatter)
1707 }
1708}
1709
1710#[cfg(test)]
1711mod tests {
1712 use super::{
1713 ComponentInstanceId, ComponentInvocationId, ConsumerId, ContextId, FieldDependencyId,
1714 FieldId, FormId, FormInstanceId, ProviderId, SemanticId, SemanticOwner, SlotId,
1715 TemplatePositionId, ValidationPlanId, ValidationRuleId,
1716 };
1717
1718 #[test]
1719 fn derives_component_scoped_ids() {
1720 let component = SemanticId::component(Some("x-counter"), "Counter");
1721
1722 assert_eq!(component.as_str(), "component:x-counter");
1723 assert_eq!(
1724 component.state_field("count").as_str(),
1725 "component:x-counter/state:count"
1726 );
1727 assert_eq!(
1728 component.method("increment").as_str(),
1729 "component:x-counter/method:increment"
1730 );
1731 assert_eq!(
1732 component.computed("remainingCount").as_str(),
1733 "component:x-counter/computed:remainingCount"
1734 );
1735 assert_eq!(
1736 component.effect("syncTitle").as_str(),
1737 "component:x-counter/effect:syncTitle"
1738 );
1739 assert_eq!(
1740 ContextId::for_component(&component, "theme").as_str(),
1741 "component:x-counter/context:theme"
1742 );
1743 assert_eq!(
1744 ProviderId::for_component(&component, "providedTheme").as_str(),
1745 "component:x-counter/provider:providedTheme"
1746 );
1747 assert_eq!(
1748 ConsumerId::for_component(&component, "theme").as_str(),
1749 "component:x-counter/consumer:theme"
1750 );
1751 assert_eq!(
1752 SlotId::for_component(&component, "children").as_str(),
1753 "component:x-counter/slot:children"
1754 );
1755 assert_eq!(
1756 component.effect_activation_slot("syncTitle").as_str(),
1757 "component:x-counter/effect-activation:syncTitle"
1758 );
1759 assert_eq!(
1760 component.action("increment", 0).as_str(),
1761 "component:x-counter/action:increment:0"
1762 );
1763 assert_eq!(
1764 component
1765 .method("render")
1766 .local_variable("title", 0)
1767 .as_str(),
1768 "component:x-counter/method:render/local:title:0"
1769 );
1770 assert_eq!(
1771 component.event_handler("click", 0).as_str(),
1772 "component:x-counter/event:click:0"
1773 );
1774 assert_eq!(
1775 component.template().as_str(),
1776 "component:x-counter/template:render"
1777 );
1778 let element = component.template().template_entity("element", "root.0");
1779 assert_eq!(
1780 TemplatePositionId::for_template_entity(&element).as_str(),
1781 "component:x-counter/template:render/element:root.0/template-position:authored"
1782 );
1783 assert_eq!(
1784 ComponentInvocationId::for_template_entity(&element, "component:x-card").as_str(),
1785 "component:x-counter/template:render/element:root.0/component-invocation:component:x-card"
1786 );
1787 }
1788
1789 #[test]
1790 fn derives_module_qualified_component_ids() {
1791 let component =
1792 SemanticId::component_in_module("src/../src/Counter.tsx", Some("x-counter"), "Counter");
1793
1794 assert_eq!(
1795 component.as_str(),
1796 "module:src/Counter.tsx/component:x-counter"
1797 );
1798 assert_eq!(
1799 component.state_field("count").as_str(),
1800 "module:src/Counter.tsx/component:x-counter/state:count"
1801 );
1802 }
1803
1804 #[test]
1805 fn derives_distinct_form_definition_instance_and_field_identities() {
1806 let component =
1807 SemanticId::component_in_module("src/Profile.tsx", Some("x-profile"), "Profile");
1808 let component_instance = serde_json::from_str::<ComponentInstanceId>(
1809 r#""root:module:src/Profile.tsx/component:x-profile""#,
1810 )
1811 .expect("canonical component instance id");
1812 let form = FormId::for_owner(&component, "profile");
1813 let instance = FormInstanceId::for_component_instance(&component_instance, &form);
1814 let nested_component_instance = serde_json::from_str::<ComponentInstanceId>(
1815 r#""root:module:src/Profile.tsx/component:x-profile/invocation:nested""#,
1816 )
1817 .expect("canonical nested component instance id");
1818 let nested_instance =
1819 FormInstanceId::for_component_instance(&nested_component_instance, &form);
1820 let field = FieldId::for_form(&form, "email");
1821 let rule = ValidationRuleId::for_field(&field, 2);
1822 let plan = ValidationPlanId::for_form(&form);
1823 let dependency = FieldDependencyId::for_rule_and_source(&rule, &field);
1824
1825 assert_eq!(
1826 form.as_str(),
1827 "module:src/Profile.tsx/component:x-profile/form:profile"
1828 );
1829 assert_eq!(
1830 field.as_str(),
1831 "module:src/Profile.tsx/component:x-profile/form:profile/field:email"
1832 );
1833 assert_eq!(
1834 instance.as_str(),
1835 "root:module:src/Profile.tsx/component:x-profile/form-instance:module:src/Profile.tsx/component:x-profile/form:profile"
1836 );
1837 assert_ne!(form.as_semantic_id(), field.as_semantic_id());
1838 assert_ne!(form.as_semantic_id(), instance.as_semantic_id());
1839 assert_ne!(field.as_semantic_id(), instance.as_semantic_id());
1840 assert_ne!(instance, nested_instance);
1841 assert_eq!(
1842 plan.as_str(),
1843 "module:src/Profile.tsx/component:x-profile/form:profile/validation-plan"
1844 );
1845 assert_eq!(
1846 dependency.as_str(),
1847 "module:src/Profile.tsx/component:x-profile/form:profile/field:email/validation-rule:2/field-dependency:module:src/Profile.tsx/component:x-profile/form:profile/field:email"
1848 );
1849 }
1850
1851 #[test]
1852 fn falls_back_to_class_name_for_invalid_components() {
1853 assert_eq!(
1854 SemanticId::component(None, "MissingDecorator").as_str(),
1855 "component:MissingDecorator"
1856 );
1857 }
1858
1859 #[test]
1860 fn distinguishes_application_roots_from_entity_owners() {
1861 let component = SemanticId::component(Some("x-counter"), "Counter");
1862
1863 assert_eq!(SemanticOwner::Application.entity_id(), None);
1864 assert_eq!(
1865 SemanticOwner::entity(component.clone()).entity_id(),
1866 Some(&component)
1867 );
1868 }
1869}