Commit aad5f9aaccebfbe11afb7f30e7cae80e7f0f0493

Cleo Rebert 2023-07-15T19:28:06

Remove unnecessary alloc and add docs.

diff --git a/Cargo.toml b/Cargo.toml
index 2ebc348..181bea1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "totp-rs"
-version = "5.0.2"
+version = "5.1.0"
 authors = ["Cleo Rebert <cleo.rebert@gmail.com>"]
 rust-version = "1.61"
 edition = "2021"
diff --git a/README.md b/README.md
index 868bc07..b5c0262 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string())
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies]
-totp-rs = "^4.0"
+totp-rs = "^5.0"
 ```
 You can then do something like:
 ```Rust
@@ -92,7 +92,7 @@ fn main() {
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies.totp-rs]
-version = "^4.0"
+version = "^5.0"
 features = ["qr"]
 ```
 You can then do something like:
@@ -119,7 +119,7 @@ fn main() {
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies.totp-rs]
-version = "^4.0"
+version = "^5.0"
 features = ["serde_support"]
 ```
 
@@ -128,7 +128,7 @@ features = ["serde_support"]
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies.totp-rs]
-version = "^4.0"
+version = "^5.0"
 features = ["otpauth"]
 ```
 You can then do something like:
@@ -147,7 +147,7 @@ fn main() {
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies.totp-rs]
-version = "^4.0"
+version = "^5.0"
 features = ["gen_secret"]
 ```
 You can then do something like:
