konduto/types/events/
event.rs

1use crate::types::validation_errors::ValidationError;
2use serde::{Deserialize, Serialize};
3
4use super::{EventName, EventSubtype, EventType, Ticket, Venue};
5
6/// Dados do evento
7///
8/// Ref: <https://docs.konduto.com/reference/event>
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct Event {
11    /// Nome do evento (obrigatório)
12    pub name: EventName,
13
14    /// Data e hora do evento em UTC (obrigatório, formato ISO 8601: YYYY-MM-DDThh:mmZ)
15    pub date: String,
16
17    /// Tipo do evento (obrigatório)
18    #[serde(rename = "type")]
19    pub event_type: EventType,
20
21    /// Subtipo/detalhe do evento (opcional)
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub subtype: Option<EventSubtype>,
24
25    /// Local do evento (opcional)
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub venue: Option<Venue>,
28
29    /// Lista de ingressos (opcional)
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub tickets: Option<Vec<Ticket>>,
32}
33
34impl Event {
35    /// Cria um builder para construir um evento
36    pub fn builder() -> EventBuilder {
37        EventBuilder::default()
38    }
39}
40
41/// Builder para criar eventos
42#[derive(Default)]
43pub struct EventBuilder {
44    name: Option<EventName>,
45    date: Option<String>,
46    event_type: Option<EventType>,
47    subtype: Option<EventSubtype>,
48    venue: Option<Venue>,
49    tickets: Vec<Ticket>,
50}
51
52impl EventBuilder {
53    pub fn name(mut self, name: EventName) -> Self {
54        self.name = Some(name);
55        self
56    }
57
58    pub fn name_str(mut self, name: impl Into<String>) -> Result<Self, ValidationError> {
59        self.name = Some(EventName::try_new(name)?);
60        Ok(self)
61    }
62
63    pub fn date(mut self, date: impl Into<String>) -> Self {
64        self.date = Some(date.into());
65        self
66    }
67
68    pub fn event_type(mut self, event_type: EventType) -> Self {
69        self.event_type = Some(event_type);
70        self
71    }
72
73    pub fn subtype(mut self, subtype: EventSubtype) -> Self {
74        self.subtype = Some(subtype);
75        self
76    }
77
78    pub fn subtype_str(mut self, subtype: impl Into<String>) -> Result<Self, ValidationError> {
79        self.subtype = Some(EventSubtype::try_new(subtype)?);
80        Ok(self)
81    }
82
83    pub fn venue(mut self, venue: Venue) -> Self {
84        self.venue = Some(venue);
85        self
86    }
87
88    pub fn add_ticket(mut self, ticket: Ticket) -> Self {
89        self.tickets.push(ticket);
90        self
91    }
92
93    pub fn tickets(mut self, tickets: Vec<Ticket>) -> Self {
94        self.tickets = tickets;
95        self
96    }
97
98    pub fn build(self) -> Result<Event, ValidationError> {
99        let date = self
100            .date
101            .ok_or_else(|| ValidationError::EmptyField("event_date".to_string()))?;
102
103        // Validar formato da data (max 17 chars para YYYY-MM-DDThh:mmZ)
104        if date.len() > 17 {
105            return Err(ValidationError::TooLong {
106                field: "event_date".to_string(),
107                max: 17,
108                actual: date.len(),
109            });
110        }
111
112        let tickets = if self.tickets.is_empty() {
113            None
114        } else {
115            Some(self.tickets)
116        };
117
118        Ok(Event {
119            name: self
120                .name
121                .ok_or_else(|| ValidationError::EmptyField("event_name".to_string()))?,
122            date,
123            event_type: self
124                .event_type
125                .ok_or_else(|| ValidationError::EmptyField("event_type".to_string()))?,
126            subtype: self.subtype,
127            venue: self.venue,
128            tickets,
129        })
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::types::address::Country;
137    use crate::types::events::{Attendee, TicketCategory};
138
139    #[test]
140    fn test_event_complete() {
141        let venue = Venue::builder()
142            .name_str("Teatro Municipal")
143            .unwrap()
144            .city_str("São Paulo")
145            .unwrap()
146            .country(Country::BR)
147            .capacity(500)
148            .build()
149            .unwrap();
150
151        let attendee = Attendee::builder()
152            .name_str("João Silva")
153            .unwrap()
154            .document_str("12345678900")
155            .unwrap()
156            .build()
157            .unwrap();
158
159        let ticket = Ticket::builder()
160            .category(TicketCategory::Regular)
161            .premium(false)
162            .attendee(attendee)
163            .build()
164            .unwrap();
165
166        let event = Event::builder()
167            .name_str("Peça de Teatro")
168            .unwrap()
169            .date("2025-12-01T20:00Z")
170            .event_type(EventType::Theater)
171            .subtype_str("drama")
172            .unwrap()
173            .venue(venue)
174            .add_ticket(ticket)
175            .build()
176            .unwrap();
177
178        assert_eq!(event.name.as_str(), "Peça de Teatro");
179        assert_eq!(event.date, "2025-12-01T20:00Z");
180        assert_eq!(event.event_type, EventType::Theater);
181        assert!(event.venue.is_some());
182        assert!(event.tickets.is_some());
183    }
184
185    #[test]
186    fn test_event_minimal() {
187        let event = Event::builder()
188            .name_str("Show Simples")
189            .unwrap()
190            .date("2025-12-01T20:00Z")
191            .event_type(EventType::Show)
192            .build()
193            .unwrap();
194
195        assert_eq!(event.name.as_str(), "Show Simples");
196        assert!(event.venue.is_none());
197        assert!(event.tickets.is_none());
198        assert!(event.subtype.is_none());
199    }
200
201    #[test]
202    fn test_event_multiple_tickets() {
203        let attendee1 = Attendee::builder()
204            .document_str("111")
205            .unwrap()
206            .build()
207            .unwrap();
208
209        let attendee2 = Attendee::builder()
210            .document_str("222")
211            .unwrap()
212            .build()
213            .unwrap();
214
215        let ticket1 = Ticket::builder()
216            .category(TicketCategory::Student)
217            .premium(false)
218            .attendee(attendee1)
219            .build()
220            .unwrap();
221
222        let ticket2 = Ticket::builder()
223            .category(TicketCategory::Regular)
224            .premium(true)
225            .attendee(attendee2)
226            .build()
227            .unwrap();
228
229        let event = Event::builder()
230            .name_str("Festival")
231            .unwrap()
232            .date("2025-12-01T20:00Z")
233            .event_type(EventType::Festival)
234            .add_ticket(ticket1)
235            .add_ticket(ticket2)
236            .build()
237            .unwrap();
238
239        assert_eq!(event.tickets.as_ref().unwrap().len(), 2);
240    }
241
242    #[test]
243    fn test_event_missing_name() {
244        let result = Event::builder()
245            .date("2025-12-01T20:00Z")
246            .event_type(EventType::Show)
247            .build();
248
249        assert!(result.is_err());
250    }
251
252    #[test]
253    fn test_event_missing_date() {
254        let result = Event::builder()
255            .name_str("Test")
256            .unwrap()
257            .event_type(EventType::Show)
258            .build();
259
260        assert!(result.is_err());
261    }
262
263    #[test]
264    fn test_event_missing_type() {
265        let result = Event::builder()
266            .name_str("Test")
267            .unwrap()
268            .date("2025-12-01T20:00Z")
269            .build();
270
271        assert!(result.is_err());
272    }
273
274    #[test]
275    fn test_event_serialization() {
276        let event = Event::builder()
277            .name_str("Rock Concert")
278            .unwrap()
279            .date("2025-12-01T20:00Z")
280            .event_type(EventType::Show)
281            .subtype_str("rock")
282            .unwrap()
283            .build()
284            .unwrap();
285
286        let json = serde_json::to_string_pretty(&event).unwrap();
287        assert!(json.contains("Rock Concert"));
288        assert!(json.contains("2025-12-01T20:00Z"));
289        assert!(json.contains("show"));
290        assert!(json.contains("rock"));
291    }
292
293    #[test]
294    fn test_event_deserialization() {
295        let json = r#"{
296            "name": "Test Event",
297            "date": "2025-12-01T20:00Z",
298            "type": "theater"
299        }"#;
300
301        let event: Event = serde_json::from_str(json).unwrap();
302        assert_eq!(event.name.as_str(), "Test Event");
303        assert_eq!(event.event_type, EventType::Theater);
304    }
305
306    #[test]
307    fn test_event_roundtrip() {
308        let original = Event::builder()
309            .name_str("Test")
310            .unwrap()
311            .date("2025-12-01T20:00Z")
312            .event_type(EventType::Sports)
313            .build()
314            .unwrap();
315
316        let json = serde_json::to_string(&original).unwrap();
317        let deserialized: Event = serde_json::from_str(&json).unwrap();
318
319        assert_eq!(original, deserialized);
320    }
321
322    #[test]
323    fn test_event_all_types() {
324        let types = vec![
325            EventType::Show,
326            EventType::Theater,
327            EventType::Movies,
328            EventType::Party,
329            EventType::Festival,
330            EventType::Course,
331            EventType::Sports,
332            EventType::Corporate,
333        ];
334
335        for event_type in types {
336            let event = Event::builder()
337                .name_str("Test Event")
338                .unwrap()
339                .date("2025-12-01T20:00Z")
340                .event_type(event_type)
341                .build()
342                .unwrap();
343
344            assert_eq!(event.event_type, event_type);
345        }
346    }
347}
348