oauth1_request_ios/
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.0.1", package = "oauth1-request-ios" }
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_ios 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_ios::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_ios 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_ios 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-ios/0.0.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_ios 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_ios::Request;
210}
211#[doc(no_inline)]
212pub use oauth_credentials_ios::{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 = "hmac-sha256")]
221    pub use self::signature_method::HmacSha256;
222    #[cfg(feature = "rsa-sha1-06")]
223    pub use self::signature_method::RsaSha1;
224    #[cfg(feature = "rsa-sha1-09")]
225    pub use self::signature_method::Rsa09Sha1;
226    #[cfg(feature = "hmac-sha1")]
227    pub use self::signature_method::HMAC_SHA1;
228    #[cfg(feature = "hmac-sha256")]
229    pub use self::signature_method::HMAC_SHA256;
230    #[cfg(feature = "alloc")]
231    pub use self::signature_method::PLAINTEXT;
232}
233
234#[cfg(feature = "alloc")]
235use alloc::string::String;
236use core::fmt::Debug;
237use core::fmt::{Display, Write};
238use core::num::NonZeroU64;
239use core::str;
240
241use self::serializer::auth;
242use self::signature_method::SignatureMethod;
243
244cfg_type_param_hack! {
245    /// A builder for OAuth `Authorization` header string.
246    #[derive(Clone, Debug)]
247    pub struct Builder<
248        'a,
249        SM,
250        #[cfg(feature = "alloc")] C = String,
251        #[cfg(not(feature = "alloc"))] C,
252        T = C,
253    > {
254        signature_method: SM,
255        client: Credentials<C>,
256        token: Option<Credentials<T>>,
257        options: auth::Options<'a>,
258    }
259}
260
261macro_rules! builder_authorize_shorthand {
262    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
263        #[doc = concat!("Authorizes a `", $method, "` request to `uri`,")]
264        /// returning an HTTP `Authorization` header value.
265        ///
266        /// `uri` must not contain a query part, which would result in a wrong signature.
267        #[cfg(feature = "alloc")]
268        pub fn $name<U, R>(&self, uri: U, request: &R) -> String
269        where
270            U: Display,
271            R: Request + ?Sized,
272            SM: Clone,
273        {
274            self.authorize($method, uri, request)
275        }
276    )*}};
277}
278
279macro_rules! builder_to_form_shorthand {
280    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
281        #[doc = concat!("Authorizes a `", $method, "` request to `uri`,")]
282        /// writing the OAuth protocol parameters to an `x-www-form-urlencoded` string
283        /// along with the other request parameters.
284        ///
285        /// `uri` must not contain a query part, which would result in a wrong signature.
286        #[cfg(feature = "alloc")]
287        pub fn $name<U, R>(&self, uri: U, request: &R) -> String
288        where
289            U: Display,
290            R: Request + ?Sized,
291            SM: Clone,
292        {
293            self.to_form($method, uri, request)
294        }
295    )*}};
296}
297
298macro_rules! builder_to_query_shorthand {
299    ($($name:ident($method:expr);)*) => {$(
300        doc_coerce_expr! {
301            #[doc = concat!("Authorizes a `", $method, "` request to `uri`, appending")]
302            /// the OAuth protocol parameters to `uri` along with the other request parameters.
303            ///
304            /// `uri` must not contain a query part, which would result in a wrong signature.
305            pub fn $name<W, R>(&self, uri: W, request: &R) -> W
306            where
307                W: Display + Write,
308                R: Request + ?Sized,
309                SM: Clone,
310            {
311                self.to_query($method, uri, request)
312            }
313        }
314    )*};
315}
316
317impl<'a, SM: SignatureMethod, C: AsRef<str>, T: AsRef<str>> Builder<'a, SM, C, T> {
318    /// Creates a `Builder` that signs requests using the specified client credentials
319    /// and signature method.
320    pub fn new(client: Credentials<C>, signature_method: SM) -> Self {
321        Builder {
322            signature_method,
323            client,
324            token: None,
325            options: auth::Options::new(),
326        }
327    }
328
329    /// Creates a `Builder` that uses the token credentials from `token`.
330    pub fn with_token(token: Token<C, T>, signature_method: SM) -> Self {
331        let mut ret = Builder::new(token.client, signature_method);
332        ret.token(token.token);
333        ret
334    }
335
336    /// Sets/unsets the token credentials pair to sign requests with.
337    pub fn token(&mut self, token: impl Into<Option<Credentials<T>>>) -> &mut Self {
338        self.token = token.into();
339        self
340    }
341
342    /// Sets/unsets the `oauth_callback` URI.
343    pub fn callback(&mut self, callback: impl Into<Option<&'a str>>) -> &mut Self {
344        self.options.callback(callback);
345        self
346    }
347
348    /// Sets/unsets the `oauth_verifier` value.
349    pub fn verifier(&mut self, verifier: impl Into<Option<&'a str>>) -> &mut Self {
350        self.options.verifier(verifier);
351        self
352    }
353
354    /// Sets/unsets the `oauth_nonce` value.
355    ///
356    /// By default, `Builder` generates a random nonce for each request.
357    /// This method overrides that behavior and forces the `Builder` to use the specified nonce.
358    ///
359    /// This method is for debugging/testing purpose only and should not be used in production.
360    pub fn nonce(&mut self, nonce: impl Into<Option<&'a str>>) -> &mut Self {
361        self.options.nonce(nonce);
362        self
363    }
364
365    /// Sets/unsets the `oauth_timestamp` value.
366    ///
367    /// By default, `Builder` uses the timestamp of the time when `authorize`-like method is called.
368    /// This method overrides that behavior and forces the `Builder` to use the specified timestamp.
369    ///
370    /// This method is for debugging/testing purpose only and should not be used in production.
371    pub fn timestamp(&mut self, timestamp: impl Into<Option<NonZeroU64>>) -> &mut Self {
372        self.options.timestamp(timestamp);
373        self
374    }
375
376    /// Sets whether to include the `oauth_version` value in requests.
377    pub fn version(&mut self, version: bool) -> &mut Self {
378        self.options.version(version);
379        self
380    }
381
382    builder_authorize_shorthand! {
383        get("GET");
384        put("PUT");
385        post("POST");
386        delete("DELETE");
387        options("OPTIONS");
388        head("HEAD");
389        connect("CONNECT");
390        patch("PATCH");
391        trace("TRACE");
392    }
393
394    builder_to_form_shorthand! {
395        put_form("PUT");
396        post_form("POST");
397        options_form("OPTIONS");
398        patch_form("PATCH");
399    }
400
401    builder_to_query_shorthand! {
402        get_query("GET");
403        put_query("PUT");
404        post_query("POST");
405        delete_query("DELETE");
406        options_query("OPTIONS");
407        head_query("HEAD");
408        connect_query("CONNECT");
409        patch_query("PATCH");
410        trace_query("TRACE");
411    }
412
413    doc_auto_cfg! {
414        /// Authorizes a request to `uri` with a custom HTTP request method,
415        /// returning an HTTP `Authorization` header value.
416        ///
417        /// `uri` must not contain a query part, which would result in a wrong signature.
418        #[cfg(feature = "alloc")]
419        pub fn authorize<U, R>(&self, method: &str, uri: U, request: &R) -> String
420        where
421            U: Display,
422            R: Request + ?Sized,
423            SM: Clone,
424        {
425            let serializer = serializer::auth::Authorizer::authorization(
426                method,
427                uri,
428                self.client.as_ref(),
429                self.token.as_ref().map(Credentials::as_ref),
430                &self.options,
431                self.signature_method.clone(),
432            );
433
434            request.serialize(serializer)
435        }
436
437        /// Authorizes a request to `uri` with a custom HTTP request method, writing the OAuth protocol
438        /// parameters to an `x-www-form-urlencoded` string along with the other request parameters.
439        ///
440        /// `uri` must not contain a query part, which would result in a wrong signature.
441        #[cfg(feature = "alloc")]
442        pub fn to_form<U, R>(&self, method: &str, uri: U, request: &R) -> String
443        where
444            U: Display,
445            R: Request + ?Sized,
446            SM: Clone,
447        {
448            let serializer = serializer::auth::Authorizer::form(
449                method,
450                uri,
451                self.client.as_ref(),
452                self.token.as_ref().map(Credentials::as_ref),
453                &self.options,
454                self.signature_method.clone(),
455            );
456
457            request.serialize(serializer)
458        }
459    }
460
461    /// Authorizes a request to `uri` with a custom HTTP request method, appending the OAuth
462    /// protocol parameters to `uri` along with the other request parameters.
463    ///
464    /// `uri` must not contain a query part, which would result in a wrong signature.
465    pub fn to_query<W, R>(&self, method: &str, uri: W, request: &R) -> W
466    where
467        W: Display + Write,
468        R: Request + ?Sized,
469        SM: Clone,
470    {
471        let serializer = serializer::auth::Authorizer::query(
472            method,
473            uri,
474            self.client.as_ref(),
475            self.token.as_ref().map(Credentials::as_ref),
476            &self.options,
477            self.signature_method.clone(),
478        );
479
480        request.serialize(serializer)
481    }
482
483    /// Same as `authorize` except that this writes the resulting `Authorization` header value
484    /// into `buf`.
485    pub fn authorize_with_buf<W, U, R>(&self, buf: W, method: &str, uri: U, request: &R) -> W
486    where
487        W: Write,
488        U: Display,
489        R: Request + ?Sized,
490        SM: Clone,
491    {
492        let serializer = serializer::auth::Authorizer::authorization_with_buf(
493            buf,
494            method,
495            uri,
496            self.client.as_ref(),
497            self.token.as_ref().map(Credentials::as_ref),
498            &self.options,
499            self.signature_method.clone(),
500        );
501
502        request.serialize(serializer)
503    }
504
505    doc_auto_cfg! {
506        /// Same as `to_form` except that this writes the resulting `x-www-form-urlencoded` string
507        /// into `buf`.
508        #[cfg(feature = "alloc")]
509        pub fn to_form_with_buf<W, U, R>(&self, buf: W, method: &str, uri: U, request: &R) -> W
510        where
511            W: Write,
512            U: Display,
513            R: Request + ?Sized,
514            SM: Clone,
515        {
516            let serializer = serializer::auth::Authorizer::form_with_buf(
517                buf,
518                method,
519                uri,
520                self.client.as_ref(),
521                self.token.as_ref().map(Credentials::as_ref),
522                &self.options,
523                self.signature_method.clone(),
524            );
525
526            request.serialize(serializer)
527        }
528
529        /// Authorizes a request and consumes `self`, returning an HTTP `Authorization` header value.
530        ///
531        /// Unlike `authorize`, this does not clone the signature method and may be more efficient for
532        /// non-`Copy` signature methods like `RsaSha1`.
533        ///
534        /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
535        /// use `authorize` instead.
536        #[cfg(feature = "alloc")]
537        pub fn into_authorization<U, R>(self, method: &str, uri: U, request: &R) -> String
538        where
539            U: Display,
540            R: Request + ?Sized,
541        {
542            let serializer = serializer::auth::Authorizer::authorization(
543                method,
544                uri,
545                self.client.as_ref(),
546                self.token.as_ref().map(Credentials::as_ref),
547                &self.options,
548                self.signature_method,
549            );
550
551            request.serialize(serializer)
552        }
553
554        /// Authorizes a request and consumes `self`, writing the OAuth protocol parameters to
555        /// an `x-www-form-urlencoded` string along with the other request parameters.
556        ///
557        /// Unlike `to_form`, this does not clone the signature method and may be more efficient for
558        /// non-`Copy` signature methods like `RsaSha1`.
559        ///
560        /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
561        /// use `to_form` instead.
562        #[cfg(feature = "alloc")]
563        pub fn into_form<U, R>(self, method: &str, uri: U, request: &R) -> String
564        where
565            U: Display,
566            R: Request + ?Sized,
567        {
568            let serializer = serializer::auth::Authorizer::form(
569                method,
570                uri,
571                self.client.as_ref(),
572                self.token.as_ref().map(Credentials::as_ref),
573                &self.options,
574                self.signature_method,
575            );
576
577            request.serialize(serializer)
578        }
579    }
580
581    /// Authorizes a request and consumes `self`, appending the OAuth protocol parameters to
582    /// `uri` along with the other request parameters.
583    ///
584    /// Unlike `to_query`, this does not clone the signature method and may be more efficient for
585    /// non-`Copy` signature methods like `RsaSha1`.
586    ///
587    /// For `HmacSha1`, `&RsaSha1` and `Plaintext`, cloning is no-op or very cheap so you should
588    /// use `to_query` instead.
589    pub fn into_query<W, R>(self, method: &str, uri: W, request: &R) -> W
590    where
591        W: Display + Write,
592        R: Request + ?Sized,
593    {
594        let serializer = serializer::auth::Authorizer::query(
595            method,
596            uri,
597            self.client.as_ref(),
598            self.token.as_ref().map(Credentials::as_ref),
599            &self.options,
600            self.signature_method,
601        );
602
603        request.serialize(serializer)
604    }
605
606    /// Same as `into_authorization` except that this writes the resulting `Authorization` header
607    /// value into `buf`.
608    pub fn into_authorization_with_buf<W, U, R>(
609        self,
610        buf: W,
611        method: &str,
612        uri: U,
613        request: &R,
614    ) -> W
615    where
616        W: Write,
617        U: Display,
618        R: Request + ?Sized,
619        SM: Clone,
620    {
621        let serializer = serializer::auth::Authorizer::authorization_with_buf(
622            buf,
623            method,
624            uri,
625            self.client.as_ref(),
626            self.token.as_ref().map(Credentials::as_ref),
627            &self.options,
628            self.signature_method,
629        );
630
631        request.serialize(serializer)
632    }
633
634    /// Same as `into_form` except that this writes the resulting `x-www-form-urlencoded` string
635    /// into `buf`.
636    pub fn into_form_with_buf<W, U, R>(self, buf: W, method: &str, uri: U, request: &R) -> W
637    where
638        W: Write,
639        U: Display,
640        R: Request + ?Sized,
641    {
642        let serializer = serializer::auth::Authorizer::form_with_buf(
643            buf,
644            method,
645            uri,
646            self.client.as_ref(),
647            self.token.as_ref().map(Credentials::as_ref),
648            &self.options,
649            self.signature_method,
650        );
651
652        request.serialize(serializer)
653    }
654}
655
656macro_rules! authorize_shorthand {
657    ($($name:ident($method:expr);)*) => {doc_auto_cfg! {$(
658        #[doc = concat!("Authorizes a `", $method, "` request to `uri` with the given credentials.")]
659        ///
660        /// This returns an HTTP `Authorization` header value.
661        ///
662        /// `uri` must not contain a query part, which would result in a wrong signature.
663        #[cfg(feature = "alloc")]
664        pub fn $name<U, R, C, T, SM>(
665            uri: U,
666            request: &R,
667            token: &Token<C, T>,
668            signature_method: SM,
669        ) -> String
670        where
671            U: Display,
672            R: Request + ?Sized,
673            C: AsRef<str>,
674            T: AsRef<str>,
675            SM: SignatureMethod,
676        {
677            authorize($method, uri, request, token, signature_method)
678        }
679    )*}};
680}
681
682authorize_shorthand! {
683    get("GET");
684    put("PUT");
685    post("POST");
686    delete("DELETE");
687    options("OPTIONS");
688    head("HEAD");
689    connect("CONNECT");
690    patch("PATCH");
691    trace("TRACE");
692}
693
694doc_auto_cfg! {
695    /// Authorizes a request to `uri` with the given credentials.
696    ///
697    /// This returns an HTTP `Authorization` header value.
698    ///
699    /// `uri` must not contain a query part, which would result in a wrong signature.
700    #[cfg(feature = "alloc")]
701    pub fn authorize<U, R, C, T, SM>(
702        method: &str,
703        uri: U,
704        request: &R,
705        token: &Token<C, T>,
706        signature_method: SM,
707    ) -> String
708    where
709        U: Display,
710        R: Request + ?Sized,
711        C: AsRef<str>,
712        T: AsRef<str>,
713        SM: SignatureMethod,
714    {
715        fn inner<U, R, SM>(
716            method: &str,
717            uri: U,
718            request: &R,
719            token: Token<&str, &str>,
720            signature_method: SM,
721        ) -> String
722        where
723            U: Display,
724            R: Request + ?Sized,
725            SM: SignatureMethod,
726        {
727            Builder::with_token(token, signature_method).into_authorization(method, uri, request)
728        }
729        inner(method, uri, request, token.as_ref(), signature_method)
730    }
731
732    /// Serializes a `Request` to an `x-www-form-urlencoded` string.
733    #[cfg(feature = "alloc")]
734    pub fn to_form<R>(request: &R) -> String
735    where
736        R: Request + ?Sized,
737    {
738        request.serialize(serializer::Urlencoder::form())
739    }
740
741    /// Serializes a `Request` to a query string and appends it to the given URI.
742    ///
743    /// This function naively concatenates a query string to `uri` and if `uri` already has
744    /// a query part, it will have a duplicate query part like `?foo=bar?baz=qux`.
745    #[cfg(feature = "alloc")]
746    pub fn to_query<R>(uri: String, request: &R) -> String
747    where
748        R: Request + ?Sized,
749    {
750        request.serialize(serializer::Urlencoder::query(uri))
751    }
752}