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