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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! Transaction Split
//! =================
//! This file contains the structs and definitions need to create
//! transaction splits for the Paystack API.

use crate::{error::PaystackError, BearerType, Currency, PaystackResult, SplitType};
use serde::Serialize;

/// This struct is used to create a split payment on your integration.
///
/// IMPORTANT: This class can obly be created using the TransactionSplitBuilder.
/// THe struct has the following fields:
///     - name: Name of the transaction split
///     - type: The type of transaction split you want to create.
///       You can use one of the following: percentage | flat.
///     - currency: Any of NGN, GHS, ZAR, or USD
///     - subaccounts: A list of object containing subaccount
///       code and number of shares
///     - bearer_type: Any of subaccount | account | all-proportional | all
///     - bearer_subaccount: Subaccount code
#[derive(Serialize, Debug, Default)]
pub struct TransactionSplit {
    name: String,
    #[serde(rename = "type")]
    split_type: SplitType,
    currency: Currency,
    subaccounts: Vec<Subaccount>,
    bearer_type: BearerType,
    bearer_subaccount: String,
}

/// This struct represents the subacount that bears the transaction split
#[derive(Serialize, Debug, Clone)]
pub struct Subaccount {
    /// This is the sub account code
    pub subaccount: String,
    /// This is the transaction share for the subaccount
    pub share: u32,
}

impl Subaccount {
    /// Creates a new subaccount for the Paystack API
    pub fn new(subaccount: impl Into<String>, share: u32) -> Self {
        Subaccount {
            subaccount: subaccount.into(),
            share,
        }
    }
}

/// A builder pattern implementation for constructing `PercentageSplit` instances.
///
/// The `PercentageSplitBuilder` struct provides a fluent and readable way to construct
/// instances of the `PercentageSplit` struct.
///
/// # Errors
///
/// Returns a `PaystackResult` with an `Err` variant if any required fields are missing,
/// including email, amount, and currency. The error indicates which field is missing.
///
/// # Examples
///
/// ```rust
/// use paystack::{Currency, SplitType, BearerType, PercentageSplitBuilder, Subaccount};
///
/// let sub_accounts = vec![
///     Subaccount::new(
///         "ACCT_z3x6z3nbo14xsil",
///         20,
///     ),
///     Subaccount::new(
///         "ACCT_pwwualwty4nhq9d",
///         30,
///     ),
/// ];
///
/// let percentage_split = PercentageSplitBuilder::new()
///     .name("Percentage Split")
///     .split_type(SplitType::Percentage)
///     .currency(Currency::NGN)
///     .add_subaccount(sub_accounts)
///     .bearer_type(BearerType::Subaccount)
///     .bearer_subaccount("ACCT_hdl8abxl8drhrl3")
///     .build();
/// ```
#[derive(Debug, Default, Clone)]
pub struct PercentageSplitBuilder {
    name: Option<String>,
    split_type: Option<SplitType>,
    currency: Option<Currency>,
    subaccounts: Option<Vec<Subaccount>>,
    bearer_type: Option<BearerType>,
    bearer_subaccount: Option<String>,
}

impl PercentageSplitBuilder {
    /// Create a new instance of the Percentage Split builder with default properties
    pub fn new() -> Self {
        PercentageSplitBuilder::default()
    }

    /// Specify the transaction split name
    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    /// Specify the transaction split amount
    pub fn split_type(mut self, split_type: SplitType) -> Self {
        self.split_type = Some(split_type);
        self
    }

    /// Specify the transaction split currency
    pub fn currency(mut self, currency: Currency) -> Self {
        self.currency = Some(currency);
        self
    }

    /// Specify the subaccount for the transaction split
    pub fn add_subaccount(mut self, sub_accounts: Vec<Subaccount>) -> Self {
        self.subaccounts = Some(sub_accounts);
        self
    }

    /// Specify the bearer type for the transaction split
    pub fn bearer_type(mut self, bearer_type: BearerType) -> Self {
        self.bearer_type = Some(bearer_type);
        self
    }

    /// Specify the bearer subaccount code
    pub fn bearer_subaccount(mut self, code: impl Into<String>) -> Self {
        self.bearer_subaccount = Some(code.into());
        self
    }

    /// Build the TransactionSplit object
    pub fn build(self) -> PaystackResult<TransactionSplit> {
        let Some(name) = self.name else {
            return Err(
                PaystackError::TransactionSplit("name is required to create a transaction split".to_string())
            )
        };

        let Some(split_type) = self.split_type else {
            return Err(
                PaystackError::TransactionSplit("split type is required to create a transaction split".to_string())
            )
        };

        let Some(currency) = self.currency else {
            return Err(
                PaystackError::Transaction(
                    "currency is required to create a transaction split".to_string()
                )
            )
        };

        let Some(subaccounts) = self.subaccounts else {
            return Err(
                PaystackError::TransactionSplit(
                    "sub accounts are required to create a transaction split".to_string()
                )
            )
        };

        let Some(bearer_type) = self.bearer_type else {
            return Err(
                PaystackError::TransactionSplit(
                    "bearer type is required to create a transaction split".to_string()
                )
            )
        };

        let Some(bearer_subaccount) = self.bearer_subaccount else {
            return Err(
                PaystackError::TransactionSplit(
                    "bearer subaccount is required to create a transaction split".to_string()
                )
            )
        };

        Ok(TransactionSplit {
            name,
            split_type,
            currency,
            subaccounts,
            bearer_type,
            bearer_subaccount,
        })
    }
}