From d29195babd5e0fee2de844c4fedb4a3c94feb64d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 5 Dec 2021 00:42:32 +0000 Subject: [PATCH] remove qitem helper --- actix-http/src/header/shared/extended.rs | 2 +- actix-http/src/header/shared/mod.rs | 2 +- actix-http/src/header/shared/quality_item.rs | 100 +++++++++++++------ 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/actix-http/src/header/shared/extended.rs b/actix-http/src/header/shared/extended.rs index b2cf1d754..60f2d359e 100644 --- a/actix-http/src/header/shared/extended.rs +++ b/actix-http/src/header/shared/extended.rs @@ -1,4 +1,4 @@ -// Originally from hyper v0.11.27 src/header/parsing.rs +//! Originally taken from `hyper::header::parsing`. use std::{fmt, str::FromStr}; diff --git a/actix-http/src/header/shared/mod.rs b/actix-http/src/header/shared/mod.rs index 274e13146..9d7806f53 100644 --- a/actix-http/src/header/shared/mod.rs +++ b/actix-http/src/header/shared/mod.rs @@ -10,5 +10,5 @@ pub use self::charset::Charset; pub use self::content_encoding::ContentEncoding; pub use self::extended::{parse_extended_value, ExtendedValue}; pub use self::http_date::HttpDate; -pub use self::quality_item::{q, qitem, Quality, QualityItem}; +pub use self::quality_item::{q, Quality, QualityItem}; pub use language_tags::LanguageTag; diff --git a/actix-http/src/header/shared/quality_item.rs b/actix-http/src/header/shared/quality_item.rs index a109b44ea..7541caad5 100644 --- a/actix-http/src/header/shared/quality_item.rs +++ b/actix-http/src/header/shared/quality_item.rs @@ -8,29 +8,37 @@ use derive_more::{Display, Error}; use crate::error::ParseError; -const MAX_QUALITY: u16 = 1000; -const MAX_FLOAT_QUALITY: f32 = 1.0; +const MAX_QUALITY_INT: u16 = 1000; +const MAX_QUALITY_FLOAT: f32 = 1.0; -/// Represents a quality used in quality values. +/// Represents a quality used in q-factor values. /// -/// Can be created with the [`q`] function. +/// The default value is [`Quality::MAX`]. /// /// # Implementation notes +/// The quality value is defined as a number between 0 and 1 with three decimal places. This means +/// there are 1001 possible values. Since floating point numbers are not exact and the smallest +/// floating point data type (`f32`) consumes four bytes, we use an `u16` value to store the +/// quality internally. For performance reasons you may set quality directly to a value between 0 +/// and 1000 e.g. `Quality(532)` matches the quality `q=0.532`. /// -/// The quality value is defined as a number between 0 and 1 with three decimal -/// places. This means there are 1001 possible values. Since floating point -/// numbers are not exact and the smallest floating point data type (`f32`) -/// consumes four bytes, hyper uses an `u16` value to store the -/// quality internally. For performance reasons you may set quality directly to -/// a value between 0 and 1000 e.g. `Quality(532)` matches the quality -/// `q=0.532`. +/// [RFC 7231 §5.3.1] gives more information on quality values in HTTP header fields. /// -/// [RFC 7231 §5.3.1](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1) gives more -/// information on quality values in HTTP header fields. +/// [RFC 7231 §5.3.1]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Quality(u16); impl Quality { + /// The maximum quality value, equivalent to `q=1.0`. + pub const MAX: Quality = Quality(MAX_QUALITY_INT); + + /// The minimum quality value, equivalent to `q=0.0`. + pub const MIN: Quality = Quality(0); + + /// Converts a float in the range 0.0–1.0 to a `Quality`. + /// + /// Intentionally private. External uses should rely on the `TryFrom` impl. + /// /// # Panics /// Panics in debug mode when value is not in the range 0.0 <= n <= 1.0. fn from_f32(value: f32) -> Self { @@ -41,24 +49,26 @@ impl Quality { "q value must be between 0.0 and 1.0" ); - Quality((value * MAX_QUALITY as f32) as u16) + Quality((value * MAX_QUALITY_INT as f32) as u16) } } +/// The default value is [`Quality::MAX`]. impl Default for Quality { fn default() -> Quality { - Quality(MAX_QUALITY) + Quality::MAX } } #[derive(Debug, Clone, Display, Error)] +#[non_exhaustive] pub struct QualityOutOfBounds; impl TryFrom for Quality { type Error = QualityOutOfBounds; fn try_from(value: u16) -> Result { - if (0..=MAX_QUALITY).contains(&value) { + if (0..=MAX_QUALITY_INT).contains(&value) { Ok(Quality(value)) } else { Err(QualityOutOfBounds) @@ -70,7 +80,7 @@ impl TryFrom for Quality { type Error = QualityOutOfBounds; fn try_from(value: f32) -> Result { - if (0.0..=MAX_FLOAT_QUALITY).contains(&value) { + if (0.0..=MAX_QUALITY_FLOAT).contains(&value) { Ok(Quality::from_f32(value)) } else { Err(QualityOutOfBounds) @@ -93,9 +103,19 @@ impl QualityItem { /// Constructs a new `QualityItem` from an item and a quality value. /// /// The item can be of any type. The quality should be a value in the range [0, 1]. - pub fn new(item: T, quality: Quality) -> QualityItem { + pub fn new(item: T, quality: Quality) -> Self { QualityItem { item, quality } } + + /// Constructs a new `QualityItem` with from an item, using the maximum q-value. + pub fn max(item: T) -> Self { + Self::new(item, Quality::MAX) + } + + /// Constructs a new `QualityItem` with from an item, using the minimum q-value. + pub fn min(item: T) -> Self { + Self::new(item, Quality::MIN) + } } impl cmp::PartialOrd for QualityItem { @@ -109,7 +129,7 @@ impl fmt::Display for QualityItem { fmt::Display::fmt(&self.item, f)?; match self.quality.0 { - MAX_QUALITY => Ok(()), + MAX_QUALITY_INT => Ok(()), 0 => f.write_str("; q=0"), x => write!(f, "; q=0.{}", format!("{:03}", x).trim_end_matches('0')), } @@ -173,22 +193,42 @@ impl str::FromStr for QualityItem { } } -/// Convenience function to wrap a value in a `QualityItem` -/// Sets `q` to the default 1.0 -pub fn qitem(item: T) -> QualityItem { - QualityItem::new(item, Quality::default()) -} - -/// Convenience function to create a `Quality` from a float or integer. +/// Convenience function to create a [`Quality`] from a `u16` (0–1000) or `f32` (0.0–1.0). /// -/// Implemented for `u16` and `f32`. Panics if value is out of range. -pub fn q(val: T) -> Quality +/// Not recommended for use with user input. Rely on the `TryFrom` impls where possible. +/// +/// # Panics +/// Panics if value is out of range. +/// +/// # Examples +/// ``` +/// # use actix_http::header::{q, Quality}; +/// let q1 = q(1000); +/// assert_eq!(q1, Quality::MAX); +/// +/// let q2 = q(0.0); +/// assert_eq!(q2, Quality::MIN); +/// +/// assert_eq!(q(0.42), q(420)); +/// ``` +/// +/// An out-of-range `u16` quality will panic. +/// ```should_panic +/// # use actix_http::header::q; +/// let _q1 = q(1042); +/// ``` +/// +/// An out-of-range `f32` quality will panic. +/// ```should_panic +/// # use actix_http::header::q; +/// let _q2 = q(1.42); +/// ``` +pub fn q(quality: T) -> Quality where T: TryInto, T::Error: fmt::Debug, { - // TODO: on next breaking change, handle unwrap differently - val.try_into().unwrap() + quality.try_into().expect("quality value was out of bounds") } #[cfg(test)]