paddle_rust_sdk/
products.rs

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