appletheia_application/outbox/
ordering_key.rs1use std::{fmt, fmt::Display, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6use crate::event::{AggregateIdValue, AggregateTypeOwned};
7use crate::request_context::CorrelationId;
8
9#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)]
10pub struct OrderingKey(String);
11
12impl OrderingKey {
13 pub fn new(value: String) -> Result<Self, OrderingKeyError> {
14 if value.trim().is_empty() {
15 return Err(OrderingKeyError::Empty);
16 }
17 Ok(Self(value))
18 }
19
20 pub fn as_str(&self) -> &str {
21 &self.0
22 }
23}
24
25impl Display for OrderingKey {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.write_str(&self.0)
28 }
29}
30
31impl FromStr for OrderingKey {
32 type Err = OrderingKeyError;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 Self::new(s.to_string())
36 }
37}
38
39impl<'de> Deserialize<'de> for OrderingKey {
40 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41 where
42 D: serde::Deserializer<'de>,
43 {
44 let value = String::deserialize(deserializer)?;
45 OrderingKey::new(value).map_err(serde::de::Error::custom)
46 }
47}
48
49impl From<(&AggregateTypeOwned, &AggregateIdValue)> for OrderingKey {
50 fn from((aggregate_type, aggregate_id): (&AggregateTypeOwned, &AggregateIdValue)) -> Self {
51 Self(format!(
52 "{}:{}",
53 aggregate_type.value(),
54 aggregate_id.value()
55 ))
56 }
57}
58
59impl From<CorrelationId> for OrderingKey {
60 fn from(value: CorrelationId) -> Self {
61 Self(value.to_string())
62 }
63}
64
65#[derive(Debug, Error)]
66pub enum OrderingKeyError {
67 #[error("ordering key cannot be empty")]
68 Empty,
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn accepts_non_empty() {
77 let key = OrderingKey::new("abc".to_string()).unwrap();
78 assert_eq!(key.as_str(), "abc");
79 }
80
81 #[test]
82 fn rejects_empty() {
83 let err = OrderingKey::new("".to_string()).unwrap_err();
84 assert!(matches!(err, OrderingKeyError::Empty));
85 }
86}