Skip to main content

brickset/v3/
request.rs

1//! Request builders.
2
3use super::util::{self, Flag};
4use chrono::NaiveDate;
5use lazy_static::lazy_static;
6use serde::{Deserialize, Serialize};
7use serde_json;
8use url::Url;
9
10#[cfg(feature = "log")]
11use log::warn;
12
13lazy_static! {
14    pub static ref ENDPOINT: url::Url = Url::parse("https://brickset.com/api/v3.asmx/").unwrap();
15}
16
17#[derive(Debug)]
18pub enum Error {
19    UrlParseError(url::ParseError),
20    SerdeJson(serde_json::Error),
21    Message(String),
22    #[cfg(feature = "reqwest")]
23    Reqwest(reqwest::Error),
24}
25
26#[derive(Debug, Clone)]
27pub struct CheckKey<'s> {
28    api_key: &'s str,
29}
30
31#[derive(Debug, Clone)]
32pub struct Login<'s> {
33    api_key: &'s str,
34    username: &'s str,
35    password: &'s str,
36}
37
38#[derive(Debug, Clone)]
39pub struct CheckUserHash<'s> {
40    api_key: &'s str,
41    user_hash: &'s str,
42}
43
44#[derive(Debug, Clone)]
45pub struct GetKeyUsageStats<'s> {
46    api_key: &'s str,
47}
48
49#[derive(Debug, Clone)]
50pub struct GetSets<'s> {
51    api_key: &'s str,
52    user_hash: Option<&'s str>,
53    params: GetSetsParameters<'s>,
54}
55
56#[derive(Debug, Clone)]
57pub struct GetAdditionalImages<'s> {
58    api_key: &'s str,
59    set_id: u64,
60}
61
62#[derive(Debug, Clone)]
63pub struct GetInstructions<'s> {
64    api_key: &'s str,
65    set_id: u64,
66}
67
68#[derive(Debug, Clone)]
69pub struct GetInstructions2<'s> {
70    api_key: &'s str,
71    set_number: &'s str,
72}
73
74#[derive(Debug, Clone)]
75pub struct GetReviews<'s> {
76    api_key: &'s str,
77    set_id: u64,
78}
79
80#[derive(Debug, Clone)]
81pub struct GetThemes<'s> {
82    api_key: &'s str,
83}
84
85#[derive(Debug, Clone)]
86pub struct GetSubthemes<'s> {
87    api_key: &'s str,
88    theme: &'s str,
89}
90
91#[derive(Debug, Clone)]
92pub struct GetYears<'s> {
93    api_key: &'s str,
94    theme: &'s str,
95}
96
97#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
98pub enum OrderBy {
99    Number,
100    YearFrom,
101    Pieces,
102    Minifigs,
103    Rating,
104    USRetailPrice,
105    UKRetailPrice,
106    CARetailPrice,
107    DERetailPrice,
108    FRRetailPrice,
109    USPricePerPiece,
110    UKPricePerPiece,
111    CAPricePerPiece,
112    DEPricePerPiece,
113    FRPricePerPiece,
114    Theme,
115    Subtheme,
116    Name,
117    Random,
118    QtyOwned,
119    OwnCount,
120    WantCount,
121    UserRating,
122    CollectionID,
123    NumberDESC,
124    YearFromDESC,
125    PiecesDESC,
126    MinifigsDESC,
127    RatingDESC,
128    USRetailPriceDESC,
129    UKRetailPriceDESC,
130    CARetailPriceDESC,
131    DERetailPriceDESC,
132    FRRetailPriceDESC,
133    USPricePerPieceDESC,
134    UKPricePerPieceDESC,
135    CAPricePerPieceDESC,
136    DEPricePerPieceDESC,
137    FRPricePerPieceDESC,
138    ThemeDESC,
139    SubthemeDESC,
140    NameDESC,
141    RandomDESC,
142    QtyOwnedDESC,
143    OwnCountDESC,
144    WantCountDESC,
145    UserRatingDESC,
146    CollectionIDDESC,
147}
148
149#[derive(Serialize, Deserialize, Default, Debug, Clone)]
150#[serde(rename_all = "camelCase")]
151pub struct GetSetsParameters<'s> {
152    #[serde(skip_serializing_if = "Option::is_none")]
153    #[serde(default)]
154    set_id: Option<u64>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    #[serde(default)]
157    query: Option<&'s str>,
158    #[serde(skip_serializing_if = "Option::is_none")]
159    #[serde(default)]
160    theme: Option<&'s str>,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    #[serde(default)]
163    subtheme: Option<&'s str>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    #[serde(default)]
166    #[serde(rename = "setNumber")]
167    full_set_number: Option<&'s str>,
168    #[serde(skip_serializing_if = "Vec::is_empty")]
169    #[serde(with = "util::int_vec_as_commastr")]
170    year: Vec<i32>,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    #[serde(default)]
173    tag: Option<&'s str>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    #[serde(default)]
176    owned: Option<Flag>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    #[serde(default)]
179    wanted: Option<Flag>,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    #[serde(with = "util::updated_since_format")]
182    #[serde(default)]
183    updated_since: Option<NaiveDate>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    #[serde(default)]
186    order_by: Option<OrderBy>,
187    #[serde(skip_serializing_if = "Option::is_none")]
188    #[serde(default)]
189    page_size: Option<usize>,
190    #[serde(skip_serializing_if = "Option::is_none")]
191    #[serde(default)]
192    page_number: Option<usize>,
193    #[serde(skip_serializing_if = "Option::is_none")]
194    #[serde(default)]
195    extended_data: Option<Flag>,
196}
197
198#[derive(Debug, Clone)]
199pub struct SetCollection<'s> {
200    api_key: &'s str,
201    user_hash: &'s str,
202    set_id: u64,
203    params: SetCollectionParameters<'s>,
204}
205
206#[derive(Serialize, Deserialize, Default, Debug, Clone)]
207#[serde(rename_all = "camelCase")]
208pub struct SetCollectionParameters<'s> {
209    #[serde(skip_serializing_if = "Option::is_none")]
210    own: Option<usize>,
211    #[serde(skip_serializing_if = "Option::is_none")]
212    want: Option<usize>,
213    #[serde(skip_serializing_if = "Option::is_none")]
214    qty_owned: Option<usize>,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    notes: Option<&'s str>,
217    #[serde(skip_serializing_if = "Option::is_none")]
218    rating: Option<i32>,
219}
220
221#[derive(Debug, Clone)]
222pub struct GetUserNotes<'s> {
223    api_key: &'s str,
224    user_hash: &'s str,
225}
226
227#[derive(Debug, Clone)]
228pub struct GetMinifigCollection<'s> {
229    api_key: &'s str,
230    user_hash: &'s str,
231    params: GetMinifigCollectionParameters<'s>,
232}
233
234#[derive(Serialize, Deserialize, Default, Debug, Clone)]
235#[serde(rename_all = "camelCase")]
236pub struct GetMinifigCollectionParameters<'s> {
237    #[serde(skip_serializing_if = "Option::is_none")]
238    owned: Option<Flag>,
239    #[serde(skip_serializing_if = "Option::is_none")]
240    wanted: Option<Flag>,
241    #[serde(skip_serializing_if = "Option::is_none")]
242    query: Option<&'s str>,
243}
244
245#[derive(Debug, Clone)]
246pub struct SetMinifigCollection<'s> {
247    api_key: &'s str,
248    user_hash: &'s str,
249    minifig_number: &'s str,
250    params: SetMinifigCollectionParameters<'s>,
251}
252
253#[derive(Serialize, Deserialize, Default, Debug, Clone)]
254#[serde(rename_all = "camelCase")]
255pub struct SetMinifigCollectionParameters<'s> {
256    #[serde(skip_serializing_if = "Option::is_none")]
257    own: Option<usize>,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    want: Option<usize>,
260    #[serde(skip_serializing_if = "Option::is_none")]
261    qty_owned: Option<usize>,
262    #[serde(skip_serializing_if = "Option::is_none")]
263    notes: Option<&'s str>,
264}
265
266#[derive(Debug, Clone)]
267pub struct GetUserMinifigNotes<'s> {
268    api_key: &'s str,
269    user_hash: &'s str
270}
271
272impl<'s> CheckKey<'s> {
273    pub fn new(api_key: &'s str) -> Self {
274        CheckKey { api_key }
275    }
276}
277
278impl<'s> Login<'s> {
279    pub fn new(api_key: &'s str, username: &'s str, password: &'s str) -> Self {
280        Login {
281            api_key,
282            username,
283            password,
284        }
285    }
286}
287
288impl<'s> CheckUserHash<'s> {
289    pub fn new(api_key: &'s str, user_hash: &'s str) -> Self {
290        CheckUserHash { api_key, user_hash }
291    }
292}
293
294impl<'s> GetKeyUsageStats<'s> {
295    pub fn new(api_key: &'s str) -> Self {
296        GetKeyUsageStats { api_key }
297    }
298}
299
300impl<'s> GetSets<'s> {
301    pub fn new(
302        api_key: &'s str,
303        user_hash: Option<&'s str>,
304        params: GetSetsParameters<'s>,
305    ) -> Self {
306        Self {
307            api_key,
308            user_hash,
309            params,
310        }
311    }
312}
313
314impl<'s> GetAdditionalImages<'s> {
315    pub fn new(api_key: &'s str, set_id: u64) -> Self {
316        Self { api_key, set_id }
317    }
318}
319
320impl<'s> GetInstructions<'s> {
321    pub fn new(api_key: &'s str, set_id: u64) -> Self {
322        Self { api_key, set_id }
323    }
324}
325
326impl<'s> GetInstructions2<'s> {
327    pub fn new(api_key: &'s str, set_number: &'s str) -> Self {
328        Self {
329            api_key,
330            set_number,
331        }
332    }
333}
334
335impl<'s> GetReviews<'s> {
336    pub fn new(api_key: &'s str, set_id: u64) -> Self {
337        Self { api_key, set_id }
338    }
339}
340
341impl<'s> GetThemes<'s> {
342    pub fn new(api_key: &'s str) -> Self {
343        Self { api_key }
344    }
345}
346
347impl<'s> GetSubthemes<'s> {
348    pub fn new(api_key: &'s str, theme: &'s str) -> Self {
349        Self { api_key, theme }
350    }
351}
352
353impl<'s> GetYears<'s> {
354    pub fn new(api_key: &'s str, theme: &'s str) -> Self {
355        Self { api_key, theme }
356    }
357}
358
359impl<'s> SetCollection<'s> {
360    pub fn new(
361        api_key: &'s str,
362        user_hash: &'s str,
363        set_id: u64,
364        params: SetCollectionParameters<'s>,
365    ) -> Self {
366        Self {
367            api_key,
368            user_hash,
369            set_id,
370            params,
371        }
372    }
373}
374
375impl<'s> GetSetsParameters<'s> {
376    #[inline]
377    pub fn new() -> Self {
378        Self::default()
379    }
380
381    /// Filter results to show only a single set ID.
382    #[inline]
383    pub fn set_id(mut self, set_id: u64) -> Self {
384        self.set_id = Some(set_id);
385        self
386    }
387
388    /// Filter results to show sets from a single year.
389    #[inline]
390    pub fn year(mut self, year: i32) -> Self {
391        self.year.clear();
392        self.year.push(year);
393        self
394    }
395
396    /// Filter results to show sets from multiple years.
397    #[inline]
398    pub fn years(mut self, year: Vec<i32>) -> Self {
399        self.year = year;
400        self
401    }
402
403    /// Query text. Searches set number, name, theme, and subtheme.
404    #[inline]
405    pub fn query(mut self, query: &'s str) -> Self {
406        self.query = Some(query);
407        self
408    }
409
410    /// Filter results to show sets from a single theme.
411    #[inline]
412    pub fn theme(mut self, theme: &'s str) -> Self {
413        self.theme = Some(theme);
414        self
415    }
416
417    /// Filter results to show sets from a single sub-theme.
418    #[inline]
419    pub fn subtheme(mut self, subtheme: &'s str) -> Self {
420        self.subtheme = Some(subtheme);
421        self
422    }
423
424    /// Full set number, including the variant number. ex: "6876-1"
425    #[inline]
426    pub fn full_set_number(mut self, full_set_number: &'s str) -> Self {
427        self.full_set_number = Some(full_set_number);
428        self
429    }
430
431    /// Filter results to show sets with a particular tag.
432    #[inline]
433    pub fn tag(mut self, tag: &'s str) -> Self {
434        self.tag = Some(tag);
435        self
436    }
437
438    /// Only show sets owned by the user.
439    #[inline]
440    pub fn owned_by_user(mut self, owned: bool) -> Self {
441        self.owned = if owned { Some(Flag) } else { None };
442        self
443    }
444
445    /// Only show sets wanted by the user.
446    #[inline]
447    pub fn wanted_by_user(mut self, wanted: bool) -> Self {
448        self.wanted = if wanted { Some(Flag) } else { None };
449        self
450    }
451
452    /// Retrieve extended information about sets, including tags, description, and notes.
453    #[inline]
454    pub fn extended_data(mut self, extended_data: bool) -> Self {
455        self.extended_data = if extended_data { Some(Flag) } else { None };
456        self
457    }
458
459    /// Filter results to only show sets updated since the given date.
460    #[inline]
461    pub fn updated_since<D: Into<NaiveDate>>(mut self, date: D) -> Self {
462        self.updated_since = Some(date.into());
463        self
464    }
465
466    /// Sort the results.
467    #[inline]
468    pub fn order_by(mut self, order_by: OrderBy) -> Self {
469        self.order_by = Some(order_by);
470        self
471    }
472
473    /// Specify the number of sets to retrieve. Maximum = 500, default = 20.
474    #[inline]
475    pub fn page_size(mut self, page_size: usize) -> Self {
476        if page_size > 500 {
477            #[cfg(feature = "log")]
478            warn!("Given page_size was {page_size}, but the maximum is 500");
479        } else if page_size == 0 {
480            #[cfg(feature = "log")]
481            warn!("Zero page size is not valid");
482        }
483
484        self.page_size = Some(page_size);
485        self
486    }
487
488    /// Specify which page of sets to retrieve. Should be used in conjunction with 
489    /// [`Self::page_size`]. Default = 1
490    #[inline]
491    pub fn page_number(mut self, page_number: usize) -> Self {
492        self.page_number = Some(page_number);
493        self
494    }
495}
496
497impl<'s> SetCollectionParameters<'s> {
498    /// new, empty SetCollectionParameters. These parameters will not alter BrickSet's database.
499    /// To alter a database entry, use these functions:
500    /// - [`Self::owned`]
501    /// - [`Self::wanted`]
502    /// - [`Self::notes`]
503    /// - [`Self::rating`]
504    pub fn new() -> Self {
505        Self::default()
506    }
507
508    /// Add or remove this set from the list of sets owned by the user. The user owns `qty_owned`
509    /// of this set. Remove the set from the user's owned list by setting `qty_owned` to zero.
510    pub fn owned(mut self, qty_owned: usize) -> Self {
511        if qty_owned == 0 {
512            self.own = None;
513        } else {
514            self.own = Some(1);
515        }
516        self.qty_owned = Some(qty_owned);
517        self
518    }
519
520    /// Add or remove this set from the user's wanted list.
521    pub fn wanted(mut self, wanted: bool) -> Self {
522        if wanted {
523            self.want = Some(1);
524        } else {
525            self.want = Some(0);
526        }
527        self
528    }
529
530    /// Alter the user's notes for the set.
531    pub fn notes(mut self, notes: &'s str) -> Self {
532        self.notes = Some(notes);
533        self
534    }
535
536    /// Alter the user's rating of the set.
537    pub fn rating(mut self, rating: i32) -> Self {
538        self.rating = Some(rating);
539        self
540    }
541}
542
543impl<'s> GetUserNotes<'s> {
544    pub fn new(api_key: &'s str, user_hash: &'s str) -> Self {
545        Self { api_key, user_hash }
546    }
547}
548
549impl<'s> GetMinifigCollection<'s> {
550    pub fn new(api_key: &'s str, user_hash: &'s str, params: GetMinifigCollectionParameters<'s>) -> Self {
551        Self { api_key, user_hash, params }
552    }
553}
554
555impl<'s> GetMinifigCollectionParameters<'s> {
556    /// Get minifigs owned by the user.
557    pub fn owned() -> Self {
558        Self { owned: Some(Flag), ..Default::default() }
559    }
560
561    /// Get minifigs wanted by the user.
562    pub fn wanted() -> Self {
563        Self { wanted: Some(Flag), ..Default::default() }
564    }
565
566    /// Add query text to the request. The query will search by minifig ID number, and name.
567    pub fn query(mut self, query: &'s str) -> Self {
568        self.query = Some(query);
569        self
570    }
571}
572
573impl<'s> SetMinifigCollection<'s> {
574    pub fn new(api_key: &'s str, user_hash: &'s str, minifig_number: &'s str, params: SetMinifigCollectionParameters<'s>) -> Self {
575        Self { api_key, user_hash, minifig_number, params }
576    }
577}
578
579impl<'s> SetMinifigCollectionParameters<'s> {
580    /// new, empty SetMinifigCollectionParameters. These parameters will not alter BrickSet's database.
581    /// /// To alter a database entry, use these functions:
582    /// - [`Self::owned`]
583    /// - [`Self::wanted`]
584    /// - [`Self::notes`]
585    pub fn new() -> Self {
586        Self::default()
587    }
588
589    /// Add or remove this minifig from the list of minifigs owned by the user. The user owns `qty_owned`
590    /// of this minifig. Remove the minifig from the user's owned list by setting `qty_owned` to zero.
591    pub fn owned(mut self, qty_owned: usize) -> Self {
592        if qty_owned == 0 {
593            self.own = Some(0);
594            self.qty_owned = None;
595        } else {
596            self.own = None;
597            self.qty_owned = Some(qty_owned);
598        }
599        self
600    }
601
602    /// Add or remove this minifig from the user's wanted list.
603    pub fn wanted(mut self, wanted: bool) -> Self {
604        if wanted {
605            self.want = Some(1);
606        } else {
607            self.want = Some(0);
608        }
609        self
610    }
611
612    /// Alter the user's notes for the minifig.
613    pub fn notes(mut self, notes: &'s str) -> Self {
614        self.notes = Some(notes);
615        self
616    }
617}
618
619impl<'s> GetUserMinifigNotes<'s> {
620    pub fn new(api_key: &'s str, user_hash: &'s str) -> Self {
621        Self { api_key, user_hash }
622    }
623}
624
625impl OrderBy {
626    /// Get the reversed version of an [`OrderBy`]. For example,
627    /// `OrderBy::Number.reversed()` will return `OrderBy::NumberDESC`,
628    /// and vice versa.
629    pub fn reversed(self) -> Self {
630        match self {
631            OrderBy::Number => OrderBy::NumberDESC,
632            OrderBy::YearFrom => OrderBy::YearFromDESC,
633            OrderBy::Pieces => OrderBy::PiecesDESC,
634            OrderBy::Minifigs => OrderBy::MinifigsDESC,
635            OrderBy::Rating => OrderBy::RatingDESC,
636            OrderBy::USRetailPrice => OrderBy::USRetailPriceDESC,
637            OrderBy::UKRetailPrice => OrderBy::UKRetailPriceDESC,
638            OrderBy::CARetailPrice => OrderBy::CARetailPriceDESC,
639            OrderBy::DERetailPrice => OrderBy::DERetailPriceDESC,
640            OrderBy::FRRetailPrice => OrderBy::FRRetailPriceDESC,
641            OrderBy::USPricePerPiece => OrderBy::USPricePerPieceDESC,
642            OrderBy::UKPricePerPiece => OrderBy::UKPricePerPieceDESC,
643            OrderBy::CAPricePerPiece => OrderBy::CAPricePerPieceDESC,
644            OrderBy::DEPricePerPiece => OrderBy::DEPricePerPieceDESC,
645            OrderBy::FRPricePerPiece => OrderBy::FRPricePerPieceDESC,
646            OrderBy::Theme => OrderBy::ThemeDESC,
647            OrderBy::Subtheme => OrderBy::SubthemeDESC,
648            OrderBy::Name => OrderBy::NameDESC,
649            OrderBy::Random => OrderBy::RandomDESC,
650            OrderBy::QtyOwned => OrderBy::QtyOwnedDESC,
651            OrderBy::OwnCount => OrderBy::OwnCountDESC,
652            OrderBy::WantCount => OrderBy::WantCountDESC,
653            OrderBy::UserRating => OrderBy::UserRatingDESC,
654            OrderBy::CollectionID => OrderBy::CollectionIDDESC,
655            OrderBy::NumberDESC => OrderBy::Number,
656            OrderBy::YearFromDESC => OrderBy::YearFrom,
657            OrderBy::PiecesDESC => OrderBy::Pieces,
658            OrderBy::MinifigsDESC => OrderBy::Minifigs,
659            OrderBy::RatingDESC => OrderBy::Rating,
660            OrderBy::USRetailPriceDESC => OrderBy::USRetailPrice,
661            OrderBy::UKRetailPriceDESC => OrderBy::UKRetailPrice,
662            OrderBy::CARetailPriceDESC => OrderBy::CARetailPrice,
663            OrderBy::DERetailPriceDESC => OrderBy::DERetailPrice,
664            OrderBy::FRRetailPriceDESC => OrderBy::FRRetailPrice,
665            OrderBy::USPricePerPieceDESC => OrderBy::USPricePerPiece,
666            OrderBy::UKPricePerPieceDESC => OrderBy::UKPricePerPiece,
667            OrderBy::CAPricePerPieceDESC => OrderBy::CAPricePerPiece,
668            OrderBy::DEPricePerPieceDESC => OrderBy::DEPricePerPiece,
669            OrderBy::FRPricePerPieceDESC => OrderBy::FRPricePerPiece,
670            OrderBy::ThemeDESC => OrderBy::Theme,
671            OrderBy::SubthemeDESC => OrderBy::Subtheme,
672            OrderBy::NameDESC => OrderBy::Name,
673            OrderBy::RandomDESC => OrderBy::Random,
674            OrderBy::QtyOwnedDESC => OrderBy::QtyOwned,
675            OrderBy::OwnCountDESC => OrderBy::OwnCount,
676            OrderBy::WantCountDESC => OrderBy::WantCount,
677            OrderBy::UserRatingDESC => OrderBy::UserRating,
678            OrderBy::CollectionIDDESC => OrderBy::CollectionID,
679        }
680    }
681}
682
683/// Implemented by any type that can be turned into a BrickSet API request.
684///
685/// - [`BricksetRequest::to_request_url`] creates a URL containing the request method and query parameters.
686/// - [`BricksetRequest::to_reqwest`] creates a POST [`reqwest::Request`] with the query paramters url-encoded in the body.
687pub trait BricksetRequest {
688    /// Encode method parameters via a URL serializer.
689    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
690    where
691        T: url::form_urlencoded::Target;
692
693    /// The request's method name.
694    fn method_name(&self) -> &'static str;
695
696    /// Create a URL representing the request. All request parameters will appear in the URL.
697    /// 
698    /// NOTE: It is better practice to only put the method name in the request URL, and use
699    /// [`Self::encode_query`] to put the parameters in the request's body.
700    fn to_request_url(&self) -> Result<Url, Error> {
701        let mut result = ENDPOINT.join(self.method_name())?;
702        self.encode_query(&mut result.query_pairs_mut())?;
703        Ok(result)
704    }
705
706    /// Build a [`reqwest::Request`] from `self`. The resulting [`reqwest::Request`] will
707    /// always be a POST request with all method parameters encoded into the body, using
708    /// content type `application/x-www-form-urlencoded`.
709    #[cfg(feature = "reqwest")]
710    fn to_reqwest(&self, client: &reqwest::Client) -> Result<reqwest::Request, Error> {
711        let url = ENDPOINT.join(self.method_name())?;
712
713        let mut body = url::form_urlencoded::Serializer::new(String::new());
714
715        self.encode_query(&mut body)?;
716
717        let body = body.finish();
718
719        Ok(client
720            .post(url)
721            .header(
722                reqwest::header::CONTENT_TYPE,
723                "application/x-www-form-urlencoded",
724            )
725            .header(reqwest::header::CONTENT_LENGTH, body.as_bytes().len())
726            .body(body)
727            .build()?)
728    }
729}
730
731impl<'s> BricksetRequest for CheckKey<'s> {
732    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
733    where
734        T: url::form_urlencoded::Target,
735    {
736        query.append_pair("apiKey", self.api_key);
737        Ok(())
738    }
739
740    fn method_name(&self) -> &'static str {
741        "checkKey"
742    }
743}
744
745impl<'s> BricksetRequest for Login<'s> {
746    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
747    where
748        T: url::form_urlencoded::Target,
749    {
750        query
751            .append_pair("apiKey", self.api_key)
752            .append_pair("username", self.username)
753            .append_pair("password", self.password);
754        Ok(())
755    }
756
757    fn method_name(&self) -> &'static str {
758        "login"
759    }
760}
761
762impl<'s> BricksetRequest for CheckUserHash<'s> {
763    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
764    where
765        T: url::form_urlencoded::Target,
766    {
767        query
768            .append_pair("apiKey", self.api_key)
769            .append_pair("userHash", self.user_hash);
770        Ok(())
771    }
772
773    fn method_name(&self) -> &'static str {
774        "checkUserHash"
775    }
776}
777
778impl<'s> BricksetRequest for GetKeyUsageStats<'s> {
779    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
780    where
781        T: url::form_urlencoded::Target,
782    {
783        query.append_pair("apiKey", self.api_key);
784        Ok(())
785    }
786
787    fn method_name(&self) -> &'static str {
788        "getKeyUsageStats"
789    }
790}
791
792impl<'s> BricksetRequest for GetSets<'s> {
793    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
794    where
795        T: url::form_urlencoded::Target,
796    {
797        let params = serde_json::to_string(&self.params)?;
798
799        if (self.params.wanted.is_some() || self.params.owned.is_some()) && self.user_hash.is_none() {
800            #[cfg(feature = "log")]
801            warn!("User hash is required when wanted/owned parameters are used in GetSets");
802        }
803
804        query
805            .append_pair("apiKey", self.api_key)
806            .append_pair("params", &params)
807            .append_pair("userHash", self.user_hash.unwrap_or(""));
808        Ok(())
809    }
810
811    fn method_name(&self) -> &'static str {
812        "getSets"
813    }
814}
815
816impl<'s> BricksetRequest for GetAdditionalImages<'s> {
817    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
818    where
819        T: url::form_urlencoded::Target,
820    {
821        query
822            .append_pair("apiKey", self.api_key)
823            .append_pair("setID", self.set_id.to_string().as_str());
824        Ok(())
825    }
826
827    fn method_name(&self) -> &'static str {
828        "getAdditionalImages"
829    }
830}
831
832impl<'s> BricksetRequest for GetInstructions<'s> {
833    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
834    where
835        T: url::form_urlencoded::Target,
836    {
837        query
838            .append_pair("apiKey", self.api_key)
839            .append_pair("setID", self.set_id.to_string().as_str());
840        Ok(())
841    }
842
843    fn method_name(&self) -> &'static str {
844        "getInstructions"
845    }
846}
847
848impl<'s> BricksetRequest for GetInstructions2<'s> {
849    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
850    where
851        T: url::form_urlencoded::Target,
852    {
853        query
854            .append_pair("apiKey", self.api_key)
855            .append_pair("setNumber", self.set_number);
856        Ok(())
857    }
858
859    fn method_name(&self) -> &'static str {
860        "getInstructions2"
861    }
862}
863
864impl<'s> BricksetRequest for GetReviews<'s> {
865    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
866    where
867        T: url::form_urlencoded::Target,
868    {
869        query
870            .append_pair("apiKey", self.api_key)
871            .append_pair("setID", self.set_id.to_string().as_str());
872        Ok(())
873    }
874
875    fn method_name(&self) -> &'static str {
876        "getReviews"
877    }
878}
879
880impl<'s> BricksetRequest for GetThemes<'s> {
881    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
882    where
883        T: url::form_urlencoded::Target,
884    {
885        query.append_pair("apiKey", self.api_key);
886        Ok(())
887    }
888
889    fn method_name(&self) -> &'static str {
890        "getThemes"
891    }
892}
893
894impl<'s> BricksetRequest for GetSubthemes<'s> {
895    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
896    where
897        T: url::form_urlencoded::Target,
898    {
899        query
900            .append_pair("apiKey", self.api_key)
901            .append_pair("theme", self.theme);
902        Ok(())
903    }
904
905    fn method_name(&self) -> &'static str {
906        "getSubthemes"
907    }
908}
909
910impl<'s> BricksetRequest for GetYears<'s> {
911    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
912    where
913        T: url::form_urlencoded::Target,
914    {
915        query
916            .append_pair("apiKey", self.api_key)
917            .append_pair("theme", self.theme);
918        Ok(())
919    }
920
921    fn method_name(&self) -> &'static str {
922        "getYears"
923    }
924}
925
926impl<'s> BricksetRequest for SetCollection<'s> {
927    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
928    where
929        T: url::form_urlencoded::Target,
930    {
931        let params = serde_json::to_string(&self.params)?;
932
933        query
934            .append_pair("apiKey", self.api_key)
935            .append_pair("userHash", self.user_hash)
936            .append_pair("setID", self.set_id.to_string().as_str())
937            .append_pair("params", &params);
938        Ok(())
939    }
940
941    fn method_name(&self) -> &'static str {
942        "setCollection"
943    }
944}
945
946impl<'s> BricksetRequest for GetUserNotes<'s> {
947    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
948    where
949        T: url::form_urlencoded::Target,
950    {
951        query
952            .append_pair("apiKey", self.api_key)
953            .append_pair("userHash", self.user_hash);
954        Ok(())
955    }
956
957    fn method_name(&self) -> &'static str {
958        "getUserNotes"
959    }
960}
961
962impl<'s> BricksetRequest for GetMinifigCollection<'s> {
963    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
964    where
965        T: url::form_urlencoded::Target,
966    {
967        let params = serde_json::to_string(&self.params)?;
968
969        query
970            .append_pair("apiKey", self.api_key)
971            .append_pair("userHash", self.user_hash)
972            .append_pair("params", params.as_str());
973        Ok(())
974    }
975
976    fn method_name(&self) -> &'static str {
977        "getMinifigCollection"
978    }
979}
980
981impl<'s> BricksetRequest for SetMinifigCollection<'s> {
982    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
983    where
984        T: url::form_urlencoded::Target,
985    {
986        let params = serde_json::to_string(&self.params)?;
987
988        query
989            .append_pair("apiKey", self.api_key)
990            .append_pair("userHash", self.user_hash)
991            .append_pair("minifigNumber", self.minifig_number.to_string().as_str())
992            .append_pair("params", params.as_str());
993        Ok(())
994    }
995
996    fn method_name(&self) -> &'static str {
997        "setMinifigCollection"
998    }
999}
1000
1001impl<'s> BricksetRequest for GetUserMinifigNotes<'s> {
1002    fn encode_query<T>(&self, query: &mut url::form_urlencoded::Serializer<T>) -> Result<(), Error>
1003    where
1004        T: url::form_urlencoded::Target {
1005        query
1006            .append_pair("apiKey", self.api_key)
1007            .append_pair("userHash", self.user_hash);
1008        Ok(())
1009    }
1010
1011    fn method_name(&self) -> &'static str {
1012        "getUserMinifigNotes"
1013    }
1014}
1015
1016impl From<url::ParseError> for Error {
1017    fn from(value: url::ParseError) -> Self {
1018        Self::UrlParseError(value)
1019    }
1020}
1021
1022impl From<serde_json::Error> for Error {
1023    fn from(value: serde_json::Error) -> Self {
1024        Self::SerdeJson(value)
1025    }
1026}
1027
1028#[cfg(feature = "reqwest")]
1029impl From<reqwest::Error> for Error {
1030    fn from(value: reqwest::Error) -> Self {
1031        Self::Reqwest(value)
1032    }
1033}
1034
1035impl std::fmt::Display for Error {
1036    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1037        match self {
1038            Error::UrlParseError(err) => err.fmt(f),
1039            Error::Message(err) => f.write_str(&err),
1040            Error::SerdeJson(err) => err.fmt(f),
1041            #[cfg(feature = "reqwest")]
1042            Error::Reqwest(err) => err.fmt(f),
1043        }
1044    }
1045}
1046
1047impl std::error::Error for Error {}