wasm4pm_compat/ids.rs
1//! Zero-cost, kind-typed identifier wrappers.
2//!
3//! Process evidence is full of integer identifiers — event ids, object ids,
4//! activity ids, relation ids, trace ids. Passed around as bare `u64`/`u32`
5//! they are trivially interchangeable, which is exactly how a dangling link or
6//! a flattening bug slips in: nothing stops you handing an *object* id where an
7//! *event* id is required.
8//!
9//! Each id here is a `#[repr(transparent)]` newtype carrying a `PhantomData<K>`
10//! **kind marker**, so [`crate::ids::EventId`] and [`crate::ids::ObjectId`] are *distinct types*
11//! even though both wrap a `u64`. Mixing them is a **compile error**, not a
12//! debugging session. The `K` parameter additionally lets a caller stamp ids
13//! with a *namespace* (e.g. a witness or a log identity) so ids from different
14//! origins cannot be confused either.
15//!
16//! These wrappers are **structure only**: they identify, they do not resolve.
17//! Resolving an id to the value it names (and validating that the link exists)
18//! is an engine concern — graduate to `wasm4pm` for that.
19//!
20//! ## String-backed name types
21//!
22//! [`crate::ids::ObjectTypeName`] and [`crate::ids::EventTypeName`] carry the actual string label
23//! (e.g. `"order"`, `"place_order"`) rather than an interned integer handle.
24//! They are structurally distinct from the integer-backed types above — mixing
25//! them is a compile error. Use the integer-backed [`crate::ids::ObjectTypeId`] /
26//! [`crate::ids::EventTypeId`] when you hold an interned handle; use the string-backed
27//! types when you hold the human-readable label itself.
28//!
29//! ## `id_of` — phantom-typed marker constructor
30//!
31//! [`crate::ids::id_of`] is a zero-cost free function that constructs any [`crate::ids::TypedId`]
32//! implementor from a raw value while anchoring it to an explicit kind marker.
33//! It is the *canonical* way to build a typed id when the kind is known at the
34//! call site: `id_of::<EventId<MyLog>>(7)` rather than `EventId::<MyLog>::new(7)`.
35
36use core::marker::PhantomData;
37use std::borrow::Cow;
38
39/// Sealed marker trait shared by every kind-typed identifier in this module.
40///
41/// # What this is
42///
43/// `TypedId` is a *sealed* trait: it is implemented only by the newtypes
44/// declared in this module ([`crate::ids::EventId`], [`crate::ids::ObjectId`], [`crate::ids::CaseId`], etc.) and
45/// cannot be implemented outside the crate. It lets generic code express
46/// "any typed id" without reaching for a raw `u64`/`u32`.
47///
48/// # What this is NOT
49///
50/// This is not a resolving interface. A `TypedId` still only **names** an
51/// entity — it does not look it up, validate liveness, or dereference a link.
52/// Graduate to `wasm4pm` for resolution.
53///
54/// # When to graduate
55///
56/// When you need to dereference an id to the value it names, or validate that
57/// a link exists, move that logic to the `wasm4pm` execution engine.
58///
59/// # Example
60///
61/// ```
62/// use wasm4pm_compat::ids::{EventId, TypedId};
63/// enum MyLog {}
64/// fn id_is_positive<I: TypedId>(id: &I) -> bool { !id.is_zero() }
65/// let ev = EventId::<MyLog>::new(7);
66/// assert!(id_is_positive(&ev));
67/// ```
68pub trait TypedId: sealed::SealedId + Copy + Eq + core::hash::Hash + core::fmt::Debug {
69 /// The underlying raw primitive type (`u64` or `u32`).
70 type Raw: Copy + Eq + core::hash::Hash + core::fmt::Debug;
71
72 /// Returns the underlying raw value.
73 fn raw_value(&self) -> Self::Raw;
74
75 /// Returns `true` when the raw value is the zero sentinel.
76 ///
77 /// Zero is conventionally "no id" in many process-mining tools; this method
78 /// lets generic code check for the sentinel without knowing the raw type.
79 fn is_zero(&self) -> bool;
80}
81
82mod sealed {
83 pub trait SealedId {}
84}
85
86/// Implements [`TypedId`] and [`sealed::SealedId`] for a given newtype.
87macro_rules! impl_typed_id {
88 ($name:ident, $raw:ty, $zero:expr) => {
89 impl<K> sealed::SealedId for $name<K> {}
90 impl<K> TypedId for $name<K> {
91 type Raw = $raw;
92 #[inline]
93 fn raw_value(&self) -> $raw {
94 self.raw
95 }
96 #[inline]
97 fn is_zero(&self) -> bool {
98 self.raw == $zero
99 }
100 }
101 };
102}
103
104/// Declares a `#[repr(transparent)]` kind-typed id newtype over `$raw`.
105macro_rules! typed_id {
106 ($(#[$meta:meta])* $name:ident, $raw:ty) => {
107 $(#[$meta])*
108 ///
109 /// Zero-cost `#[repr(transparent)]` wrapper carrying a `PhantomData<K>`
110 /// kind marker. Structure-only: it names an entity, it does not resolve
111 /// or validate the link. Graduate to `wasm4pm` to dereference it.
112 #[repr(transparent)]
113 pub struct $name<K> {
114 raw: $raw,
115 _kind: PhantomData<K>,
116 }
117
118 impl<K> $name<K> {
119 #[doc = concat!("Wraps a raw `", stringify!($raw), "` as a typed [`", stringify!($name), "`].")]
120 ///
121 /// # Examples
122 ///
123 /// ```
124 #[doc = concat!("use wasm4pm_compat::ids::", stringify!($name), ";")]
125 /// enum Local {}
126 #[doc = concat!("let id = ", stringify!($name), "::<Local>::new(7);")]
127 /// assert_eq!(id.raw(), 7);
128 /// ```
129 #[inline]
130 pub const fn new(raw: $raw) -> Self {
131 Self { raw, _kind: PhantomData }
132 }
133
134 #[doc = concat!("Returns the underlying raw `", stringify!($raw), "`.")]
135 ///
136 /// # Examples
137 ///
138 /// ```
139 #[doc = concat!("use wasm4pm_compat::ids::", stringify!($name), ";")]
140 /// enum Local {}
141 #[doc = concat!("assert_eq!(", stringify!($name), "::<Local>::new(42).raw(), 42);")]
142 /// ```
143 #[inline]
144 pub const fn raw(self) -> $raw {
145 self.raw
146 }
147
148 #[doc = concat!("Consumes `self` and returns the underlying raw `", stringify!($raw), "` value.")]
149 ///
150 /// Identical to [`raw`](Self::raw); provided for newtype-wrapper
151 /// ergonomics so callers can use the same `into_inner()` idiom
152 /// across all ID kinds.
153 ///
154 /// # Examples
155 ///
156 /// ```
157 #[doc = concat!("use wasm4pm_compat::ids::", stringify!($name), ";")]
158 /// enum Local {}
159 #[doc = concat!("assert_eq!(", stringify!($name), "::<Local>::new(5).into_inner(), 5);")]
160 /// ```
161 #[inline]
162 pub const fn into_inner(self) -> $raw {
163 self.raw
164 }
165
166 #[doc = concat!("Borrows the underlying raw `", stringify!($raw), "` value.")]
167 ///
168 /// Identical to [`AsRef`] but using the newtype-wrapper ergonomic
169 /// name `as_inner()` so callers can use a consistent idiom across
170 /// all ID kinds.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 #[doc = concat!("use wasm4pm_compat::ids::", stringify!($name), ";")]
176 /// enum Local {}
177 #[doc = concat!("assert_eq!(*", stringify!($name), "::<Local>::new(9).as_inner(), 9);")]
178 /// ```
179 #[inline]
180 pub const fn as_inner(&self) -> &$raw {
181 &self.raw
182 }
183 }
184
185 // Manual derives so `K` need not itself be `Clone`/`Copy`/etc.
186 impl<K> Clone for $name<K> {
187 #[inline]
188 fn clone(&self) -> Self { *self }
189 }
190 impl<K> Copy for $name<K> {}
191 impl<K> PartialEq for $name<K> {
192 #[inline]
193 fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
194 }
195 impl<K> Eq for $name<K> {}
196 impl<K> core::hash::Hash for $name<K> {
197 #[inline]
198 fn hash<H: core::hash::Hasher>(&self, state: &mut H) { self.raw.hash(state); }
199 }
200 impl<K> core::fmt::Debug for $name<K> {
201 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
202 f.debug_tuple(stringify!($name)).field(&self.raw).finish()
203 }
204 }
205 /// Displays the raw numeric value prefixed by the type name, e.g. `EventId(7)`.
206 impl<K> core::fmt::Display for $name<K> {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 write!(f, "{}({})", stringify!($name), self.raw)
209 }
210 }
211 impl<K> PartialOrd for $name<K> {
212 #[inline]
213 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
214 Some(self.cmp(other))
215 }
216 }
217 impl<K> Ord for $name<K> {
218 #[inline]
219 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
220 self.raw.cmp(&other.raw)
221 }
222 }
223 };
224}
225
226typed_id!(
227 /// Identifies a single event within a log (the atom of process evidence).
228 #[doc(alias = "event identifier")]
229 #[doc(alias = "event id")]
230 EventId, u64
231);
232typed_id!(
233 /// Identifies a single object in an object-centric log (OCEL).
234 ObjectId, u64
235);
236typed_id!(
237 /// Identifies an activity (the name an event realizes), interned to `u32`.
238 ActivityId, u32
239);
240typed_id!(
241 /// Identifies an event-to-object relation (a qualified link in OCEL).
242 RelationId, u32
243);
244typed_id!(
245 /// Identifies a trace (case) — a sequence of events for one process instance.
246 TraceId, u64
247);
248typed_id!(
249 /// Identifies an object-type class in an object-centric log (OCEL).
250 ///
251 /// In OCEL every object belongs to exactly one object type (e.g. `"order"`,
252 /// `"item"`, `"payment"`). [`ObjectTypeId`] is an interned `u32` handle for
253 /// that type name. It is structurally distinct from [`ObjectId`] (which
254 /// identifies a *specific* object instance) and from [`EventTypeId`] (which
255 /// identifies an activity type). Confusing them is a compile error.
256 ObjectTypeId, u32
257);
258typed_id!(
259 /// Identifies an event-type (activity label) in a typed event log.
260 ///
261 /// [`EventTypeId`] is an interned `u32` handle for an activity name at the
262 /// *type* level (e.g. `"place_order"` as a class). It is structurally
263 /// distinct from [`ActivityId`] (which may carry log-local interning) and
264 /// from [`EventId`] (which identifies a *specific* event occurrence).
265 /// Confusing them is a compile error.
266 EventTypeId, u32
267);
268typed_id!(
269 /// Identifies a case in a case-centric (XES-style) log.
270 ///
271 /// [`CaseId`] and [`TraceId`] are intentionally distinct: [`CaseId`] names
272 /// the case *attribute* as parsed from an external format (e.g. XES
273 /// `concept:name`), while [`TraceId`] names a structural trace position
274 /// within an already-admitted [`crate::event_log::EventLog`]. Mixing them is
275 /// a compile error, not a naming convention.
276 CaseId, u64
277);
278
279// ── TypedId sealed-trait implementations ─────────────────────────────────────
280
281impl_typed_id!(EventId, u64, 0u64);
282impl_typed_id!(ObjectId, u64, 0u64);
283impl_typed_id!(ActivityId, u32, 0u32);
284impl_typed_id!(RelationId, u32, 0u32);
285impl_typed_id!(TraceId, u64, 0u64);
286impl_typed_id!(ObjectTypeId, u32, 0u32);
287impl_typed_id!(EventTypeId, u32, 0u32);
288impl_typed_id!(CaseId, u64, 0u64);
289
290// ── String-backed name types ──────────────────────────────────────────────────
291
292/// Carries the human-readable label of an object-type class in an OCEL log.
293///
294/// # What this is
295///
296/// `ObjectTypeName<K>` holds the actual string label (e.g. `"order"`, `"item"`,
297/// `"payment"`) of an OCEL object-type class. It is the string-backed counterpart
298/// to [`ObjectTypeId`] (which holds an interned `u32` handle). Use this type
299/// when the label itself — not an integer handle — is the identity token.
300///
301/// The inner value is `Cow<'static, str>` so that both compile-time string
302/// literals (`&'static str`) and heap-allocated `String`s (as `Owned`) can be
303/// held without copying when the source is already static.
304///
305/// # What this is NOT
306///
307/// This is not a resolution mechanism. Knowing the label does not prove the
308/// type exists in a log or that any object belongs to it. Graduate to
309/// `wasm4pm` for type-membership validation.
310///
311/// # When to graduate
312///
313/// When you need to resolve an `ObjectTypeName` to its object members, or
314/// validate type membership, graduate to the `wasm4pm` execution engine.
315///
316/// # Examples
317///
318/// ```ignore
319/// use wasm4pm_compat::ids::ObjectTypeName;
320/// enum MyLog {}
321/// let order_type = ObjectTypeName::<MyLog>::from_static("order");
322/// assert_eq!(order_type.as_str(), "order");
323/// ```
324pub struct ObjectTypeName<K> {
325 label: Cow<'static, str>,
326 _kind: PhantomData<K>,
327}
328
329impl<K> ObjectTypeName<K> {
330 /// Wraps a `&'static str` label without allocation.
331 ///
332 /// # Examples
333 ///
334 /// ```ignore
335 /// use wasm4pm_compat::ids::ObjectTypeName;
336 /// enum MyLog {}
337 /// let t = ObjectTypeName::<MyLog>::from_static("order");
338 /// assert_eq!(t.as_str(), "order");
339 /// ```
340 #[inline]
341 pub fn from_static(label: &'static str) -> Self {
342 Self {
343 label: Cow::Borrowed(label),
344 _kind: PhantomData,
345 }
346 }
347
348 /// Wraps an owned `String` label, allocating on the heap.
349 ///
350 /// # Examples
351 ///
352 /// ```ignore
353 /// use wasm4pm_compat::ids::ObjectTypeName;
354 /// enum MyLog {}
355 /// let t = ObjectTypeName::<MyLog>::from_owned(String::from("item"));
356 /// assert_eq!(t.as_str(), "item");
357 /// ```
358 #[inline]
359 pub fn from_owned(label: String) -> Self {
360 Self {
361 label: Cow::Owned(label),
362 _kind: PhantomData,
363 }
364 }
365
366 /// Returns the string label.
367 ///
368 /// # Examples
369 ///
370 /// ```ignore
371 /// use wasm4pm_compat::ids::ObjectTypeName;
372 /// enum MyLog {}
373 /// assert_eq!(ObjectTypeName::<MyLog>::from_static("payment").as_str(), "payment");
374 /// ```
375 #[inline]
376 pub fn as_str(&self) -> &str {
377 &self.label
378 }
379}
380
381impl<K> Clone for ObjectTypeName<K> {
382 #[inline]
383 fn clone(&self) -> Self {
384 Self {
385 label: self.label.clone(),
386 _kind: PhantomData,
387 }
388 }
389}
390impl<K> PartialEq for ObjectTypeName<K> {
391 #[inline]
392 fn eq(&self, other: &Self) -> bool {
393 self.label == other.label
394 }
395}
396impl<K> Eq for ObjectTypeName<K> {}
397impl<K> core::hash::Hash for ObjectTypeName<K> {
398 #[inline]
399 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
400 self.label.hash(state);
401 }
402}
403impl<K> core::fmt::Debug for ObjectTypeName<K> {
404 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
405 f.debug_tuple("ObjectTypeName").field(&self.label).finish()
406 }
407}
408/// Displays the string label prefixed by the type name, e.g. `ObjectTypeName("order")`.
409impl<K> core::fmt::Display for ObjectTypeName<K> {
410 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
411 write!(f, "ObjectTypeName(\"{}\")", self.label)
412 }
413}
414impl<K> PartialOrd for ObjectTypeName<K> {
415 #[inline]
416 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
417 Some(self.cmp(other))
418 }
419}
420impl<K> Ord for ObjectTypeName<K> {
421 #[inline]
422 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
423 self.label.cmp(&other.label)
424 }
425}
426
427/// Carries the human-readable label of an event-type (activity) in a typed log.
428///
429/// # What this is
430///
431/// `EventTypeName<K>` holds the actual string label (e.g. `"place_order"`,
432/// `"ship_item"`) of an activity class. It is the string-backed counterpart to
433/// [`EventTypeId`] (which holds an interned `u32` handle). Use this type when
434/// the label itself — not an integer handle — is the identity token.
435///
436/// The inner value is `Cow<'static, str>` so that both compile-time string
437/// literals and heap-allocated `String`s can be held without copying when the
438/// source is already static.
439///
440/// # What this is NOT
441///
442/// This is not a resolution mechanism. Knowing the label does not prove the
443/// activity type exists in a specific log. Graduate to `wasm4pm` for that.
444///
445/// # When to graduate
446///
447/// When you need to resolve an `EventTypeName` to event occurrences, or
448/// validate activity membership, graduate to the `wasm4pm` execution engine.
449///
450/// # Examples
451///
452/// ```ignore
453/// use wasm4pm_compat::ids::EventTypeName;
454/// enum MyLog {}
455/// let place = EventTypeName::<MyLog>::from_static("place_order");
456/// assert_eq!(place.as_str(), "place_order");
457/// ```
458pub struct EventTypeName<K> {
459 label: Cow<'static, str>,
460 _kind: PhantomData<K>,
461}
462
463impl<K> EventTypeName<K> {
464 /// Wraps a `&'static str` label without allocation.
465 ///
466 /// # Examples
467 ///
468 /// ```ignore
469 /// use wasm4pm_compat::ids::EventTypeName;
470 /// enum MyLog {}
471 /// let t = EventTypeName::<MyLog>::from_static("place_order");
472 /// assert_eq!(t.as_str(), "place_order");
473 /// ```
474 #[inline]
475 pub fn from_static(label: &'static str) -> Self {
476 Self {
477 label: Cow::Borrowed(label),
478 _kind: PhantomData,
479 }
480 }
481
482 /// Wraps an owned `String` label, allocating on the heap.
483 ///
484 /// # Examples
485 ///
486 /// ```ignore
487 /// use wasm4pm_compat::ids::EventTypeName;
488 /// enum MyLog {}
489 /// let t = EventTypeName::<MyLog>::from_owned(String::from("ship_item"));
490 /// assert_eq!(t.as_str(), "ship_item");
491 /// ```
492 #[inline]
493 pub fn from_owned(label: String) -> Self {
494 Self {
495 label: Cow::Owned(label),
496 _kind: PhantomData,
497 }
498 }
499
500 /// Returns the string label.
501 ///
502 /// # Examples
503 ///
504 /// ```ignore
505 /// use wasm4pm_compat::ids::EventTypeName;
506 /// enum MyLog {}
507 /// assert_eq!(EventTypeName::<MyLog>::from_static("ship_item").as_str(), "ship_item");
508 /// ```
509 #[inline]
510 pub fn as_str(&self) -> &str {
511 &self.label
512 }
513}
514
515impl<K> Clone for EventTypeName<K> {
516 #[inline]
517 fn clone(&self) -> Self {
518 Self {
519 label: self.label.clone(),
520 _kind: PhantomData,
521 }
522 }
523}
524impl<K> PartialEq for EventTypeName<K> {
525 #[inline]
526 fn eq(&self, other: &Self) -> bool {
527 self.label == other.label
528 }
529}
530impl<K> Eq for EventTypeName<K> {}
531impl<K> core::hash::Hash for EventTypeName<K> {
532 #[inline]
533 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
534 self.label.hash(state);
535 }
536}
537impl<K> core::fmt::Debug for EventTypeName<K> {
538 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
539 f.debug_tuple("EventTypeName").field(&self.label).finish()
540 }
541}
542/// Displays the string label prefixed by the type name, e.g. `EventTypeName("place_order")`.
543impl<K> core::fmt::Display for EventTypeName<K> {
544 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
545 write!(f, "EventTypeName(\"{}\")", self.label)
546 }
547}
548impl<K> PartialOrd for EventTypeName<K> {
549 #[inline]
550 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
551 Some(self.cmp(other))
552 }
553}
554impl<K> Ord for EventTypeName<K> {
555 #[inline]
556 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
557 self.label.cmp(&other.label)
558 }
559}
560
561// ── id_of — phantom-typed marker constructor ──────────────────────────────────
562
563/// Constructs a [`TypedId`] value from its raw primitive, anchored to the
564/// id type's kind marker via turbofish.
565///
566/// # What this is
567///
568/// `id_of` is a zero-cost free function that is the *canonical* call-site form
569/// for building a typed id when the kind is known at the call site:
570///
571/// ```ignore
572/// use wasm4pm_compat::ids::{EventId, id_of};
573/// enum MyLog {}
574/// let ev = id_of::<EventId<MyLog>>(42u64);
575/// assert_eq!(ev.raw(), 42u64);
576/// ```
577///
578/// It is identical in behaviour to `T::new(raw)` but makes the intent explicit
579/// in code: *"I am constructing an id of this exact kind"*.
580///
581/// # What this is NOT
582///
583/// This is not a resolver. The constructed id names an entity; it does not
584/// validate that the entity exists in any log. Graduate to `wasm4pm` for that.
585///
586/// # When to graduate
587///
588/// When you need to resolve the id to the value it names, or validate link
589/// existence, move that logic to the `wasm4pm` execution engine.
590///
591/// # Examples
592///
593/// ```ignore
594/// use wasm4pm_compat::ids::{EventId, ObjectId, TraceId, id_of};
595/// enum MyLog {}
596/// let ev = id_of::<EventId<MyLog>>(1u64);
597/// let obj = id_of::<ObjectId<MyLog>>(2u64);
598/// let tr = id_of::<TraceId<MyLog>>(3u64);
599/// assert_eq!(ev.raw(), 1u64);
600/// assert_eq!(obj.raw(), 2u64);
601/// assert_eq!(tr.raw(), 3u64);
602/// ```
603pub fn id_of<T: NewFromRaw>(raw: T::Raw) -> T {
604 T::new_from_raw(raw)
605}
606
607/// Supporting trait for [`id_of`]: lets the free function call the correct
608/// `new` constructor regardless of which typed-id newtype `T` is.
609///
610/// # What this is
611///
612/// `NewFromRaw` is a *sealed* companion trait implemented only by the numeric
613/// typed-id newtypes in this module. External code cannot implement it; it
614/// exists solely to make [`id_of`] generic over all typed-id kinds.
615///
616/// # What this is NOT
617///
618/// This trait is not part of the public extension surface. Do not implement it
619/// outside this module. Do not use it directly — use [`id_of`] instead.
620///
621/// # When to graduate
622///
623/// Never — this is a crate-internal plumbing trait with no engine concern.
624pub trait NewFromRaw: sealed::SealedId {
625 /// The underlying raw primitive (same as [`TypedId::Raw`]).
626 type Raw;
627 /// Constructs `Self` from a raw primitive. Identical to the concrete `new` fn.
628 fn new_from_raw(raw: Self::Raw) -> Self;
629}
630
631macro_rules! impl_new_from_raw {
632 ($name:ident, $raw:ty) => {
633 impl<K> NewFromRaw for $name<K> {
634 type Raw = $raw;
635 #[inline]
636 fn new_from_raw(raw: $raw) -> Self {
637 Self::new(raw)
638 }
639 }
640 };
641}
642
643impl_new_from_raw!(EventId, u64);
644impl_new_from_raw!(ObjectId, u64);
645impl_new_from_raw!(ActivityId, u32);
646impl_new_from_raw!(RelationId, u32);
647impl_new_from_raw!(TraceId, u64);
648impl_new_from_raw!(ObjectTypeId, u32);
649impl_new_from_raw!(EventTypeId, u32);
650impl_new_from_raw!(CaseId, u64);
651
652// ── From / Into / AsRef / FromStr for integer-backed id types ────────────────
653
654/// Expands `From<$raw>`, `Into<$raw>`, `AsRef<$raw>`, and `FromStr` for a
655/// `#[repr(transparent)]` id newtype.
656///
657/// `From` and its reciprocal `Into` are infallible by design — wrapping a raw
658/// integer never fails. `AsRef<$raw>` borrows the interior without copying.
659/// `FromStr` parses the decimal representation of the raw primitive; it is
660/// infallible in the sense that the only failure mode is a malformed integer
661/// string, which is surfaced via `$raw::from_str`.
662macro_rules! impl_id_conversions {
663 ($name:ident, $raw:ty) => {
664 impl<K> From<$raw> for $name<K> {
665 /// Wraps a raw primitive as a typed id. Infallible.
666 #[inline]
667 fn from(raw: $raw) -> Self {
668 Self::new(raw)
669 }
670 }
671
672 impl<K> From<$name<K>> for $raw {
673 /// Unwraps the typed id back to its raw primitive. Infallible.
674 #[inline]
675 fn from(id: $name<K>) -> $raw {
676 id.raw
677 }
678 }
679
680 impl<K> AsRef<$raw> for $name<K> {
681 /// Borrows the underlying raw value without copying.
682 #[inline]
683 fn as_ref(&self) -> &$raw {
684 &self.raw
685 }
686 }
687
688 impl<K> core::str::FromStr for $name<K> {
689 type Err = <$raw as core::str::FromStr>::Err;
690
691 /// Parses the decimal representation of the raw primitive.
692 ///
693 /// # Errors
694 ///
695 /// Returns the same error as `$raw::from_str` when the string is not
696 /// a valid decimal integer.
697 #[inline]
698 fn from_str(s: &str) -> Result<Self, Self::Err> {
699 s.parse::<$raw>().map(Self::new)
700 }
701 }
702 };
703}
704
705impl_id_conversions!(EventId, u64);
706impl_id_conversions!(ObjectId, u64);
707impl_id_conversions!(ActivityId, u32);
708impl_id_conversions!(RelationId, u32);
709impl_id_conversions!(TraceId, u64);
710impl_id_conversions!(ObjectTypeId, u32);
711impl_id_conversions!(EventTypeId, u32);
712impl_id_conversions!(CaseId, u64);
713
714// ── From / AsRef / FromStr for string-backed name types ──────────────────────
715
716impl<K> From<&'static str> for ObjectTypeName<K> {
717 /// Wraps a `&'static str` label without allocation. Infallible.
718 #[inline]
719 fn from(s: &'static str) -> Self {
720 Self::from_static(s)
721 }
722}
723
724impl<K> From<String> for ObjectTypeName<K> {
725 /// Wraps an owned `String` label, allocating on the heap. Infallible.
726 #[inline]
727 fn from(s: String) -> Self {
728 Self::from_owned(s)
729 }
730}
731
732impl<K> AsRef<str> for ObjectTypeName<K> {
733 /// Borrows the string label without allocating.
734 #[inline]
735 fn as_ref(&self) -> &str {
736 self.as_str()
737 }
738}
739
740impl<K> core::str::FromStr for ObjectTypeName<K> {
741 type Err = core::convert::Infallible;
742
743 /// Parses any string as an `ObjectTypeName`. Always succeeds.
744 #[inline]
745 fn from_str(s: &str) -> Result<Self, Self::Err> {
746 Ok(Self::from_owned(s.to_owned()))
747 }
748}
749
750impl<K> From<&'static str> for EventTypeName<K> {
751 /// Wraps a `&'static str` label without allocation. Infallible.
752 #[inline]
753 fn from(s: &'static str) -> Self {
754 Self::from_static(s)
755 }
756}
757
758impl<K> From<String> for EventTypeName<K> {
759 /// Wraps an owned `String` label, allocating on the heap. Infallible.
760 #[inline]
761 fn from(s: String) -> Self {
762 Self::from_owned(s)
763 }
764}
765
766impl<K> AsRef<str> for EventTypeName<K> {
767 /// Borrows the string label without allocating.
768 #[inline]
769 fn as_ref(&self) -> &str {
770 self.as_str()
771 }
772}
773
774impl<K> core::str::FromStr for EventTypeName<K> {
775 type Err = core::convert::Infallible;
776
777 /// Parses any string as an `EventTypeName`. Always succeeds.
778 #[inline]
779 fn from_str(s: &str) -> Result<Self, Self::Err> {
780 Ok(Self::from_owned(s.to_owned()))
781 }
782}