paddle_rust_sdk/
products.rs

1//! Builders for making requests to the Paddle API for product entities.
2//!
3//! See the [Paddle API](https://developer.paddle.com/api-reference/products/overview) documentation for more information.
4
5use std::collections::HashMap;
6
7use reqwest::Method;
8use serde::Serialize;
9use serde_with::skip_serializing_none;
10
11use crate::entities::Product;
12use crate::enums::{CatalogType, Status, TaxCategory};
13use crate::ids::ProductID;
14use crate::{Paddle, Result};
15
16/// Request builder for fetching products from Paddle API.
17#[skip_serializing_none]
18#[derive(Serialize)]
19pub struct ProductsList<'a> {
20    #[serde(skip)]
21    client: &'a Paddle,
22    after: Option<ProductID>,
23    #[serde(serialize_with = "crate::comma_separated")]
24    id: Option<Vec<ProductID>>,
25    #[serde(serialize_with = "crate::comma_separated")]
26    include: Option<Vec<String>>,
27    order_by: Option<String>,
28    per_page: Option<usize>,
29    status: Option<Status>,
30    #[serde(serialize_with = "crate::comma_separated")]
31    tax_category: Option<Vec<TaxCategory>>,
32    r#type: Option<CatalogType>,
33}
34
35impl<'a> ProductsList<'a> {
36    pub fn new(client: &'a Paddle) -> Self {
37        Self {
38            client,
39            after: None,
40            id: None,
41            include: None,
42            order_by: None,
43            per_page: None,
44            status: None,
45            tax_category: None,
46            r#type: None,
47        }
48    }
49
50    /// Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations.
51    pub fn after(&mut self, product_id: impl Into<ProductID>) -> &mut Self {
52        self.after = Some(product_id.into());
53        self
54    }
55
56    /// Return only the IDs specified.
57    pub fn ids(
58        &mut self,
59        product_ids: impl IntoIterator<Item = impl Into<ProductID>>,
60    ) -> &mut Self {
61        self.id = Some(product_ids.into_iter().map(|i| i.into()).collect());
62        self
63    }
64
65    /// Include related entities in the response. Valid values are: "prices".
66    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
67        self.include = Some(
68            entities
69                .into_iter()
70                .map(|s| s.as_ref().to_string())
71                .collect(),
72        );
73        self
74    }
75
76    /// Order returned entities by the specified field. Valid fields for ordering: `created_at`, `custom_data`, `description`, `id`, `image_url`, `name`, `status`, `tax_category`, and `updated_at`.
77    pub fn order_by_asc(&mut self, field: &str) -> &mut Self {
78        self.order_by = Some(format!("{}[ASC]", field));
79        self
80    }
81
82    /// Order returned entities by the specified field. Valid fields for ordering: `created_at`, `custom_data`, `description`, `id`, `image_url`, `name`, `status`, `tax_category`, and `updated_at`.
83    pub fn order_by_desc(&mut self, field: &str) -> &mut Self {
84        self.order_by = Some(format!("{}[DESC]", field));
85        self
86    }
87
88    /// Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested.
89    /// Check `meta.pagination.per_page` in the response to see how many were returned.
90    ///
91    /// Default: `50`; Maximum: `200`.
92    pub fn per_page(&mut self, entities_per_page: usize) -> &mut Self {
93        self.per_page = Some(entities_per_page);
94        self
95    }
96
97    /// Return entities that match the specified status.
98    pub fn status(&mut self, status: Status) -> &mut Self {
99        self.status = Some(status);
100        self
101    }
102
103    /// Return entities that match the specified tax categories.
104    pub fn tax_category(
105        &mut self,
106        tax_categories: impl IntoIterator<Item = TaxCategory>,
107    ) -> &mut Self {
108        self.tax_category = Some(tax_categories.into_iter().collect());
109        self
110    }
111
112    /// Return entities that match the specified catalog type.
113    pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
114        self.r#type = Some(catalog_type);
115        self
116    }
117
118    /// Send the request to Paddle and return the response.
119    pub async fn send(&self) -> Result<Vec<Product>> {
120        self.client.send(self, Method::GET, "/products").await
121    }
122}
123
124/// Request builder for creating a new product in Paddle API.
125#[skip_serializing_none]
126#[derive(Serialize)]
127pub struct ProductCreate<'a> {
128    #[serde(skip)]
129    client: &'a Paddle,
130    name: String,
131    tax_category: TaxCategory,
132    description: Option<String>,
133    r#type: Option<CatalogType>,
134    image_url: Option<String>,
135    custom_data: Option<HashMap<String, String>>,
136}
137
138impl<'a> ProductCreate<'a> {
139    pub fn new(client: &'a Paddle, name: impl Into<String>, tax_category: TaxCategory) -> Self {
140        Self {
141            client,
142            name: name.into(),
143            tax_category,
144            description: None,
145            r#type: None,
146            image_url: None,
147            custom_data: None,
148        }
149    }
150
151    /// Set the product description.
152    pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
153        self.description = Some(description.into());
154        self
155    }
156
157    /// Set the product catalog type.
158    pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
159        self.r#type = Some(catalog_type);
160        self
161    }
162
163    /// Set the product image URL.
164    pub fn image_url(&mut self, image_url: impl Into<String>) -> &mut Self {
165        self.image_url = Some(image_url.into());
166        self
167    }
168
169    /// Set custom data for the product.
170    pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
171        self.custom_data = Some(custom_data);
172        self
173    }
174
175    /// Send the request to Paddle and return the response.
176    pub async fn send(&self) -> Result<Product> {
177        self.client.send(self, Method::POST, "/products").await
178    }
179}
180
181/// Request builder for fetching a specific product from Paddle API.
182#[skip_serializing_none]
183#[derive(Serialize)]
184pub struct ProductGet<'a> {
185    #[serde(skip)]
186    client: &'a Paddle,
187    #[serde(skip)]
188    product_id: ProductID,
189    #[serde(serialize_with = "crate::comma_separated")]
190    include: Option<Vec<String>>,
191}
192
193impl<'a> ProductGet<'a> {
194    pub fn new(client: &'a Paddle, product_id: impl Into<ProductID>) -> Self {
195        Self {
196            client,
197            product_id: product_id.into(),
198            include: None,
199        }
200    }
201
202    /// Include related entities in the response.
203    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
204        self.include = Some(
205            entities
206                .into_iter()
207                .map(|s| s.as_ref().to_string())
208                .collect(),
209        );
210        self
211    }
212
213    /// Send the request to Paddle and return the response.
214    pub async fn send(&self) -> Result<Product> {
215        self.client
216            .send(
217                self,
218                Method::GET,
219                &format!("/products/{}", self.product_id.as_ref()),
220            )
221            .await
222    }
223}
224
225/// Request builder for updating a product in Paddle API.
226#[skip_serializing_none]
227#[derive(Serialize)]
228pub struct ProductUpdate<'a> {
229    #[serde(skip)]
230    client: &'a Paddle,
231    #[serde(skip)]
232    product_id: ProductID,
233    name: Option<String>,
234    description: Option<String>,
235    r#type: Option<CatalogType>,
236    tax_category: Option<TaxCategory>,
237    image_url: Option<String>,
238    custom_data: Option<HashMap<String, String>>,
239    status: Option<Status>,
240}
241
242impl<'a> ProductUpdate<'a> {
243    pub fn new(client: &'a Paddle, product_id: impl Into<ProductID>) -> Self {
244        Self {
245            client,
246            product_id: product_id.into(),
247            name: None,
248            description: None,
249            r#type: None,
250            tax_category: None,
251            image_url: None,
252            custom_data: None,
253            status: None,
254        }
255    }
256
257    /// Set the product name.
258    pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
259        self.name = Some(name.into());
260        self
261    }
262
263    /// Set the product description.
264    pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
265        self.description = Some(description.into());
266        self
267    }
268
269    /// Set the product catalog type.
270    pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
271        self.r#type = Some(catalog_type);
272        self
273    }
274
275    /// Set the product tax category.
276    pub fn tax_category(&mut self, tax_category: TaxCategory) -> &mut Self {
277        self.tax_category = Some(tax_category);
278        self
279    }
280
281    /// Set the product image URL.
282    pub fn image_url(&mut self, image_url: impl Into<String>) -> &mut Self {
283        self.image_url = Some(image_url.into());
284        self
285    }
286
287    /// Set custom data for the product.
288    pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
289        self.custom_data = Some(custom_data);
290        self
291    }
292
293    /// Set the product status.
294    pub fn status(&mut self, status: Status) -> &mut Self {
295        self.status = Some(status);
296        self
297    }
298
299    /// Send the request to Paddle and return the response.
300    pub async fn send(&self) -> Result<Product> {
301        self.client
302            .send(
303                self,
304                Method::PATCH,
305                &format!("/products/{}", self.product_id.as_ref()),
306            )
307            .await
308    }
309}