acton_ern/traits/ern_component.rs
1use std::str::FromStr;
2
3use crate::errors::ErnError;
4use crate::{Account, Category, Domain, EntityRoot, Part, Parts};
5
6/// Represents a component of an Entity Resource Name (ERN).
7///
8/// This trait is used to ensure type safety and proper ordering when building ERNs.
9/// Each component in an ERN implements this trait, defining its prefix and the
10/// type of the next component that should follow it in the ERN structure.
11///
12/// The trait is primarily used by the `ErnBuilder` to enforce the correct order
13/// of components during ERN construction.
14pub trait ErnComponent {
15 /// Returns the prefix string that should appear before this component in an ERN.
16 ///
17 /// For example, the `Domain` component has the prefix "ern:" to indicate the
18 /// start of an ERN string.
19 fn prefix() -> &'static str;
20
21 /// The type of the next component that should follow this one in the ERN structure.
22 ///
23 /// This associated type is used by the builder pattern to enforce the correct
24 /// sequence of components. For example, `Domain::NextState` is `Category`,
25 /// indicating that a `Category` should follow a `Domain` in an ERN.
26 type NextState;
27
28 /// Builds the ERN root component this type contributes, if it can occupy the root slot.
29 ///
30 /// `ErnBuilder` dispatches on component *position*, which is not enough to tell the
31 /// root-capable components apart: `EntityRoot` and `SHA1Name` share the same prefix
32 /// and the same `NextState`. This method carries the identifier algorithm down to the
33 /// builder, so `with::<SHA1Name>(..)` really does produce a deterministic v5 root
34 /// instead of silently falling back to a time-ordered v7 one.
35 ///
36 /// Components that never occupy the root slot use the default implementation and
37 /// return `None`.
38 fn build_root(_value: &str) -> Option<Result<EntityRoot, ErnError>> {
39 None
40 }
41}
42
43macro_rules! impl_ern_component {
44 ($type:ty, $prefix:expr, $next:ty) => {
45 impl ErnComponent for $type {
46 fn prefix() -> &'static str {
47 $prefix
48 }
49 type NextState = $next;
50 }
51 };
52}
53impl ErnComponent for EntityRoot {
54 fn prefix() -> &'static str {
55 ""
56 }
57 type NextState = Part;
58
59 fn build_root(value: &str) -> Option<Result<EntityRoot, ErnError>> {
60 Some(EntityRoot::from_str(value))
61 }
62}
63
64impl ErnComponent for Account {
65 fn prefix() -> &'static str {
66 ""
67 }
68 type NextState = EntityRoot;
69}
70
71impl_ern_component!(Domain, "ern:", Category);
72impl_ern_component!(Category, "", Account);
73impl_ern_component!(Part, "", Parts);
74
75/// Implementation for the `Parts` component of an ERN.
76///
77/// The `Parts` component represents a collection of path parts in the ERN.
78/// Its `NextState` is itself, allowing for multiple parts to be added.
79impl ErnComponent for Parts {
80 fn prefix() -> &'static str {
81 ":"
82 }
83 type NextState = Parts;
84}