use std::collections::HashMap;
use std::ops::Range;
use reqwest::Method;
use serde::Serialize;
use serde_with::skip_serializing_none;
use crate::entities::{Duration, Money, Price, PriceQuantity, UnitPriceOverride};
use crate::enums::{CatalogType, CountryCodeSupported, CurrencyCode, Interval, Status, TaxMode};
use crate::ids::{PriceID, ProductID};
use crate::paginated::Paginated;
use crate::nullable::Nullable;
use crate::{Paddle, Result};
#[skip_serializing_none]
#[derive(Serialize)]
pub struct PricesList<'a> {
#[serde(skip)]
client: &'a Paddle,
after: Option<PriceID>,
#[serde(serialize_with = "crate::comma_separated")]
id: Option<Vec<PriceID>>,
#[serde(serialize_with = "crate::comma_separated")]
include: Option<Vec<String>>,
order_by: Option<String>,
per_page: Option<usize>,
#[serde(serialize_with = "crate::comma_separated")]
product_id: Option<Vec<ProductID>>,
status: Option<Status>,
recurring: Option<bool>,
r#type: Option<CatalogType>,
}
impl<'a> PricesList<'a> {
pub fn new(client: &'a Paddle) -> Self {
Self {
client,
after: None,
id: None,
include: None,
order_by: None,
per_page: None,
product_id: None,
status: None,
recurring: None,
r#type: None,
}
}
pub fn after(&mut self, price_id: impl Into<PriceID>) -> &mut Self {
self.after = Some(price_id.into());
self
}
pub fn ids(&mut self, price_ids: impl IntoIterator<Item = impl Into<PriceID>>) -> &mut Self {
self.id = Some(price_ids.into_iter().map(Into::into).collect());
self
}
pub fn include(&mut self, includes: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
self.include = Some(includes.into_iter().map(Into::into).collect());
self
}
pub fn order_by_asc(&mut self, field: &str) -> &mut Self {
self.order_by = Some(format!("{}[ASC]", field));
self
}
pub fn order_by_desc(&mut self, field: &str) -> &mut Self {
self.order_by = Some(format!("{}[DESC]", field));
self
}
pub fn per_page(&mut self, entities_per_page: usize) -> &mut Self {
self.per_page = Some(entities_per_page);
self
}
pub fn product_ids(
&mut self,
product_ids: impl IntoIterator<Item = impl Into<ProductID>>,
) -> &mut Self {
self.product_id = Some(product_ids.into_iter().map(Into::into).collect());
self
}
pub fn status(&mut self, status: Status) -> &mut Self {
self.status = Some(status);
self
}
pub fn recurring(&mut self, value: bool) -> &mut Self {
self.recurring = Some(value);
self
}
pub fn r#type(&mut self, catalog_type: CatalogType) -> &mut Self {
self.r#type = Some(catalog_type);
self
}
pub fn send(&self) -> Paginated<'_, Vec<Price>> {
Paginated::new(self.client, "/prices", self)
}
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct PricesCreate<'a> {
#[serde(skip)]
client: &'a Paddle,
description: String,
product_id: ProductID,
unit_price: Money,
r#type: Option<CatalogType>,
name: Option<String>,
billing_cycle: Option<Duration>,
trial_period: Option<Duration>,
tax_mode: TaxMode,
unit_price_overrides: Option<Vec<UnitPriceOverride>>,
quantity: Option<PriceQuantity>,
custom_data: Option<HashMap<String, String>>,
}
impl<'a> PricesCreate<'a> {
pub fn new(
client: &'a Paddle,
product_id: impl Into<ProductID>,
description: impl Into<String>,
amount: u64,
currency: CurrencyCode,
) -> Self {
Self {
client,
description: description.into(),
product_id: product_id.into(),
unit_price: Money {
amount: amount.to_string(),
currency_code: currency,
},
r#type: None,
name: None,
billing_cycle: None,
trial_period: None,
tax_mode: TaxMode::AccountSetting,
unit_price_overrides: None,
quantity: None,
custom_data: None,
}
}
pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
self.r#type = Some(catalog_type);
self
}
pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = Some(name.into());
self
}
pub fn billing_cycle(&mut self, frequency: u64, interval: Interval) -> &mut Self {
self.billing_cycle = Some(Duration {
interval,
frequency,
});
self
}
pub fn trial_period(&mut self, frequency: u64, interval: Interval) -> &mut Self {
self.trial_period = Some(Duration {
interval,
frequency,
});
self
}
pub fn tax_mode(&mut self, tax_mode: TaxMode) -> &mut Self {
self.tax_mode = tax_mode;
self
}
pub fn add_unit_price_override(
&mut self,
country_codes: impl IntoIterator<Item = CountryCodeSupported>,
amount: u64,
currency: CurrencyCode,
) -> &mut Self {
if self.unit_price_overrides.is_none() {
self.unit_price_overrides = Some(vec![]);
}
self.unit_price_overrides
.as_mut()
.unwrap()
.push(UnitPriceOverride {
country_codes: country_codes.into_iter().collect(),
unit_price: Money {
amount: amount.to_string(),
currency_code: currency,
},
});
self
}
pub fn set_unit_price_overrides(&mut self, overrides: Vec<UnitPriceOverride>) -> &mut Self {
self.unit_price_overrides = Some(overrides);
self
}
pub fn quantity(&mut self, range: Range<u64>) -> &mut Self {
self.quantity = Some(PriceQuantity {
minimum: range.start,
maximum: range.end,
});
self
}
pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
self.custom_data = Some(custom_data);
self
}
pub async fn send(&self) -> Result<Price> {
self.client.send(self, Method::POST, "/prices").await
}
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct PriceGet<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
price_id: PriceID,
#[serde(serialize_with = "crate::comma_separated")]
include: Option<Vec<String>>,
}
impl<'a> PriceGet<'a> {
pub fn new(client: &'a Paddle, price_id: impl Into<PriceID>) -> Self {
Self {
client,
price_id: price_id.into(),
include: None,
}
}
pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
self.include = Some(
entities
.into_iter()
.map(|s| s.as_ref().to_string())
.collect(),
);
self
}
pub async fn send(&self) -> Result<Price> {
self.client
.send(
self,
Method::GET,
&format!("/prices/{}", self.price_id.as_ref()),
)
.await
}
}
#[derive(Serialize)]
pub struct PriceUpdate<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
price_id: PriceID,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
description: Nullable<String>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
r#type: Nullable<CatalogType>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
name: Nullable<String>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
billing_cycle: Nullable<Duration>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
trial_period: Nullable<Duration>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
tax_mode: Nullable<TaxMode>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
unit_price: Nullable<Money>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
unit_price_overrides: Nullable<Vec<UnitPriceOverride>>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
quantity: Nullable<PriceQuantity>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
status: Nullable<Status>,
#[serde(skip_serializing_if = "Nullable::is_unchanged")]
custom_data: Nullable<HashMap<String, String>>,
}
impl<'a> PriceUpdate<'a> {
pub fn new(client: &'a Paddle, price_id: impl Into<PriceID>) -> Self {
Self {
client,
price_id: price_id.into(),
description: Nullable::Unchanged,
r#type: Nullable::Unchanged,
name: Nullable::Unchanged,
billing_cycle: Nullable::Unchanged,
trial_period: Nullable::Unchanged,
tax_mode: Nullable::Unchanged,
unit_price: Nullable::Unchanged,
unit_price_overrides: Nullable::Unchanged,
quantity: Nullable::Unchanged,
status: Nullable::Unchanged,
custom_data: Nullable::Unchanged,
}
}
pub fn description(&mut self, description: impl Into<Nullable<String>>) -> &mut Self {
self.description = description.into();
self
}
pub fn catalog_type(&mut self, catalog_type: impl Into<Nullable<CatalogType>>) -> &mut Self {
self.r#type = catalog_type.into();
self
}
pub fn name(&mut self, name: impl Into<Nullable<String>>) -> &mut Self {
self.name = name.into();
self
}
pub fn billing_cycle(
&mut self,
billing_cycle: impl Into<Nullable<Duration>>,
) -> &mut Self {
self.billing_cycle = billing_cycle.into();
self
}
pub fn trial_period(
&mut self,
trial_period: impl Into<Nullable<Duration>>,
) -> &mut Self {
self.trial_period = trial_period.into();
self
}
pub fn tax_mode(&mut self, tax_mode: impl Into<Nullable<TaxMode>>) -> &mut Self {
self.tax_mode = tax_mode.into();
self
}
pub fn unit_price(&mut self, unit_price: impl Into<Nullable<Money>>) -> &mut Self {
self.unit_price = unit_price.into();
self
}
pub fn add_unit_price_override(
&mut self,
country_codes: impl IntoIterator<Item = CountryCodeSupported>,
amount: u64,
currency: CurrencyCode,
) -> &mut Self {
if !matches!(self.unit_price_overrides, Nullable::Value(_)) {
self.unit_price_overrides = Nullable::Value(vec![]);
}
if let Nullable::Value(ref mut v) = self.unit_price_overrides {
v.push(UnitPriceOverride {
country_codes: country_codes.into_iter().collect(),
unit_price: Money {
amount: amount.to_string(),
currency_code: currency,
},
});
}
self
}
pub fn set_unit_price_overrides(
&mut self,
overrides: impl Into<Nullable<Vec<UnitPriceOverride>>>,
) -> &mut Self {
self.unit_price_overrides = overrides.into();
self
}
pub fn quantity(&mut self, quantity: impl Into<Nullable<PriceQuantity>>) -> &mut Self {
self.quantity = quantity.into();
self
}
pub fn status(&mut self, status: impl Into<Nullable<Status>>) -> &mut Self {
self.status = status.into();
self
}
pub fn custom_data(
&mut self,
custom_data: impl Into<Nullable<HashMap<String, String>>>,
) -> &mut Self {
self.custom_data = custom_data.into();
self
}
pub async fn send(&self) -> Result<Price> {
self.client
.send(
self,
Method::PATCH,
&format!("/prices/{}", self.price_id.as_ref()),
)
.await
}
}