oauth1_request/
lib.rs

1//! Yet yet yet another OAuth 1.0 client library.
2//!
3//! # Usage
4//!
5//! Add this to your `Cargo.toml`:
6//!
7//! ```toml
8//! [dependencies]
9//! oauth = { version = "0.6", package = "oauth1-request" }
10//! ```
11//!
12//! For brevity, we refer to the crate name as `oauth` throughout the documentation,
13//! since the API is designed in favor of qualified paths like `oauth::get`.
14//!
15//! ## Create a request
16//!
17//! A typical authorization flow looks like this:
18//!
19#![cfg_attr(all(feature = "derive", feature = "hmac-sha1"), doc = " ```")]
20#![cfg_attr(
21    not(all(feature = "derive", feature = "hmac-sha1")),
22    doc = " ```ignore"
23)]
24//! # extern crate oauth1_request as oauth;
25//! #
26//! // Define a type to represent your request.
27//! #[derive(oauth::Request)]
28//! struct CreateComment<'a> {
29//!     article_id: u64,
30//!     text: &'a str,
31//! }
32//!
33//! let uri = "https://example.com/api/v1/comments/create.json";
34//!
35//! let request = CreateComment {
36//!     article_id: 123456789,
37//!     text: "A request signed with OAuth & Rust 🦀 🔏",
38//! };
39//!
40//! // Prepare your credentials.
41//! let token =
42//!     oauth::Token::from_parts("consumer_key", "consumer_secret", "token", "token_secret");
43//!
44//! // Create the `Authorization` header.
45//! let authorization_header = oauth::post(uri, &request, &token, oauth::HMAC_SHA1);
46//! # // Override the above value to pin the nonce and timestamp value.
47//! # let mut builder = oauth::Builder::new(token.client, oauth::HMAC_SHA1);
48//! # builder.token(token.token);
49//! # builder.nonce("Dk-OGluFEQ4f").timestamp(std::num::NonZeroU64::new(1234567890));
50//! # let authorization_header = builder.post(uri, &request);
51//! // `oauth_nonce` and `oauth_timestamp` vary on each execution.
52//! assert_eq!(
53//!     authorization_header,
54//!     "OAuth \
55//!          oauth_consumer_key=\"consumer_key\",\
56//!          oauth_nonce=\"Dk-OGluFEQ4f\",\
57//!          oauth_signature_method=\"HMAC-SHA1\",\
58//!          oauth_timestamp=\"1234567890\",\
59//!          oauth_token=\"token\",\
60//!          oauth_signature=\"n%2FrUgos4CFFZbZK8Z8wFR7drU4c%3D\"",
61//! );
62//!
63//! // You can create an `x-www-form-urlencoded` string or a URI with query pairs from the request.
64//!
65//! let form = oauth::to_form(&request);
66//! assert_eq!(
67//!     form,
68//!     "article_id=123456789&text=A%20request%20signed%20with%20OAuth%20%26%20Rust%20%F0%9F%A6%80%20%F0%9F%94%8F",
69//! );
70//!
71//! let uri = oauth::to_query(uri.to_owned(), &request);
72//! assert_eq!(
73//!     uri,
74//!     "https://example.com/api/v1/comments/create.json?article_id=123456789&text=A%20request%20signed%20with%20OAuth%20%26%20Rust%20%F0%9F%A6%80%20%F0%9F%94%8F",
75//! );
76//! ```
77//!
78//! See [`Request`][oauth1_request_derive::Request] for more details on the derive macro.
79//!
80//! If you want to authorize a request with dynamic keys, use
81//! [`oauth::ParameterList`][ParameterList].
82//!
83#![cfg_attr(feature = "alloc", doc = " ```")]
84#![cfg_attr(not(feature = "alloc"), doc = " ```ignore")]
85//! # extern crate oauth1_request as oauth;
86//! #
87//! use std::fmt::Display;
88//!
89//! let request = oauth::ParameterList::new([
90//!     ("article_id", &123456789 as &dyn Display),
91//!     ("text", &"A request signed with OAuth & Rust 🦀 🔏"),
92//! ]);
93//!
94//! let form = oauth::to_form(&request);
95//! assert_eq!(
96//!     form,
97//!     "article_id=123456789&text=A%20request%20signed%20with%20OAuth%20%26%20Rust%20%F0%9F%A6%80%20%F0%9F%94%8F",
98//! );
99//! ```
100//!
101//! Use [`oauth::Builder`][Builder] if you need to specify a callback URI or verifier:
102//!
103#![cfg_attr(all(feature = "alloc", feature = "hmac-sha1"), doc = " ```")]
104#![cfg_attr(not(all(feature = "alloc", feature = "hmac-sha1")), doc = " ```ignore")]
105//! # extern crate oauth1_request as oauth;
106//! #
107//! let uri = "https://example.com/oauth/request_temp_credentials";
108//! let callback = "https://client.example.net/oauth/callback";
109//!
110//! let client = oauth::Credentials::new("consumer_key", "consumer_secret");
111//!
112//! let authorization_header = oauth::Builder::<_, _>::new(client, oauth::HmacSha1::new())
113//!     .callback(callback)
114//!     .post(uri, &());
115//! ```
116
117#![cfg_attr(docsrs, feature(doc_cfg))]
118#![doc(html_root_url = "https://docs.rs/oauth1-request/0.6.1")]
119#![warn(missing_docs, rust_2018_idioms)]
120#![cfg_attr(not(feature = "std"), no_std)]
121
122#[cfg(feature = "alloc")]
123extern crate alloc;
124
125#[macro_use]
126mod util;
127
128pub mod request;
129pub mod serializer;
130pub mod signature_method;
131
132doc_auto_cfg! {
133    /// A derive macro for [`Request`] trait.
134    ///
135    /// The derive macro uses the struct's field names and `Display` implementation of the values as
136    /// the keys and values of the parameter pairs of the `Request`.
137    ///
138    /// ## Example
139    ///
140    #[cfg_attr(feature = "alloc", doc = " ```")]
141    #[cfg_attr(not(feature = "alloc"), doc = " ```ignore")]
142    /// # extern crate oauth1_request as oauth;
143    /// #
144    /// #[derive(oauth::Request)]
145    /// struct CreateItem<'a> {
146    ///     name: &'a str,
147    ///     #[oauth1(rename = "type")]
148    ///     kind: Option<u32>,
149    ///     #[oauth1(skip_if = str::is_empty)]
150    ///     note: &'a str,
151    /// }
152    ///
153    /// let request = CreateItem {
154    ///     name: "test",
155    ///     kind: Some(42),
156    ///     note: "",
157    /// };
158    ///
159    /// assert_eq!(oauth::to_form(&request), "name=test&type=42");
160    /// ```
161    ///
162    /// ## Field attributes
163    ///
164    /// You can customize the behavior of the derive macro with the following field attributes:
165    ///
166    /// - `#[oauth1(encoded)]`
167    ///
168    /// Do not percent encode the value when serializing it.
169    ///
170    /// - `#[oauth1(fmt = path)]`
171    ///
172    /// Use the formatting function at `path` instead of `Display::fmt` when serializing the value.
173    /// The function must be callable as `fn(&T, &mut Formatter<'_>) -> fmt::Result`
174    /// (same as `Display::fmt`).
175    ///
176    /// - `#[oauth1(option = true)]` (or `#[oauth1(option = false)]`)
177    ///
178    /// If set to `true`, skip the field when the value is `None` or use the unwrapped value otherwise.
179    /// The value's type must be `Option<T>` in this case.
180    ///
181    /// When the field's type name is `Option<_>`, the attribute is implicitly set to `true`.
182    /// Use `#[oauth1(option = false)]` if you need to opt out of that behavior.
183    ///
184    /// - `#[oauth1(rename = "name")]`
185    ///
186    /// Use the given string as the parameter's key. The given string must be URI-safe.
187    ///
188    /// - `#[oauth1(skip)]`
189    ///
190    /// Do not serialize the field.
191    ///
192    /// - `#[oauth1(skip_if = path)]`
193    ///
194    /// Call the function at `path` and do not serialize the field if the function returns `true`.
195    /// The function must be callable as `fn(&T) -> bool`.
196    ///
197    /// ## Container attributes
198    ///
199    /// - `#[oauth1(crate = "name")]`
200    ///
201    /// Specify the path of `oauth1_request` crate. The path is automatically determined by the
202    /// derive macro by default, even if the crate is renamed with the [`[package]`][package] key of
203    /// `Cargo.toml`, so you usually don't need this attribute. It may be useful if you are using an
204    /// exotic build tool where the crate name cannot be determined reliably.
205    ///
206    /// [package]: <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml>
207    #[cfg(feature = "derive")]
208    #[doc(inline)]
209    pub use oauth1_request_derive::Request;
210}
211#[doc(no_inline)]
212pub use oauth_credentials::{Credentials, Token};
213
214doc_auto_cfg! {
215    pub use self::request::ParameterList;
216    pub use self::request::Request;
217    #[cfg(feature = "hmac-sha1")]
218    pub use self::signature_method::HmacSha1;
219    pub use self::signature_method::Plaintext;
220    #[cfg(feature = "rsa-sha1-06")]
221    pub use self::signature_method::RsaSha1;
222    #[cfg(feature = "rsa-sha1-09")]
223    pub use self::signature_method::Rsa09Sha1;
224    #[cfg(feature = "hmac-sha1")]
225    pub use self::signature_method::HMAC_SHA1;
226    #[cfg(feature = "alloc")]
227    pub use self::signature_method::PLAINTEXT;
228}
229
230#[cfg(feature = "alloc")]
231use alloc::string::String;
232use core::fmt::Debug;
233use core::fmt::{Display, Write};
234use core::num::NonZeroU64;
235use core::str;
236
237use self::serializer::auth;
238use self::signature_method::SignatureMethod;
239
240cfg_type_param_hack! {
241    /// A builder for OAuth `Authorization` header string.
242    #[derive(Clone, Debug)]
243    pub struct Builder<
244        'a,
245        SM,
246        #[cfg(feature = "alloc")] C = String,
247        #[cfg(not(feature = "alloc"))] C,
248        T = C,
249    > {
250        signature_method: SM,
251        client: Credentials<C>,
252        token: Option<Credentials<T>>,
253        options: auth::Options<'a>,
254    }
255}
256
257macro_rules! builder_authorize_shorthand {
258    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
259        #[doc = concat!("Authorizes a `", $method, "` request to `uri`,")]
260        /// returning an HTTP `Authorization` header value.
261        ///
262        /// `uri` must not contain a query part, which would result in a wrong signature.
263        #[cfg(feature = "alloc")]
264        pub fn $name<U, R>(&self, uri: U, request: &R) -> String
265        where
266            U: Display,
267            R: Request + ?Sized,
268            SM: Clone,
269        {
270            self.authorize($method, uri, request)
271        }
272    )*}};
273}
274
275macro_rules! builder_to_form_shorthand {
276    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
277        #[doc = concat!("Authorizes a `", $method, "` request to `uri`,")]
278        /// writing the OAuth protocol parameters to an `x-www-form-urlencoded` string
279        /// along with the other request parameters.
280        ///
281        /// `uri` must not contain a query part, which would result in a wrong signature.
282        #[cfg(feature = "alloc")]
283        pub fn $name<U, R>(&self, uri: U, request: &R) -> String
284        where
285            U: Display,
286            R: Request + ?Sized,
287            SM: Clone,
288        {
289            self.to_form($method, uri, request)
290        }
291    )*}};
292}
293
294macro_rules! builder_to_query_shorthand {
295    ($($name:ident($method:expr);)*) => {$(
296        doc_coerce_expr! {
297            #[doc = concat!("Authorizes a `", $method, "` request to `uri`, appending")]
298            /// the OAuth protocol parameters to `uri` along with the other request parameters.
299            ///
300            /// `uri` must not contain a query part, which would result in a wrong signature.
301            pub fn $name<W, R>(&self, uri: W, request: &R) -> W
302            where
303                W: Display + Write,
304                R: Request + ?Sized,
305                SM: Clone,
306            {
307                self.to_query($method, uri, request)
308            }
309        }
310    )*};
311}
312
313impl<'a, SM: SignatureMethod, C: AsRef<str>, T: AsRef<str>> Builder<'a, SM, C, T> {
314    /// Creates a `Builder` that signs requests using the specified client credentials
315    /// and signature method.
316    pub fn new(client: Credentials<C>, signature_method: SM) -> Self {
317        Builder {
318            signature_method,
319            client,
320            token: None,
321            options: auth::Options::new(),
322        }
323    }
324
325    /// Creates a `Builder` that uses the token credentials from `token`.
326    pub fn with_token(token: Token<C, T>, signature_method: SM) -> Self {
327        let mut ret = Builder::new(token.client, signature_method);
328        ret.token(token.token);
329        ret
330    }
331
332    /// Sets/unsets the token credentials pair to sign requests with.
333    pub fn token(&mut self, token: impl Into<Option<Credentials<T>>>) -> &mut Self {
334        self.token = token.into();
335        self
336    }
337
338    /// Sets/unsets the `oauth_callback` URI.
339    pub fn callback(&mut self, callback: impl Into<Option<&'a str>>) -> &mut Self {
340        self.options.callback(callback);
341        self
342    }
343
344    /// Sets/unsets the `oauth_verifier` value.
345    pub fn verifier(&mut self, verifier: impl Into<Option<&'a str>>) -> &mut Self {
346        self.options.verifier(verifier);
347        self
348    }
349
350    /// Sets/unsets the `oauth_nonce` value.
351    ///
352    /// By default, `Builder` generates a random nonce for each request.
353    /// This method overrides that behavior and forces the `Builder` to use the specified nonce.
354    ///
355    /// This method is for debugging/testing purpose only and should not be used in production.
356    pub fn nonce(&mut self, nonce: impl Into<Option<&'a str>>) -> &mut Self {
357        self.options.nonce(nonce);
358        self
359    }
360
361    /// Sets/unsets the `oauth_timestamp` value.
362    ///
363    /// By default, `Builder` uses the timestamp of the time when `authorize`-like method is called.
364    /// This method overrides that behavior and forces the `Builder` to use the specified timestamp.
365    ///
366    /// This method is for debugging/testing purpose only and should not be used in production.
367    pub fn timestamp(&mut self, timestamp: impl Into<Option<NonZeroU64>>) -> &mut Self {
368        self.options.timestamp(timestamp);
369        self
370    }
371
372    /// Sets whether to include the `oauth_version` value in requests.
373    pub fn version(&mut self, version: bool) -> &mut Self {
374        self.options.version(version);
375        self
376    }
377
378    builder_authorize_shorthand! {
379        get("GET");
380        put("PUT");
381        post("POST");
382        delete("DELETE");
383        options("OPTIONS");
384        head("HEAD");
385        connect("CONNECT");
386        patch("PATCH");
387        trace("TRACE");
388    }
389
390    builder_to_form_shorthand! {
391        put_form("PUT");
392        post_form("POST");
393        options_form("OPTIONS");
394        patch_form("PATCH");
395    }
396
397    builder_to_query_shorthand! {
398        get_query("GET");
399        put_query("PUT");
400        post_query("POST");
401        delete_query("DELETE");
402        options_query("OPTIONS");
403        head_query("HEAD");
404        connect_query("CONNECT");
405        patch_query("PATCH");
406        trace_query("TRACE");
407    }
408
409    doc_auto_cfg! {
410        /// Authorizes a request to `uri` with a custom HTTP request method,
411        /// returning an HTTP `Authorization` header value.
412        ///
413        /// `uri` must not contain a query part, which would result in a wrong signature.
414        #[cfg(feature = "alloc")]
415        pub fn authorize<U, R>(&self, method: &str, uri: U, request: &R) -> String
416        where
417            U: Display,
418            R: Request + ?Sized,
419            SM: Clone,
420        {
421            let serializer = serializer::auth::Authorizer::authorization(
422                method,
423                uri,
424                self.client.as_ref(),
425                self.token.as_ref().map(Credentials::as_ref),
426                &self.options,
427                self.signature_method.clone(),
428            );
429
430            request.serialize(serializer)
431        }
432
433        /// Authorizes a request to `uri` with a custom HTTP request method, writing the OAuth protocol
434        /// parameters to an `x-www-form-urlencoded` string along with the other request parameters.
435        ///
436        /// `uri` must not contain a query part, which would result in a wrong signature.
437        #[cfg(feature = "alloc")]
438        pub fn to_form<U, R>(&self, method: &str, uri: U, request: &R) -> String
439        where
440            U: Display,
441            R: Request + ?Sized,
442            SM: Clone,
443        {
444            let serializer = serializer::auth::Authorizer::form(
445                method,
446                uri,
447                self.client.as_ref(),
448                self.token.as_ref().map(Credentials::as_ref),
449                &self.options,
450                self.signature_method.clone(),
451            );
452
453            request.serialize(serializer)
454        }
455    }
456
457    /// Authorizes a request to `uri` with a custom HTTP request method, appending the OAuth
458    /// protocol parameters to `uri` along with the other request parameters.
459    ///
460    /// `uri` must not contain a query part, which would result in a wrong signature.
461    pub fn to_query<W, R>(&self, method: &str, uri: W, request: &R) -> W
462    where
463        W: Display + Write,
464        R: Request + ?Sized,
465        SM: Clone,
466    {
467        let serializer = serializer::auth::Authorizer::query(
468            method,
469            uri,
470            self.client.as_ref(),
471            self.token.as_ref().map(Credentials::as_ref),
472            &self.options,
473            self.signature_method.clone(),
474        );
475
476        request.serialize(serializer)
477    }
478
479    /// Same as `authorize` except that this writes the resulting `Authorization` header value
480    /// into `buf`.
481    pub fn authorize_with_buf<W, U, R>(&self, buf: W, method: &str, uri: U, request: &R) -> W
482    where
483        W: Write,
484        U: Display,
485        R: Request + ?Sized,
486        SM: Clone,
487    {
488        let serializer = serializer::auth::Authorizer::authorization_with_buf(
489            buf,
490            method,
491            uri,
492            self.client.as_ref(),
493            self.token.as_ref().map(Credentials::as_ref),
494            &self.options,
495            self.signature_method.clone(),
496        );
497
498        request.serialize(serializer)
499    }
500
501    doc_auto_cfg! {
502        /// Same as `to_form` except that this writes the resulting `x-www-form-urlencoded` string
503        /// into `buf`.
504        #[cfg(feature = "alloc")]
505        pub fn to_form_with_buf<W, U, R>(&self, buf: W, method: &str, uri: U, request: &R) -> W
506        where
507            W: Write,
508            U: Display,
509            R: Request + ?Sized,
510            SM: Clone,
511        {
512            let serializer = serializer::auth::Authorizer::form_with_buf(
513                buf,
514                method,
515                uri,
516                self.client.as_ref(),
517                self.token.as_ref().map(Credentials::as_ref),
518                &self.options,
519                self.signature_method.clone(),
520            );
521
522            request.serialize(serializer)
523        }
524
525        /// Authorizes a request and consumes `self`, returning an HTTP `Authorization` header value.
526        ///
527        /// Unlike `authorize`, this does not clone the signature method and may be more efficient for
528        /// non-`Copy` signature methods like `RsaSha1`.
529        ///
530        /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
531        /// use `authorize` instead.
532        #[cfg(feature = "alloc")]
533        pub fn into_authorization<U, R>(self, method: &str, uri: U, request: &R) -> String
534        where
535            U: Display,
536            R: Request + ?Sized,
537        {
538            let serializer = serializer::auth::Authorizer::authorization(
539                method,
540                uri,
541                self.client.as_ref(),
542                self.token.as_ref().map(Credentials::as_ref),
543                &self.options,
544                self.signature_method,
545            );
546
547            request.serialize(serializer)
548        }
549
550        /// Authorizes a request and consumes `self`, writing the OAuth protocol parameters to
551        /// an `x-www-form-urlencoded` string along with the other request parameters.
552        ///
553        /// Unlike `to_form`, this does not clone the signature method and may be more efficient for
554        /// non-`Copy` signature methods like `RsaSha1`.
555        ///
556        /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
557        /// use `to_form` instead.
558        #[cfg(feature = "alloc")]
559        pub fn into_form<U, R>(self, method: &str, uri: U, request: &R) -> String
560        where
561            U: Display,
562            R: Request + ?Sized,
563        {
564            let serializer = serializer::auth::Authorizer::form(
565                method,
566                uri,
567                self.client.as_ref(),
568                self.token.as_ref().map(Credentials::as_ref),
569                &self.options,
570                self.signature_method,
571            );
572
573            request.serialize(serializer)
574        }
575    }
576
577    /// Authorizes a request and consumes `self`, appending the OAuth protocol parameters to
578    /// `uri` along with the other request parameters.
579    ///
580    /// Unlike `to_query`, this does not clone the signature method and may be more efficient for
581    /// non-`Copy` signature methods like `RsaSha1`.
582    ///
583    /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
584    /// use `to_query` instead.
585    pub fn into_query<W, R>(self, method: &str, uri: W, request: &R) -> W
586    where
587        W: Display + Write,
588        R: Request + ?Sized,
589    {
590        let serializer = serializer::auth::Authorizer::query(
591            method,
592            uri,
593            self.client.as_ref(),
594            self.token.as_ref().map(Credentials::as_ref),
595            &self.options,
596            self.signature_method,
597        );
598
599        request.serialize(serializer)
600    }
601
602    /// Same as `into_authorization` except that this writes the resulting `Authorization` header
603    /// value into `buf`.
604    pub fn into_authorization_with_buf<W, U, R>(
605        self,
606        buf: W,
607        method: &str,
608        uri: U,
609        request: &R,
610    ) -> W
611    where
612        W: Write,
613        U: Display,
614        R: Request + ?Sized,
615        SM: Clone,
616    {
617        let serializer = serializer::auth::Authorizer::authorization_with_buf(
618            buf,
619            method,
620            uri,
621            self.client.as_ref(),
622            self.token.as_ref().map(Credentials::as_ref),
623            &self.options,
624            self.signature_method,
625        );
626
627        request.serialize(serializer)
628    }
629
630    /// Same as `into_form` except that this writes the resulting `x-www-form-urlencoded` string
631    /// into `buf`.
632    pub fn into_form_with_buf<W, U, R>(self, buf: W, method: &str, uri: U, request: &R) -> W
633    where
634        W: Write,
635        U: Display,
636        R: Request + ?Sized,
637    {
638        let serializer = serializer::auth::Authorizer::form_with_buf(
639            buf,
640            method,
641            uri,
642            self.client.as_ref(),
643            self.token.as_ref().map(Credentials::as_ref),
644            &self.options,
645            self.signature_method,
646        );
647
648        request.serialize(serializer)
649    }
650}
651
652macro_rules! authorize_shorthand {
653    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
654        #[doc = concat!("Authorizes a `", $method, "` request to `uri` with the given credentials.")]
655        ///
656        /// This returns an HTTP `Authorization` header value.
657        ///
658        /// `uri` must not contain a query part, which would result in a wrong signature.
659        #[cfg(feature = "alloc")]
660        pub fn $name<U, R, C, T, SM>(
661            uri: U,
662            request: &R,
663            token: &Token<C, T>,
664            signature_method: SM,
665        ) -> String
666        where
667            U: Display,
668            R: Request + ?Sized,
669            C: AsRef<str>,
670            T: AsRef<str>,
671            SM: SignatureMethod,
672        {
673            authorize($method, uri, request, token, signature_method)
674        }
675    )*}};
676}
677
678authorize_shorthand! {
679    get("GET");
680    put("PUT");
681    post("POST");
682    delete("DELETE");
683    options("OPTIONS");
684    head("HEAD");
685    connect("CONNECT");
686    patch("PATCH");
687    trace("TRACE");
688}
689
690doc_auto_cfg! {
691    /// Authorizes a request to `uri` with the given credentials.
692    ///
693    /// This returns an HTTP `Authorization` header value.
694    ///
695    /// `uri` must not contain a query part, which would result in a wrong signature.
696    #[cfg(feature = "alloc")]
697    pub fn authorize<U, R, C, T, SM>(
698        method: &str,
699        uri: U,
700        request: &R,
701        token: &Token<C, T>,
702        signature_method: SM,
703    ) -> String
704    where
705        U: Display,
706        R: Request + ?Sized,
707        C: AsRef<str>,
708        T: AsRef<str>,
709        SM: SignatureMethod,
710    {
711        fn inner<U, R, SM>(
712            method: &str,
713            uri: U,
714            request: &R,
715            token: Token<&str, &str>,
716            signature_method: SM,
717        ) -> String
718        where
719            U: Display,
720            R: Request + ?Sized,
721            SM: SignatureMethod,
722        {
723            Builder::with_token(token, signature_method).into_authorization(method, uri, request)
724        }
725        inner(method, uri, request, token.as_ref(), signature_method)
726    }
727
728    /// Serializes a `Request` to an `x-www-form-urlencoded` string.
729    #[cfg(feature = "alloc")]
730    pub fn to_form<R>(request: &R) -> String
731    where
732        R: Request + ?Sized,
733    {
734        request.serialize(serializer::Urlencoder::form())
735    }
736
737    /// Serializes a `Request` to a query string and appends it to the given URI.
738    ///
739    /// This function naively concatenates a query string to `uri` and if `uri` already has
740    /// a query part, it will have a duplicate query part like `?foo=bar?baz=qux`.
741    #[cfg(feature = "alloc")]
742    pub fn to_query<R>(uri: String, request: &R) -> String
743    where
744        R: Request + ?Sized,
745    {
746        request.serialize(serializer::Urlencoder::query(uri))
747    }
748}