Skip to main content

systemprompt_content/models/builders/
link.rs

1//! Parameter builders for link and click mutations.
2//!
3//! [`CreateLinkParams`] describes a trackable campaign link, while
4//! [`RecordClickParams`] and [`TrackClickParams`] carry the click-event fields
5//! captured on redirect — the former for a fully-resolved click row, the latter
6//! for the inbound tracking request before the click id is minted.
7
8use chrono::{DateTime, Utc};
9use systemprompt_identifiers::{
10    CampaignId, ContentId, ContextId, LinkClickId, LinkId, SessionId, TaskId, UserId,
11};
12
13#[derive(Debug, Clone)]
14pub struct CreateLinkParams {
15    pub short_code: String,
16    pub target_url: String,
17    pub link_type: String,
18    pub source_content_id: Option<ContentId>,
19    pub source_page: Option<String>,
20    pub campaign_id: Option<CampaignId>,
21    pub campaign_name: Option<String>,
22    pub utm_params: Option<String>,
23    pub link_text: Option<String>,
24    pub link_position: Option<String>,
25    pub destination_type: Option<String>,
26    pub is_active: bool,
27    pub expires_at: Option<DateTime<Utc>>,
28}
29
30impl CreateLinkParams {
31    pub const fn new(short_code: String, target_url: String, link_type: String) -> Self {
32        Self {
33            short_code,
34            target_url,
35            link_type,
36            source_content_id: None,
37            source_page: None,
38            campaign_id: None,
39            campaign_name: None,
40            utm_params: None,
41            link_text: None,
42            link_position: None,
43            destination_type: None,
44            is_active: true,
45            expires_at: None,
46        }
47    }
48
49    pub fn with_source_content_id(mut self, source_content_id: Option<ContentId>) -> Self {
50        self.source_content_id = source_content_id;
51        self
52    }
53
54    pub fn with_source_page(mut self, source_page: Option<String>) -> Self {
55        self.source_page = source_page;
56        self
57    }
58
59    pub fn with_campaign_id(mut self, campaign_id: Option<CampaignId>) -> Self {
60        self.campaign_id = campaign_id;
61        self
62    }
63
64    pub fn with_campaign_name(mut self, campaign_name: Option<String>) -> Self {
65        self.campaign_name = campaign_name;
66        self
67    }
68
69    pub fn with_utm_params(mut self, utm_params: Option<String>) -> Self {
70        self.utm_params = utm_params;
71        self
72    }
73
74    pub fn with_link_text(mut self, link_text: Option<String>) -> Self {
75        self.link_text = link_text;
76        self
77    }
78
79    pub fn with_link_position(mut self, link_position: Option<String>) -> Self {
80        self.link_position = link_position;
81        self
82    }
83
84    pub fn with_destination_type(mut self, destination_type: Option<String>) -> Self {
85        self.destination_type = destination_type;
86        self
87    }
88
89    pub const fn with_is_active(mut self, is_active: bool) -> Self {
90        self.is_active = is_active;
91        self
92    }
93
94    pub const fn with_expires_at(mut self, expires_at: Option<DateTime<Utc>>) -> Self {
95        self.expires_at = expires_at;
96        self
97    }
98}
99
100#[derive(Debug, Clone)]
101pub struct RecordClickParams {
102    pub click_id: LinkClickId,
103    pub link_id: LinkId,
104    pub session_id: SessionId,
105    pub user_id: Option<UserId>,
106    pub context_id: Option<ContextId>,
107    pub task_id: Option<TaskId>,
108    pub referrer_page: Option<String>,
109    pub referrer_url: Option<String>,
110    pub clicked_at: DateTime<Utc>,
111    pub user_agent: Option<String>,
112    pub ip_address: Option<String>,
113    pub device_type: Option<String>,
114    pub country: Option<String>,
115    pub is_first_click: bool,
116    pub is_conversion: bool,
117}
118
119impl RecordClickParams {
120    pub const fn new(
121        click_id: LinkClickId,
122        link_id: LinkId,
123        session_id: SessionId,
124        clicked_at: DateTime<Utc>,
125    ) -> Self {
126        Self {
127            click_id,
128            link_id,
129            session_id,
130            user_id: None,
131            context_id: None,
132            task_id: None,
133            referrer_page: None,
134            referrer_url: None,
135            clicked_at,
136            user_agent: None,
137            ip_address: None,
138            device_type: None,
139            country: None,
140            is_first_click: false,
141            is_conversion: false,
142        }
143    }
144
145    pub fn with_user_id(mut self, user_id: Option<UserId>) -> Self {
146        self.user_id = user_id;
147        self
148    }
149
150    pub fn with_context_id(mut self, context_id: Option<ContextId>) -> Self {
151        self.context_id = context_id;
152        self
153    }
154
155    pub fn with_task_id(mut self, task_id: Option<TaskId>) -> Self {
156        self.task_id = task_id;
157        self
158    }
159
160    pub fn with_referrer_page(mut self, referrer_page: Option<String>) -> Self {
161        self.referrer_page = referrer_page;
162        self
163    }
164
165    pub fn with_referrer_url(mut self, referrer_url: Option<String>) -> Self {
166        self.referrer_url = referrer_url;
167        self
168    }
169
170    pub fn with_user_agent(mut self, user_agent: Option<String>) -> Self {
171        self.user_agent = user_agent;
172        self
173    }
174
175    pub fn with_ip_address(mut self, ip_address: Option<String>) -> Self {
176        self.ip_address = ip_address;
177        self
178    }
179
180    pub fn with_device_type(mut self, device_type: Option<String>) -> Self {
181        self.device_type = device_type;
182        self
183    }
184
185    pub fn with_country(mut self, country: Option<String>) -> Self {
186        self.country = country;
187        self
188    }
189
190    pub const fn with_is_first_click(mut self, is_first_click: bool) -> Self {
191        self.is_first_click = is_first_click;
192        self
193    }
194
195    pub const fn with_is_conversion(mut self, is_conversion: bool) -> Self {
196        self.is_conversion = is_conversion;
197        self
198    }
199}
200
201#[derive(Debug, Clone)]
202pub struct TrackClickParams {
203    pub link_id: LinkId,
204    pub session_id: SessionId,
205    pub user_id: Option<UserId>,
206    pub context_id: Option<ContextId>,
207    pub task_id: Option<TaskId>,
208    pub referrer_page: Option<String>,
209    pub referrer_url: Option<String>,
210    pub user_agent: Option<String>,
211    pub ip_address: Option<String>,
212    pub device_type: Option<String>,
213    pub country: Option<String>,
214}
215
216impl TrackClickParams {
217    pub const fn new(link_id: LinkId, session_id: SessionId) -> Self {
218        Self {
219            link_id,
220            session_id,
221            user_id: None,
222            context_id: None,
223            task_id: None,
224            referrer_page: None,
225            referrer_url: None,
226            user_agent: None,
227            ip_address: None,
228            device_type: None,
229            country: None,
230        }
231    }
232
233    pub fn with_user_id(mut self, user_id: Option<UserId>) -> Self {
234        self.user_id = user_id;
235        self
236    }
237
238    pub fn with_context_id(mut self, context_id: Option<ContextId>) -> Self {
239        self.context_id = context_id;
240        self
241    }
242
243    pub fn with_task_id(mut self, task_id: Option<TaskId>) -> Self {
244        self.task_id = task_id;
245        self
246    }
247
248    pub fn with_referrer_page(mut self, referrer_page: Option<String>) -> Self {
249        self.referrer_page = referrer_page;
250        self
251    }
252
253    pub fn with_referrer_url(mut self, referrer_url: Option<String>) -> Self {
254        self.referrer_url = referrer_url;
255        self
256    }
257
258    pub fn with_user_agent(mut self, user_agent: Option<String>) -> Self {
259        self.user_agent = user_agent;
260        self
261    }
262
263    pub fn with_ip_address(mut self, ip_address: Option<String>) -> Self {
264        self.ip_address = ip_address;
265        self
266    }
267
268    pub fn with_device_type(mut self, device_type: Option<String>) -> Self {
269        self.device_type = device_type;
270        self
271    }
272
273    pub fn with_country(mut self, country: Option<String>) -> Self {
274        self.country = country;
275        self
276    }
277}