From 6a186d00256d29fccd1f9f92548a94391606dc65 Mon Sep 17 00:00:00 2001 From: Edgar Date: Tue, 5 Jan 2021 13:40:36 +0100 Subject: [PATCH] add typed currency --- Cargo.toml | 2 +- README.md | 11 ++++-- src/common.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++++++- src/errors.rs | 12 ++++++ src/lib.rs | 11 +++++- src/orders.rs | 8 ++-- 6 files changed, 140 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f9043c..27c0913 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "paypal-rs" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" authors = ["Edgar "] description = "A library that wraps the paypal api asynchronously." repository = "https://github.com/edg-l/paypal-rs/" diff --git a/README.md b/README.md index 2914e1f..d2274cb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg) ![Docs](https://docs.rs/paypal-rs/badge.svg) -A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously in a strongly typed manner. +A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously in a stringly typed manner. Crate: https://crates.io/crates/paypal-rs @@ -11,6 +11,8 @@ Documentation: https://docs.rs/paypal-rs Currently in early development. +Note: This README shows how to use the prerelease version, to view the README for `0.1.0` go [here](https://github.com/edg-l/paypal-rs/tree/0.1.0). + ## Example ```rust @@ -18,7 +20,8 @@ use paypal_rs::{ Client, HeaderParams, Prefer, - orders::{OrderPayload, Intent, PurchaseUnit, Amount} + orders::{OrderPayload, Intent, PurchaseUnit, Amount}, + common::Currency, }; #[tokio::main] @@ -33,7 +36,7 @@ async fn main() { let order_payload = OrderPayload::new( Intent::Authorize, vec![PurchaseUnit::new(Amount::new( - "EUR", "10.0", + Currency::EUR, "10.0", ))], ); @@ -76,4 +79,4 @@ You need the enviroment variables PAYPAL_CLIENTID and PAYPAL_SECRET to be set. - [ ] Referenced Payouts API - 0.12.0 - [ ] Vault API - 0.13.0 - [ ] Webhooks Management API - 0.14.0 -- [ ] Payment Experience Web Profiles API - 1.0.0 \ No newline at end of file +- [ ] Payment Experience Web Profiles API - 1.0.0 diff --git a/src/common.rs b/src/common.rs index 864638a..188175b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,6 +1,8 @@ //! Common paypal object definitions used amon 2 or more APIs use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use crate::errors::InvalidCurrencyError; /// The phone type. /// @@ -71,7 +73,7 @@ pub struct Address { #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct Money { /// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency. - pub currency_code: String, + pub currency_code: Currency, /// The value, which might be: /// - An integer for currencies like JPY that are not typically fractional. /// - A decimal fraction for currencies like TND that are subdivided into thousandths. @@ -105,3 +107,106 @@ pub struct LinkDescription { #[serde(skip_serializing_if = "Option::is_none")] pub method: Option, } + +/// ISO-4217 currency codes. +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum Currency { + /// Australian dollar + AUD, + /// Brazilian real, supported for in country paypal accounts only. + BRL, + /// Canadian dollar + CAD, + /// Chinese Renmenbi + CNY, + /// Czech koruna + CZK, + /// Danish krone + DKK, + /// Euro + EUR, + /// Hong Kong dollar + HKD, + /// Hungarian forint, does not support decimals. + HUF, + /// Indian rupee, supported for in country paypal india accounts only. + INR, + /// Israeli new shekel + ILS, + /// Japanese yen, does not support decimals. + JPY, + /// Malaysian ringgit + MYR, + /// Mexican peso + MXN, + /// New Taiwan dollar, does not support decimals. + TWD, + /// New Zealand dollar + NZD, + /// Norwegian krone + NOK, + /// Philippine peso + PHP, + /// Polish złoty + PLN, + /// Pound sterling + GBP, + /// Russian ruble + RUB, + /// Singapore dollar + SGD, + /// Swedish krona + SEK, + /// Swiss franc + CHF, + /// Thai baht + THB, + /// United States dollar + USD +} + +impl Default for Currency { + fn default() -> Self { + Self::EUR + } +} + +impl std::fmt::Display for Currency { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self, f) + } +} + +impl FromStr for Currency { + type Err = InvalidCurrencyError; + + fn from_str(s: &str) -> Result { + match s { + "AUD" => Ok(Self::AUD), + "BRL" => Ok(Self::BRL), + "CAD" => Ok(Self::CAD), + "CNY" => Ok(Self::CNY), + "CZK" => Ok(Self::CZK), + "DKK" => Ok(Self::DKK), + "EUR" => Ok(Self::EUR), + "HKD" => Ok(Self::HKD), + "HUF" => Ok(Self::HUF), + "INR" => Ok(Self::INR), + "ILS" => Ok(Self::ILS), + "JPY" => Ok(Self::JPY), + "MYR" => Ok(Self::MYR), + "MXN" => Ok(Self::MXN), + "NOK" => Ok(Self::NOK), + "PHP" => Ok(Self::PHP), + "PLN" => Ok(Self::PLN), + "GBP" => Ok(Self::GBP), + "RUB" => Ok(Self::RUB), + "SGD" => Ok(Self::SGD), + "SEK" => Ok(Self::SGD), + "CHF" => Ok(Self::CHF), + "THB" => Ok(Self::THB), + "USD" => Ok(Self::USD), + cur => Err(InvalidCurrencyError(cur.to_owned())) + } + } +} diff --git a/src/errors.rs b/src/errors.rs index ee7436d..81e04a2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -31,3 +31,15 @@ impl fmt::Display for ApiResponseError { } impl Error for ApiResponseError {} + +/// When a currency is invalid. +#[derive(Debug)] +pub struct InvalidCurrencyError(pub String); + +impl fmt::Display for InvalidCurrencyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} is not a valid currency", self.0) + } +} + +impl Error for InvalidCurrencyError {} diff --git a/src/lib.rs b/src/lib.rs index eebe253..80bee75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,6 +308,8 @@ impl Client { #[cfg(test)] mod tests { use crate::{orders::*, Client, HeaderParams, Prefer}; + use crate::common::Currency; + use std::str::FromStr; use std::env; async fn create_client() -> Client { @@ -324,7 +326,7 @@ mod tests { async fn test_order() { let mut client = create_client().await; - let order = OrderPayload::new(Intent::Authorize, vec![PurchaseUnit::new(Amount::new("EUR", "10.0"))]); + let order = OrderPayload::new(Intent::Authorize, vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))]); let ref_id = format!( "TEST-{:?}", @@ -359,4 +361,11 @@ mod tests { .await .unwrap(); } + + #[test] + fn test_currency() { + assert_eq!(Currency::EUR.to_string(), "EUR"); + assert_eq!(Currency::JPY.to_string(), "JPY"); + assert_eq!(Currency::JPY, Currency::from_str("JPY").unwrap()); + } } diff --git a/src/orders.rs b/src/orders.rs index 6176188..c05a687 100644 --- a/src/orders.rs +++ b/src/orders.rs @@ -141,7 +141,7 @@ pub struct Breakdown { #[derive(Debug, Default, Serialize, Deserialize)] pub struct Amount { /// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency. - pub currency_code: String, + pub currency_code: Currency, /// The value, which might be: /// - An integer for currencies like JPY that are not typically fractional. /// - A decimal fraction for currencies like TND that are subdivided into thousandths. @@ -155,10 +155,10 @@ pub struct Amount { impl Amount { /// Creates a new amount with the required values. - pub fn new>(currency_code: S, value: S) -> Self { + pub fn new(currency: Currency, value: &str) -> Self { Amount { - currency_code: currency_code.into(), - value: value.into(), + currency_code: currency, + value: value.to_owned(), breakdown: None, } }