@@ -223,7 +223,7 @@ fn main() {
 Add it to your `Cargo.toml`:
 ```toml
 [dependencies.totp-rs]
-version = "^4.1"
+version = "^5.0"
 features = ["qr"]
 ```
 You can then do something like:
diff --git a/src/lib.rs b/src/lib.rs
index 0526c53..ebae025 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -698,8 +698,8 @@ impl TOTP {
     /// 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
     #[cfg(feature = "qr")]
     pub fn get_qr(&self) -> Result<String, String> {
+        use base64::{engine::general_purpose, Engine as _};
         use image::ImageEncoder;
-        use base64::{Engine as _, engine::general_purpose};
 
         let url = self.get_url();
         let mut vec = Vec::new();
diff --git a/src/rfc.rs b/src/rfc.rs
index 30ba33a..28cf83e 100644
--- a/src/rfc.rs
+++ b/src/rfc.rs
@@ -5,12 +5,12 @@ use crate::TOTP;
 #[cfg(feature = "serde_support")]
 use serde::{Deserialize, Serialize};
 
-/// Data is not compliant to [rfc-6238](https://tools.ietf.org/html/rfc6238)
+/// Error returned when input is not compliant to [rfc-6238](https://tools.ietf.org/html/rfc6238).
 #[derive(Debug, Eq, PartialEq)]
 pub enum Rfc6238Error {
-    /// Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code
+    /// Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code.
     InvalidDigits(usize),
-    /// The length of the shared secret MUST be at least 128 bits
+    /// The length of the shared secret MUST be at least 128 bits.
     SecretTooSmall(usize),
 }
 
@@ -69,13 +69,13 @@ pub fn assert_secret_length(secret: &[u8]) -> Result<(), Rfc6238Error> {
 pub struct Rfc6238 {
     /// SHA-1
     algorithm: Algorithm,
-    /// The number of digits composing the auth code. Per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-5.3), this can oscilate between 6 and 8 digits
+    /// The number of digits composing the auth code. Per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-5.3), this can oscilate between 6 and 8 digits.
     digits: usize,
     /// The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 1.
     skew: u8,
-    /// The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 30 seconds
+    /// The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 30 seconds.
     step: u64,
-    /// 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
+    /// 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<u8>,
     #[cfg(feature = "otpauth")]
     /// The "Github" part of "Github:constantoine@github.com". Must not contain a colon `:`
@@ -83,19 +83,19 @@ pub struct Rfc6238 {
     /// Not mandatory, but strongly recommended!
     issuer: Option<String>,
     #[cfg(feature = "otpauth")]
-    /// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:`
+    /// 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,
 }
 
 impl Rfc6238 {
-    /// Create an [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options that can be turned into a [TOTP](struct.TOTP.html)
+    /// Create an [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options that can be turned into a [TOTP](struct.TOTP.html).
     ///
     /// # Errors
     ///
     /// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
-    /// - `digits` is lower than 6 or higher than 8
-    /// - `secret` is smaller than 128 bits (16 characters)
+    /// - `digits` is lower than 6 or higher than 8.
+    /// - `secret` is smaller than 128 bits (16 characters).
     #[cfg(feature = "otpauth")]
     pub fn new(
         digits: usize,
@@ -131,13 +131,13 @@ impl Rfc6238 {
     }
 
     /// Create an [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options that can be turned into a [TOTP](struct.TOTP.html),
-    /// with a default value of 6 for `digits`, None `issuer` and an empty account
+    /// with a default value of 6 for `digits`, None `issuer` and an empty account.
     ///
     /// # Errors
     ///
     /// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
-    /// - `digits` is lower than 6 or higher than 8
-    /// - `secret` is smaller than 128 bits (16 characters)
+    /// - `digits` is lower than 6 or higher than 8.
+    /// - `secret` is smaller than 128 bits (16 characters).
     #[cfg(feature = "otpauth")]
     pub fn with_defaults(secret: Vec<u8>) -> Result<Rfc6238, Rfc6238Error> {
         Rfc6238::new(6, secret, Some("".to_string()), "".to_string())
@@ -148,7 +148,7 @@ impl Rfc6238 {
         Rfc6238::new(6, secret)
     }
 
-    /// Set the `digits`
+    /// Set the `digits`.
     pub fn digits(&mut self, value: usize) -> Result<(), Rfc6238Error> {
         assert_digits(&value)?;
         self.digits = value;
@@ -156,13 +156,13 @@ impl Rfc6238 {
     }
 
     #[cfg(feature = "otpauth")]
-    /// Set the `issuer`
+    /// Set the `issuer`.
     pub fn issuer(&mut self, value: String) {
         self.issuer = Some(value);
     }
 
     #[cfg(feature = "otpauth")]
-    /// Set the `account_name`
+    /// Set the `account_name`.
     pub fn account_name(&mut self, value: String) {
         self.account_name = value;
     }
@@ -172,7 +172,7 @@ impl Rfc6238 {
 impl TryFrom<Rfc6238> for TOTP {
     type Error = TotpUrlError;
 
-    /// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config
+    /// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config.
     fn try_from(rfc: Rfc6238) -> Result<Self, Self::Error> {
         TOTP::new(rfc.algorithm, rfc.digits, rfc.skew, rfc.step, rfc.secret)
     }
@@ -182,7 +182,7 @@ impl TryFrom<Rfc6238> for TOTP {
 impl TryFrom<Rfc6238> for TOTP {
     type Error = TotpUrlError;
 
-    /// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config
+    /// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config.
     fn try_from(rfc: Rfc6238) -> Result<Self, Self::Error> {
         TOTP::new(
             rfc.algorithm,
diff --git a/src/secret.rs b/src/secret.rs
index 4fbdfc1..f9ac152 100644
--- a/src/secret.rs
+++ b/src/secret.rs
@@ -81,8 +81,10 @@ use base32::{self, Alphabet};
 
 use constant_time_eq::constant_time_eq;
 
+/// Different ways secret parsing failed.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum SecretParseError {
+    /// Invalid base32 input.
     ParseBase32,
 }
 
@@ -96,18 +98,19 @@ impl std::fmt::Display for SecretParseError {
 
 impl std::error::Error for Secret {}
 
+/// Shared secret between client and server to validate token against/generate token from.
 #[derive(Debug, Clone, Eq)]
 #[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
 pub enum Secret {
-    /// represent a non-encoded "raw" secret
+    /// Non-encoded "raw" secret.
     Raw(Vec<u8>),
-    /// represent a base32 encoded secret
+    /// Base32 encoded secret.
     Encoded(String),
 }
 
 impl PartialEq for Secret {
-    /// Will check that to_bytes() returns the same
-    /// One secret can be Raw, and the other Encoded
+    /// Will check that to_bytes() returns the same.
+    /// One secret can be Raw, and the other Encoded.
     fn eq(&self, other: &Self) -> bool {
         constant_time_eq(&self.to_bytes().unwrap(), &other.to_bytes().unwrap())
     }
@@ -121,7 +124,7 @@ impl Default for Secret {
 }
 
 impl Secret {
-    /// Get the inner String value as a Vec of bytes
+    /// Get the inner String value as a Vec of bytes.
     pub fn to_bytes(&self) -> Result<Vec<u8>, SecretParseError> {
         match self {
             Secret::Raw(s) => Ok(s.to_vec()),
@@ -143,7 +146,7 @@ impl Secret {
         }
     }
 
-    /// Try to transforms a `Secret::Raw` into a `Secret::Encoded`
+    /// Try to transforms a `Secret::Raw` into a `Secret::Encoded`.
     pub fn to_encoded(&self) -> Self {
         match self {
             Secret::Raw(s) => {
@@ -153,15 +156,15 @@ impl Secret {
         }
     }
 
-    /// ⚠️ requires feature `gen_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)
+    /// the recomended size from [rfc-4226](https://www.rfc-editor.org/rfc/rfc4226#section-4).
     ///
     /// > The length of the shared secret MUST be at least 128 bits.
     /// > This document RECOMMENDs a shared secret length of 160 bits.
     ///
-    /// ⚠️ The generated secret is not guaranteed to be a valid UTF-8 sequence
+    /// ⚠️ The generated secret is not guaranteed to be a valid UTF-8 sequence.
     #[cfg(feature = "gen_secret")]
     pub fn generate_secret() -> Secret {
         use rand::Rng;
@@ -177,11 +180,10 @@ impl std::fmt::Display for Secret {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Secret::Raw(bytes) => {
-                let mut s: String = String::new();
                 for b in bytes {
-                    s = format!("{}{:02x}", &s, &b);
+                    write!(f, "{:02x}", b)?;
                 }
-                write!(f, "{}", s)
+                Ok(())
             }
             Secret::Encoded(s) => write!(f, "{}", s),
         }
diff --git a/src/url_error.rs b/src/url_error.rs
index 3d63de2..fe7184b 100644
--- a/src/url_error.rs
+++ b/src/url_error.rs
@@ -3,22 +3,37 @@ use url::ParseError;
 
 use crate::Rfc6238Error;
 
+/// Errors returned mostly upon decoding URL.
 #[derive(Debug, Eq, PartialEq)]
 pub enum TotpUrlError {
+    /// Couldn't decode URL.
     #[cfg(feature = "otpauth")]
     Url(ParseError),
+    /// Invalid scheme.
     Scheme(String),
+    /// Invalid host.
     Host(String),
+    /// Wrong base32 input.
     Secret(String),
+    /// Invalid secret size. (Too short?)
     SecretSize(usize),
+    /// Unknown algorithm.
     Algorithm(String),
+    /// Characters should only be digits.
     Digits(String),
+    /// Digits should be between 6 and 8.
     DigitsNumber(usize),
+    /// Couldn't decode step into a number.
     Step(String),
+    /// Issuer contains invalid character `:`.
     Issuer(String),
+    /// Couldn't decode issuer.
     IssuerDecoding(String),
+    /// Issuers should be the same.
     IssuerMistmatch(String, String),
+    /// Account name contains invalid character `:` or couldn't be decoded.
     AccountName(String),
+    /// Couldn't parse account name.
     AccountNameDecoding(String),
 }