1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use strum_macros::Display;

use super::DriverStatusProperty;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DebitNoteEvent {
    pub debit_note_id: String,
    pub event_date: DateTime<Utc>,
    #[serde(flatten)]
    pub event_type: DebitNoteEventType,
}

#[derive(Clone, Debug, Display, Serialize, Deserialize)]
#[serde(tag = "eventType")]
pub enum DebitNoteEventType {
    DebitNoteReceivedEvent,
    DebitNoteAcceptedEvent,
    DebitNoteRejectedEvent {
        rejection: crate::payment::Rejection,
    },
    DebitNoteCancelledEvent,
    DebitNoteSettledEvent,
    DebitNotePaymentStatusEvent {
        property: DriverStatusProperty,
    },
    DebitNotePaymentOkEvent,
}

impl DebitNoteEventType {
    pub fn discriminant(&self) -> &'static str {
        use DebitNoteEventType::*;
        match self {
            DebitNoteReceivedEvent => "RECEIVED",
            DebitNoteAcceptedEvent => "ACCEPTED",
            DebitNoteRejectedEvent { .. } => "REJECTED",
            DebitNoteCancelledEvent => "CANCELLED",
            DebitNoteSettledEvent => "SETTLED",
            DebitNotePaymentStatusEvent { .. } => "PAYMENT_EVENT",
            DebitNotePaymentOkEvent => "PAYMENT_OK",
        }
    }

    pub fn details(&self) -> Option<serde_json::Value> {
        use serde_json::to_value;
        use DebitNoteEventType::*;

        match self {
            DebitNoteRejectedEvent { rejection } => to_value(rejection).ok(),
            DebitNotePaymentStatusEvent { property } => to_value(property).ok(),
            _ => None,
        }
    }

    pub fn from_discriminant_and_details(
        discriminant: &str,
        details: Option<serde_json::Value>,
    ) -> Option<Self> {
        use serde_json::from_value;
        use DebitNoteEventType::*;

        Some(match (discriminant, details) {
            ("RECEIVED", _) => DebitNoteReceivedEvent,
            ("ACCEPTED", _) => DebitNoteAcceptedEvent,
            ("REJECTED", Some(details)) => DebitNoteRejectedEvent {
                rejection: from_value(details).ok()?,
            },
            ("CANCELLED", _) => DebitNoteCancelledEvent,
            ("SETTLED", _) => DebitNoteSettledEvent,
            ("PAYMENT_EVENT", Some(details)) => DebitNotePaymentStatusEvent {
                property: from_value(details).ok()?,
            },
            ("PAYMENT_OK", _) => DebitNotePaymentOkEvent,
            _ => None?,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::payment::{Rejection, RejectionReason};
    use bigdecimal::{BigDecimal, FromPrimitive};

    #[test]
    fn test_serialize_rejected_event_has_flat_rejection() {
        let ie = DebitNoteEvent {
            debit_note_id: "ajdik".to_string(),
            event_date: DateTime::parse_from_str("2020-12-21T15:51:21.126645Z", "%+")
                .unwrap()
                .into(),
            event_type: DebitNoteEventType::DebitNoteRejectedEvent {
                rejection: Rejection {
                    rejection_reason: RejectionReason::UnsolicitedService,
                    total_amount_accepted: BigDecimal::from_f32(13.14).unwrap(),
                    message: None,
                },
            },
        };

        assert_eq!(
            "{\"debitNoteId\":\"ajdik\",\
                \"eventDate\":\"2020-12-21T15:51:21.126645Z\",\
                \"eventType\":\"DebitNoteRejectedEvent\",\
                \"rejection\":{\
                    \"rejectionReason\":\"UNSOLICITED_SERVICE\",\
                    \"totalAmountAccepted\":\"13.14000\"\
                }\
             }",
            serde_json::to_string(&ie).unwrap()
        );
    }
}