1use chrono::{DateTime, Utc};
8use tokio::sync::mpsc;
9
10use crate::types::User;
11
12#[derive(Debug, Clone)]
13#[non_exhaustive]
14pub enum LifecycleEvent {
15 Registered(RegisteredEvent),
16}
17
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub struct RegisteredEvent {
21 pub user: User,
22 pub source: RegistrationSource,
23 pub ctx: EventContext,
24}
25
26impl RegisteredEvent {
27 pub fn new(user: User, source: RegistrationSource, ctx: EventContext) -> Self {
31 Self { user, source, ctx }
32 }
33}
34
35#[derive(Debug, Clone)]
36#[non_exhaustive]
37pub enum RegistrationSource {
38 Password,
39 Invitation {
40 email: Option<String>,
41 metadata: Option<String>,
42 },
43 OAuth {
44 provider: String,
45 },
46}
47
48#[derive(Debug, Clone)]
49#[non_exhaustive]
50pub struct EventContext {
51 pub ip: Option<String>,
52 pub user_agent: Option<String>,
53 pub base_url: String,
54 pub occurred_at: DateTime<Utc>,
55}
56
57impl EventContext {
58 pub fn new(
62 ip: Option<String>,
63 user_agent: Option<String>,
64 base_url: String,
65 occurred_at: DateTime<Utc>,
66 ) -> Self {
67 Self {
68 ip,
69 user_agent,
70 base_url,
71 occurred_at,
72 }
73 }
74}
75
76pub type LifecycleEventSender = mpsc::UnboundedSender<LifecycleEvent>;
77pub type LifecycleEventReceiver = mpsc::UnboundedReceiver<LifecycleEvent>;
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::types::{Email, UserId};
83
84 fn sample_user() -> User {
85 User {
86 id: UserId::new(),
87 email: Email::new("test@example.com".into()).unwrap(),
88 username: None,
89 password_hash: None,
90 email_verified: false,
91 is_active: true,
92 created_at: Utc::now(),
93 updated_at: Utc::now(),
94 custom_data: None,
95 }
96 }
97
98 #[test]
99 fn registered_event_constructs_and_clones() {
100 let event = LifecycleEvent::Registered(RegisteredEvent::new(
101 sample_user(),
102 RegistrationSource::Password,
103 EventContext::new(
104 Some("127.0.0.1".into()),
105 Some("test-agent".into()),
106 "http://test".into(),
107 Utc::now(),
108 ),
109 ));
110
111 let cloned = event.clone();
112 let _ = format!("{cloned:?}");
114 }
115
116 #[test]
117 fn oauth_source_carries_provider() {
118 let source = RegistrationSource::OAuth {
119 provider: "mock".into(),
120 };
121 let _ = format!("{source:?}");
122 let cloned = source.clone();
123 match cloned {
124 RegistrationSource::OAuth { provider } => assert_eq!(provider, "mock"),
125 _ => panic!("expected OAuth variant"),
126 }
127 }
128}