paykit_lib/
payment_amount.rs1use crate::{
2 validation::{validate_asset_text, validate_decimal_text},
3 Result,
4};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct PaymentAmount {
24 value: String,
26 asset: String,
28}
29
30impl PaymentAmount {
31 pub fn value(&self) -> &str {
33 &self.value
34 }
35 pub fn asset(&self) -> &str {
37 &self.asset
38 }
39
40 pub fn new(value: impl Into<String>, asset: impl Into<String>) -> Result<Self> {
42 Self::new_with_label(value.into(), asset.into(), "Payment Amount")
43 }
44
45 pub(crate) fn new_with_label(
46 value: String,
47 asset: String,
48 label: &'static str,
49 ) -> Result<Self> {
50 let amount = Self { value, asset };
51 amount.validate_with_label(label)?;
52 Ok(amount)
53 }
54
55 pub(crate) fn validate_with_label(&self, label: &'static str) -> Result<()> {
56 validate_decimal_text(&self.value, format_args!("{label}.value"))?;
57 validate_asset_text(&self.asset, "Payment Amount asset")
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::PaykitError;
65
66 #[test]
67 fn payment_amount_accepts_decimal_value_and_asset() {
68 let amount = PaymentAmount::new("10.00", "usd").unwrap();
69 assert_eq!(amount.value, "10.00");
70 assert_eq!(amount.asset, "usd");
71 }
72
73 #[test]
74 fn payment_amount_rejects_invalid_value() {
75 let err = PaymentAmount::new("ten", "usd").unwrap_err();
76 assert!(matches!(err, PaykitError::Validation(msg) if msg.contains("decimal string")));
77 }
78
79 #[test]
80 fn payment_amount_rejects_empty_asset() {
81 let err = PaymentAmount::new("10.00", "").unwrap_err();
82 assert!(matches!(err, PaykitError::Validation(msg) if msg.contains("must not be empty")));
83 }
84
85 #[test]
86 fn payment_amount_rejects_control_character_asset() {
87 let err = PaymentAmount::new("10.00", "usd\n").unwrap_err();
88 assert!(matches!(err, PaykitError::Validation(msg) if msg.contains("control characters")));
89 }
90
91 #[test]
92 fn payment_amount_rejects_multiple_decimal_points() {
93 let err = PaymentAmount::new("1.2.3", "usd").unwrap_err();
94 assert!(matches!(err, PaykitError::Validation(msg) if msg.contains("decimal string")));
95 }
96
97 #[test]
98 fn payment_amount_rejects_lone_decimal_point() {
99 let err = PaymentAmount::new(".", "usd").unwrap_err();
100 assert!(matches!(err, PaykitError::Validation(msg) if msg.contains("at least one digit")));
101 }
102
103 #[test]
108 fn test_payment_amount_bare_decimal_point_currently_accepted() {
109 let leading = PaymentAmount::new(".5", "usd").unwrap();
110 assert_eq!(leading.value, ".5");
111 let trailing = PaymentAmount::new("10.", "usd").unwrap();
112 assert_eq!(trailing.value, "10.");
113 }
114}