From de61f0a33e994eb8c2403a339b23f5de8c9ca332 Mon Sep 17 00:00:00 2001 From: Anton Maltsev Date: Fri, 19 Jan 2024 13:55:04 +0700 Subject: [PATCH 1/2] Added crate feature requirements to docs --- Cargo.toml | 5 +++-- src/custom_providers.rs | 5 +++++ src/lib.rs | 41 +++++++++++++++++++++++++++++++++-------- src/rfc.rs | 4 ++++ src/secret.rs | 4 ++-- src/url_error.rs | 1 + 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e1f9fcf..fcf70b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ members = [ ] [package.metadata.docs.rs] -features = [ "qr", "serde_support", "gen_secret" ] +all-features = true +rustc-args = ["--cfg", "docsrs"] [features] default = [] @@ -39,4 +40,4 @@ url = { version = "2.4", optional = true } constant_time_eq = "0.2" rand = { version = "0.8", features = ["std_rng", "std"], optional = true, default-features = false } zeroize = { version = "1.6", features = ["alloc", "derive"], optional = true } -qrcodegen-image = { version = "1.0", features = ["base64"], optional = true, path = "qrcodegen-image" } \ No newline at end of file +qrcodegen-image = { version = "1.0", features = ["base64"], optional = true, path = "qrcodegen-image" } diff --git a/src/custom_providers.rs b/src/custom_providers.rs index 08ec2e8..86750b1 100644 --- a/src/custom_providers.rs +++ b/src/custom_providers.rs @@ -2,6 +2,7 @@ use crate::{Algorithm, TOTP}; #[cfg(feature = "steam")] +#[cfg_attr(docsrs, doc(cfg(feature = "steam")))] impl TOTP { #[cfg(feature = "otpauth")] /// Will create a new instance of TOTP using the Steam algorithm with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values @@ -9,6 +10,8 @@ impl TOTP { /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` /// + /// # Example + /// /// ```rust /// use totp_rs::{Secret, TOTP}; /// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".into()); @@ -32,6 +35,8 @@ impl TOTP { /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` /// + /// # Example + /// /// ```rust /// use totp_rs::{Secret, TOTP}; /// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".to_string()); diff --git a/src/lib.rs b/src/lib.rs index 14d1ecd..fd9243b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,9 @@ //! # } //! ``` +// enable `doc_cfg` feature for `docs.rs`. +#![cfg_attr(docsrs, feature(doc_cfg))] + mod custom_providers; mod rfc; mod secret; @@ -88,6 +91,8 @@ pub enum Algorithm { SHA256, SHA512, #[cfg(feature = "steam")] + #[cfg_attr(docsrs, doc(cfg(feature = "steam")))] + /// Steam TOTP token algorithm Steam, } @@ -153,11 +158,13 @@ pub struct TOTP { /// non-encoded value pub secret: Vec, #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// The "Github" part of "Github:constantoine@github.com". Must not contain a colon `:` /// For example, the name of your service/website. /// Not mandatory, but strongly recommended! pub issuer: Option, #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:` /// For example, the name of your user's account. pub account_name: String, @@ -210,6 +217,8 @@ impl core::fmt::Display for TOTP { } #[cfg(all(feature = "gen_secret", not(feature = "otpauth")))] +// because `Default` is implemented regardless of `otpauth` feature we don't specify it here +#[cfg_attr(docsrs, doc(cfg(feature = "gen_secret")))] impl Default for TOTP { fn default() -> Self { return TOTP::new( @@ -224,6 +233,7 @@ impl Default for TOTP { } #[cfg(all(feature = "gen_secret", feature = "otpauth"))] +#[cfg_attr(docsrs, doc(cfg(feature = "gen_secret")))] impl Default for TOTP { fn default() -> Self { TOTP::new( @@ -245,16 +255,18 @@ impl TOTP { /// /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` + /// * `digits`: MUST be between 6 & 8 + /// * `secret`: Must have bitsize of at least 128 + /// * `account_name`: Must not contain `:` + /// * `issuer`: Must not contain `:` + /// + /// # Example /// /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); /// let totp = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap(), None, "".to_string()).unwrap(); /// ``` - /// * `digits`: MUST be between 6 & 8 - /// * `secret`: Must have bitsize of at least 128 - /// * `account_name`: Must not contain `:` - /// * `issuer`: Must not contain `:` /// /// # Errors /// @@ -293,6 +305,8 @@ impl TOTP { /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` /// + /// # Example + /// /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); @@ -323,14 +337,16 @@ impl TOTP { /// /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` + /// * `digits`: MUST be between 6 & 8 + /// * `secret`: Must have bitsize of at least 128 + /// + /// # Example /// /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); /// let totp = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap()).unwrap(); /// ``` - /// * `digits`: MUST be between 6 & 8 - /// * `secret`: Must have bitsize of at least 128 /// /// # Errors /// @@ -353,6 +369,8 @@ impl TOTP { /// # Description /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` /// + /// # Example + /// /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); @@ -475,6 +493,7 @@ impl TOTP { /// Generate a TOTP from the standard otpauth URL #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] pub fn from_url>(url: S) -> Result { let (algorithm, digits, skew, step, secret, issuer, account_name) = Self::parts_from_url(url)?; @@ -483,6 +502,7 @@ impl TOTP { /// Generate a TOTP from the standard otpauth URL, using `TOTP::new_unchecked` internally #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] pub fn from_url_unchecked>(url: S) -> Result { let (algorithm, digits, skew, step, secret, issuer, account_name) = Self::parts_from_url(url)?; @@ -499,6 +519,7 @@ impl TOTP { /// Parse the TOTP parts from the standard otpauth URL #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] fn parts_from_url>( url: S, ) -> Result<(Algorithm, usize, u8, u64, Vec, Option, String), TotpUrlError> { @@ -614,6 +635,7 @@ impl TOTP { /// Label and issuer will be URL-encoded if needed be /// Secret will be base 32'd without padding, as per RFC. #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] pub fn get_url(&self) -> String { #[allow(unused_mut)] let mut host = "totp"; @@ -645,6 +667,7 @@ impl TOTP { } #[cfg(feature = "qr")] +#[cfg_attr(docsrs, doc(cfg(feature = "qr")))] impl TOTP { #[deprecated( since = "5.3.0", @@ -665,7 +688,8 @@ impl TOTP { /// This would require the get_url method to generate an url bigger than 2000 characters, /// Which would be too long for some browsers anyway. /// - /// It will also return an error in case it can't encode the qr into a png. This shouldn't happen unless either the qrcode library returns malformed data, or the image library doesn't encode the data correctly + /// It will also return an error in case it can't encode the qr into a png. + /// This shouldn't happen unless either the qrcode library returns malformed data, or the image library doesn't encode the data correctly pub fn get_qr_base64(&self) -> Result { let url = self.get_url(); qrcodegen_image::draw_base64(&url) @@ -680,7 +704,8 @@ impl TOTP { /// This would require the get_url method to generate an url bigger than 2000 characters, /// Which would be too long for some browsers anyway. /// - /// It will also return an error in case it can't encode the qr into a png. This shouldn't happen unless either the qrcode library returns malformed data, or the image library doesn't encode the data correctly + /// It will also return an error in case it can't encode the qr into a png. + /// This shouldn't happen unless either the qrcode library returns malformed data, or the image library doesn't encode the data correctly pub fn get_qr_png(&self) -> Result, String> { let url = self.get_url(); qrcodegen_image::draw_png(&url) diff --git a/src/rfc.rs b/src/rfc.rs index 28cf83e..f1457cb 100644 --- a/src/rfc.rs +++ b/src/rfc.rs @@ -78,11 +78,13 @@ pub struct Rfc6238 { /// As per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-4) the secret should come from a strong source, most likely a CSPRNG. It should be at least 128 bits, but 160 are recommended. secret: Vec, #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// The "Github" part of "Github:constantoine@github.com". Must not contain a colon `:` /// For example, the name of your service/website. /// Not mandatory, but strongly recommended! issuer: Option, #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:`. /// For example, the name of your user's account. account_name: String, @@ -156,12 +158,14 @@ impl Rfc6238 { } #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// Set the `issuer`. pub fn issuer(&mut self, value: String) { self.issuer = Some(value); } #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] /// Set the `account_name`. pub fn account_name(&mut self, value: String) { self.account_name = value; diff --git a/src/secret.rs b/src/secret.rs index 5487870..aea6c88 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -119,6 +119,7 @@ impl PartialEq for Secret { } #[cfg(feature = "gen_secret")] +#[cfg_attr(docsrs, doc(cfg(feature = "gen_secret")))] impl Default for Secret { fn default() -> Self { Secret::generate_secret() @@ -158,8 +159,6 @@ impl Secret { } } - /// ⚠️ requires feature `gen_secret`. - /// /// Generate a CSPRNG binary value of 160 bits, /// the recomended size from [rfc-4226](https://www.rfc-editor.org/rfc/rfc4226#section-4). /// @@ -168,6 +167,7 @@ impl Secret { /// /// ⚠️ The generated secret is not guaranteed to be a valid UTF-8 sequence. #[cfg(feature = "gen_secret")] + #[cfg_attr(docsrs, doc(cfg(feature = "gen_secret")))] pub fn generate_secret() -> Secret { use rand::Rng; diff --git a/src/url_error.rs b/src/url_error.rs index fe7184b..7714317 100644 --- a/src/url_error.rs +++ b/src/url_error.rs @@ -8,6 +8,7 @@ use crate::Rfc6238Error; pub enum TotpUrlError { /// Couldn't decode URL. #[cfg(feature = "otpauth")] + #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] Url(ParseError), /// Invalid scheme. Scheme(String), From 4b11a713c811c6e216182e287a3f3135bb4fabb2 Mon Sep 17 00:00:00 2001 From: Anton Maltsev Date: Fri, 19 Jan 2024 18:05:33 +0700 Subject: [PATCH 2/2] Removed doc attribute on private function --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index fd9243b..9976977 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -519,7 +519,6 @@ impl TOTP { /// Parse the TOTP parts from the standard otpauth URL #[cfg(feature = "otpauth")] - #[cfg_attr(docsrs, doc(cfg(feature = "otpauth")))] fn parts_from_url>( url: S, ) -> Result<(Algorithm, usize, u8, u64, Vec, Option, String), TotpUrlError> {