rust_woocommerce/controllers/
products.rs

1use chrono::NaiveDateTime;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5use crate::{
6    BackordersStatus, CatalogVisibility, MetaData, ProductStatus, ProductType, StockStatus,
7    TaxStatus,
8};
9#[skip_serializing_none]
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ProductModify {
12    id: Option<i32>,
13    name: Option<String>,
14    slug: Option<String>,
15    permalink: Option<String>,
16    #[serde(rename = "type")]
17    product_type: Option<ProductType>,
18    status: Option<ProductStatus>,
19    featured: Option<bool>,
20    catalog_visibility: Option<CatalogVisibility>,
21    description: Option<String>,
22    short_description: Option<String>,
23    sku: Option<String>,
24    regular_price: Option<String>,
25    sale_price: Option<String>,
26    date_on_sale_from: Option<NaiveDateTime>,
27    date_on_sale_to: Option<NaiveDateTime>,
28    #[serde(rename = "virtual")]
29    is_virtual: Option<bool>,
30    downloadable: Option<bool>,
31    downloads: Option<Vec<DownloadDTO>>,
32    download_limit: Option<i32>,
33    download_expiry: Option<i32>,
34    external_url: Option<String>,
35    button_text: Option<String>,
36    tax_status: Option<TaxStatus>,
37    tax_class: Option<String>,
38    manage_stock: Option<bool>,
39    stock_quantity: Option<i32>,
40    stock_status: Option<StockStatus>,
41    backorders: Option<BackordersStatus>,
42    sold_individually: Option<bool>,
43    weight: Option<String>,
44    dimensions: Option<DimensionsDTO>,
45    shipping_class: Option<String>,
46    reviews_allowed: Option<bool>,
47    related_ids: Option<Vec<i32>>,
48    upsell_ids: Option<Vec<i32>>,
49    cross_sell_ids: Option<Vec<i32>>,
50    parent_id: Option<i32>,
51    purchase_note: Option<String>,
52    categories: Option<Vec<CategoryDTO>>,
53    tags: Option<Vec<TagDTO>>,
54    images: Option<Vec<ImageDTO>>,
55    attributes: Option<Vec<AttributeDTO>>,
56    default_attributes: Option<Vec<DefaultAttributeDTO>>,
57    grouped_products: Option<Vec<i32>>,
58    menu_order: Option<i32>,
59    meta_data: Option<Vec<MetaData>>,
60}
61impl ProductModify {
62    pub fn builder() -> ProductModifyBuilder {
63        ProductModifyBuilder::default()
64    }
65}
66#[derive(Default)]
67pub struct ProductModifyBuilder {
68    id: Option<i32>,
69    name: Option<String>,
70    slug: Option<String>,
71    permalink: Option<String>,
72    product_type: Option<ProductType>,
73    status: Option<ProductStatus>,
74    featured: Option<bool>,
75    catalog_visibility: Option<CatalogVisibility>,
76    description: Option<String>,
77    short_description: Option<String>,
78    sku: Option<String>,
79    regular_price: Option<String>,
80    sale_price: Option<String>,
81    date_on_sale_from: Option<NaiveDateTime>,
82    date_on_sale_to: Option<NaiveDateTime>,
83    is_virtual: Option<bool>,
84    downloadable: Option<bool>,
85    downloads: Option<Vec<DownloadDTO>>,
86    download_limit: Option<i32>,
87    download_expiry: Option<i32>,
88    external_url: Option<String>,
89    button_text: Option<String>,
90    tax_status: Option<TaxStatus>,
91    tax_class: Option<String>,
92    manage_stock: Option<bool>,
93    stock_quantity: Option<i32>,
94    stock_status: Option<StockStatus>,
95    backorders: Option<BackordersStatus>,
96    sold_individually: Option<bool>,
97    weight: Option<String>,
98    dimensions: Option<DimensionsDTO>,
99    shipping_class: Option<String>,
100    reviews_allowed: Option<bool>,
101    related_ids: Option<Vec<i32>>,
102    upsell_ids: Option<Vec<i32>>,
103    cross_sell_ids: Option<Vec<i32>>,
104    parent_id: Option<i32>,
105    purchase_note: Option<String>,
106    categories: Option<Vec<CategoryDTO>>,
107    tags: Option<Vec<TagDTO>>,
108    images: Option<Vec<ImageDTO>>,
109    attributes: Option<Vec<AttributeDTO>>,
110    default_attributes: Option<Vec<DefaultAttributeDTO>>,
111    grouped_products: Option<Vec<i32>>,
112    menu_order: Option<i32>,
113    meta_data: Option<Vec<MetaData>>,
114}
115impl ProductModifyBuilder {
116    /// Unique identifier for the resource.
117    pub fn id(&mut self, id: i32) -> &mut Self {
118        let _ = self.id.insert(id);
119        self
120    }
121    /// Product name.
122    pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
123        let _ = self.name.insert(name.into());
124        self
125    }
126    /// Product slug.
127    pub fn slug(&mut self, slug: impl Into<String>) -> &mut Self {
128        let _ = self.slug.insert(slug.into());
129        self
130    }
131    /// Product URL.
132    pub fn permalink(&mut self, permalink: impl Into<String>) -> &mut Self {
133        let _ = self.permalink.insert(permalink.into());
134        self
135    }
136    /// Product type, Options: simple, grouped, external and variable. Default is simple.
137    pub fn product_type(&mut self, product_type: ProductType) -> &mut Self {
138        let _ = self.product_type.insert(product_type);
139        self
140    }
141    /// Product status (post status). Options: draft, pending, private and publish. Default is publish.
142    pub fn status(&mut self, status: ProductStatus) -> &mut Self {
143        let _ = self.status.insert(status);
144        self
145    }
146    /// Featured product. Default is false.
147    pub fn featured(&mut self) -> &mut Self {
148        let _ = self.featured.insert(true);
149        self
150    }
151    pub fn unfeatured(&mut self) -> &mut Self {
152        let _ = self.featured.insert(false);
153        self
154    }
155    /// Catalog visibility. Options: visible, catalog, search and hidden. Default is visible.
156    pub fn catalog_visibility(&mut self, catalog_visibility: CatalogVisibility) -> &mut Self {
157        let _ = self.catalog_visibility.insert(catalog_visibility);
158        self
159    }
160    /// Product description.
161    pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
162        let _ = self.description.insert(description.into());
163        self
164    }
165    /// Product short description.
166    pub fn short_description(&mut self, short_description: impl Into<String>) -> &mut Self {
167        let _ = self.short_description.insert(short_description.into());
168        self
169    }
170    /// Unique identifier.
171    pub fn sku(&mut self, sku: impl Into<String>) -> &mut Self {
172        let _ = self.sku.insert(sku.into());
173        self
174    }
175    /// Product regular price.
176    pub fn regular_price(&mut self, regular_price: impl Into<String>) -> &mut Self {
177        let _ = self.regular_price.insert(regular_price.into());
178        self
179    }
180    /// Product sale price.
181    pub fn sale_price(&mut self, sale_price: impl Into<String>) -> &mut Self {
182        let _ = self.sale_price.insert(sale_price.into());
183        self
184    }
185    /// Start date of sale price, in the site's timezone.
186    pub fn date_on_sale_from(&mut self, year: i32, month: u32, day: u32) -> &mut Self {
187        let dt = chrono::NaiveDate::from_ymd_opt(year, month, day)
188            .unwrap()
189            .and_hms_opt(0, 0, 0)
190            .unwrap();
191        let _ = self.date_on_sale_from.insert(dt);
192        self
193    }
194    /// End date of sale price, in the site's timezone.
195    pub fn date_on_sale_to(&mut self, year: i32, month: u32, day: u32) -> &mut Self {
196        let dt = chrono::NaiveDate::from_ymd_opt(year, month, day)
197            .unwrap()
198            .and_hms_opt(0, 0, 0)
199            .unwrap();
200        let _ = self.date_on_sale_to.insert(dt);
201        self
202    }
203    /// If the product is virtual. Default is false.
204    pub fn set_virtual(&mut self) -> &mut Self {
205        let _ = self.is_virtual.insert(true);
206        self
207    }
208    /// If the product is downloadable. Default is false.
209    pub fn downloadable(&mut self) -> &mut Self {
210        let _ = self.downloadable.insert(true);
211        self
212    }
213    /// List of downloadable files. See Product - Downloads properties
214    pub fn downloads(&mut self, file_src: impl Into<String>) -> &mut Self {
215        let f = DownloadDTO {
216            file: file_src.into(),
217        };
218        self.downloads.get_or_insert(vec![]).push(f);
219        self
220    }
221    /// Number of times downloadable files can be downloaded after purchase. Default is -1.
222    pub fn download_limit(&mut self, download_limit: i32) -> &mut Self {
223        let _ = self.download_limit.insert(download_limit);
224        self
225    }
226    /// Number of days until access to downloadable files expires. Default is -1.
227    pub fn download_expiry(&mut self, days: i32) -> &mut Self {
228        let _ = self.download_expiry.insert(days);
229        self
230    }
231    /// Product external URL. Only for external products.
232    pub fn external_url(&mut self, url: impl Into<String>) -> &mut Self {
233        let _ = self.external_url.insert(url.into());
234        self
235    }
236    /// Product external button text. Only for external products.
237    pub fn button_text(&mut self, button_text: impl Into<String>) -> &mut Self {
238        let _ = self.button_text.insert(button_text.into());
239        self
240    }
241    /// Tax status. Options: taxable, shipping and none. Default is taxable.
242    pub fn tax_status(&mut self, tax_status: TaxStatus) -> &mut Self {
243        let _ = self.tax_status.insert(tax_status);
244        self
245    }
246    /// Tax class.
247    pub fn tax_class(&mut self, tax_class: impl Into<String>) -> &mut Self {
248        let _ = self.tax_class.insert(tax_class.into());
249        self
250    }
251    /// Stock management at product level. Default is false.
252    pub fn manage_stock(&mut self) -> &mut Self {
253        let _ = self.manage_stock.insert(true);
254        self
255    }
256    /// Stock quantity.
257    pub fn stock_quantity(&mut self, stock_quantity: i32) -> &mut Self {
258        let _ = self.stock_quantity.insert(stock_quantity);
259        self
260    }
261    /// Controls the stock status of the product. Options: instock, outofstock, onbackorder. Default is instock.
262    pub fn stock_status(&mut self, stock_status: StockStatus) -> &mut Self {
263        let _ = self.stock_status.insert(stock_status);
264        self
265    }
266    /// If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no.
267    pub fn backorders(&mut self, backorders: BackordersStatus) -> &mut Self {
268        let _ = self.backorders.insert(backorders);
269        self
270    }
271    /// Allow one item to be bought in a single order. Default is false.
272    pub fn sold_individually(&mut self) -> &mut Self {
273        let _ = self.sold_individually.insert(true);
274        self
275    }
276    /// Product weight.
277    pub fn weight(&mut self, weight: impl Into<String>) -> &mut Self {
278        let _ = self.weight.insert(weight.into());
279        self
280    }
281    /// Product dimensions.
282    pub fn dimensions(
283        &mut self,
284        length: impl Into<String>,
285        width: impl Into<String>,
286        height: impl Into<String>,
287    ) -> &mut Self {
288        let d = DimensionsDTO {
289            length: length.into(),
290            width: width.into(),
291            height: height.into(),
292        };
293        let _ = self.dimensions.insert(d);
294        self
295    }
296    /// Shipping class slug.
297    pub fn shipping_class(&mut self, slug: impl Into<String>) -> &mut Self {
298        let _ = self.shipping_class.insert(slug.into());
299        self
300    }
301    /// Allow reviews. Default is true.
302    pub fn reviews_allowed_set_false(&mut self) -> &mut Self {
303        let _ = self.reviews_allowed.insert(false);
304        self
305    }
306    /// List of related products IDs.
307    pub fn related_ids(&mut self, id: i32) -> &mut Self {
308        self.related_ids.get_or_insert(vec![]).push(id);
309        self
310    }
311    /// List of up-sell products IDs.
312    pub fn upsell_ids(&mut self, id: i32) -> &mut Self {
313        self.upsell_ids.get_or_insert(vec![]).push(id);
314        self
315    }
316    /// List of cross-sell products IDs.
317    pub fn cross_sell_ids(&mut self, id: i32) -> &mut Self {
318        self.cross_sell_ids.get_or_insert(vec![]).push(id);
319        self
320    }
321    /// Product parent ID.
322    pub fn parent_id(&mut self, parent_id: i32) -> &mut Self {
323        let _ = self.parent_id.insert(parent_id);
324        self
325    }
326    /// Optional note to send the customer after purchase.
327    pub fn purchase_note(&mut self, purchase_note: impl Into<String>) -> &mut Self {
328        let _ = self.purchase_note.insert(purchase_note.into());
329        self
330    }
331    /// List of categories.
332    pub fn categories(&mut self, category_id: i32) -> &mut Self {
333        self.categories
334            .get_or_insert(vec![])
335            .push(CategoryDTO { id: category_id });
336        self
337    }
338    /// List of tags.
339    pub fn tags(&mut self, tag_id: i32) -> &mut Self {
340        self.tags.get_or_insert(vec![]).push(TagDTO { id: tag_id });
341        self
342    }
343    /// List of images.
344    pub fn images(&mut self, img_src: impl Into<String>) -> &mut Self {
345        self.images.get_or_insert(vec![]).push(ImageDTO {
346            src: img_src.into(),
347        });
348        self
349    }
350    /// List of attributes.
351    pub fn attribute(&mut self, attribute: AttributeDTO) -> &mut Self {
352        self.attributes.get_or_insert(vec![]).push(attribute);
353        self
354    }
355    /// Defaults variation attributes.
356    pub fn default_attribute(
357        &mut self,
358        id: Option<i32>,
359        name: impl Into<String>,
360        option: impl Into<String>,
361    ) -> &mut Self {
362        self.default_attributes
363            .get_or_insert(vec![])
364            .push(DefaultAttributeDTO {
365                id,
366                name: name.into(),
367                option: option.into(),
368            });
369        self
370    }
371    /// List of grouped products ID.
372    pub fn grouped_product(&mut self, grouped_product_id: i32) -> &mut Self {
373        self.grouped_products
374            .get_or_insert(vec![])
375            .push(grouped_product_id);
376        self
377    }
378    /// Menu order, used to custom sort products.
379    pub fn menu_order(&mut self, menu_order: i32) -> &mut Self {
380        let _ = self.menu_order.insert(menu_order);
381        self
382    }
383    /// Meta data.
384    pub fn meta_data(&mut self, key: impl Into<String>, value: impl Serialize) -> &mut Self {
385        self.meta_data.get_or_insert(vec![]).push(MetaData {
386            id: None,
387            key: key.into(),
388            value: serde_json::json!(value),
389        });
390        self
391    }
392    pub fn build(&self) -> ProductModify {
393        ProductModify {
394            id: self.id,
395            name: self.name.clone(),
396            slug: self.slug.clone(),
397            permalink: self.permalink.clone(),
398            product_type: self.product_type.clone(),
399            status: self.status.clone(),
400            featured: self.featured,
401            catalog_visibility: self.catalog_visibility.clone(),
402            description: self.description.clone(),
403            short_description: self.short_description.clone(),
404            sku: self.sku.clone(),
405            regular_price: self.regular_price.clone(),
406            sale_price: self.sale_price.clone(),
407            date_on_sale_from: self.date_on_sale_from,
408            date_on_sale_to: self.date_on_sale_to,
409            is_virtual: self.is_virtual,
410            downloadable: self.downloadable,
411            downloads: self.downloads.clone(),
412            download_limit: self.download_limit,
413            download_expiry: self.download_expiry,
414            external_url: self.external_url.clone(),
415            button_text: self.button_text.clone(),
416            tax_status: self.tax_status.clone(),
417            tax_class: self.tax_class.clone(),
418            manage_stock: self.manage_stock,
419            stock_quantity: self.stock_quantity,
420            stock_status: self.stock_status.clone(),
421            backorders: self.backorders.clone(),
422            sold_individually: self.sold_individually,
423            weight: self.weight.clone(),
424            dimensions: self.dimensions.clone(),
425            shipping_class: self.shipping_class.clone(),
426            reviews_allowed: self.reviews_allowed,
427            related_ids: self.related_ids.clone(),
428            upsell_ids: self.upsell_ids.clone(),
429            cross_sell_ids: self.cross_sell_ids.clone(),
430            parent_id: self.parent_id,
431            purchase_note: self.purchase_note.clone(),
432            categories: self.categories.clone(),
433            tags: self.tags.clone(),
434            images: self.images.clone(),
435            attributes: self.attributes.clone(),
436            default_attributes: self.default_attributes.clone(),
437            grouped_products: self.grouped_products.clone(),
438            menu_order: self.menu_order,
439            meta_data: self.meta_data.clone(),
440        }
441    }
442}
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct DownloadDTO {
445    // pub id: String,
446    // pub name: String,
447    pub file: String,
448}
449#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct DimensionsDTO {
451    /// Product length.
452    pub length: String,
453    /// Product width.
454    pub width: String,
455    /// Product height.
456    pub height: String,
457}
458#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct CategoryDTO {
460    pub id: i32,
461}
462#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct TagDTO {
464    pub id: i32,
465}
466#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct ImageDTO {
468    pub src: String,
469    // pub name: String,
470    // pub alt: String,
471}
472#[skip_serializing_none]
473#[derive(Debug, Clone, Serialize, Deserialize, Default)]
474pub struct AttributeDTO {
475    id: Option<i32>,
476    name: String,
477    position: Option<i32>,
478    visible: bool,
479    variation: bool,
480    options: Vec<String>,
481}
482impl AttributeDTO {
483    pub fn builder() -> AttributeDTOBuilder<NoName, NoOptions> {
484        AttributeDTOBuilder::<NoName, NoOptions>::default()
485    }
486}
487#[derive(Default)]
488pub struct WithName(String);
489#[derive(Default)]
490pub struct NoName;
491#[derive(Default)]
492pub struct Options(Vec<String>);
493#[derive(Default)]
494pub struct NoOptions;
495#[derive(Default)]
496pub struct AttributeDTOBuilder<N, O> {
497    id: Option<i32>,
498    name: N,
499    position: Option<i32>,
500    visible: Option<bool>,
501    variation: Option<bool>,
502    options: O,
503}
504impl<N, O> AttributeDTOBuilder<N, O> {
505    /// Attribute ID.
506    pub fn id(mut self, id: i32) -> Self {
507        let _ = self.id.insert(id);
508        self
509    }
510    /// Attribute name.
511    pub fn name(self, name: impl Into<String>) -> AttributeDTOBuilder<WithName, O> {
512        AttributeDTOBuilder {
513            id: self.id,
514            name: WithName(name.into()),
515            position: self.position,
516            visible: self.visible,
517            variation: self.variation,
518            options: self.options,
519        }
520    }
521    /// Attribute position.
522    pub fn position(mut self, position: i32) -> Self {
523        let _ = self.position.insert(position);
524        self
525    }
526    /// Define if the attribute is visible on the "Additional information" tab in the product's page. Default is false.
527    pub fn visible(mut self) -> Self {
528        let _ = self.visible.insert(true);
529        self
530    }
531    /// Define if the attribute can be used as variation. Default is false.
532    pub fn variation(mut self) -> Self {
533        let _ = self.variation.insert(true);
534        self
535    }
536
537    /// List of available term names of the attribute.
538    pub fn options(self, options: Vec<String>) -> AttributeDTOBuilder<N, Options> {
539        AttributeDTOBuilder {
540            id: self.id,
541            name: self.name,
542            position: self.position,
543            visible: self.visible,
544            variation: self.variation,
545            options: Options(options),
546        }
547    }
548}
549impl<N> AttributeDTOBuilder<N, Options> {
550    pub fn option(mut self, option: impl Into<String>) -> Self {
551        self.options.0.push(option.into());
552        self
553    }
554}
555impl<N> AttributeDTOBuilder<N, NoOptions> {
556    /// List of available term names of the attribute.
557    pub fn option(self, option: impl Into<String>) -> AttributeDTOBuilder<N, Options> {
558        AttributeDTOBuilder {
559            id: self.id,
560            name: self.name,
561            position: self.position,
562            visible: self.visible,
563            variation: self.variation,
564            options: Options(vec![option.into()]),
565        }
566    }
567}
568impl AttributeDTOBuilder<WithName, Options> {
569    pub fn build(self) -> AttributeDTO {
570        AttributeDTO {
571            id: self.id,
572            name: self.name.0,
573            position: self.position,
574            visible: self.visible.unwrap_or(true),
575            variation: self.variation.unwrap_or_default(),
576            options: self.options.0,
577        }
578    }
579}
580#[derive(Debug, Clone, Serialize, Deserialize)]
581#[skip_serializing_none]
582pub struct DefaultAttributeDTO {
583    #[serde(skip_serializing_if = "Option::is_none")]
584    pub id: Option<i32>,
585    pub name: String,
586    pub option: String,
587}