actix_web/http/header/
preference.rs

1use std::{
2    fmt::{self, Write as _},
3    str,
4};
5
6/// A wrapper for types used in header values where wildcard (`*`) items are allowed but the
7/// underlying type does not support them.
8///
9/// For example, we use the `language-tags` crate for the [`AcceptLanguage`](super::AcceptLanguage)
10/// typed header but it does not parse `*` successfully. On the other hand, the `mime` crate, used
11/// for [`Accept`](super::Accept), has first-party support for wildcard items so this wrapper is not
12/// used in those header types.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)]
14pub enum Preference<T> {
15    /// A wildcard value.
16    Any,
17
18    /// A valid `T`.
19    Specific(T),
20}
21
22impl<T> Preference<T> {
23    /// Returns true if preference is the any/wildcard (`*`) value.
24    pub fn is_any(&self) -> bool {
25        matches!(self, Self::Any)
26    }
27
28    /// Returns true if preference is the specific item (`T`) variant.
29    pub fn is_specific(&self) -> bool {
30        matches!(self, Self::Specific(_))
31    }
32
33    /// Returns reference to value in `Specific` variant, if it is set.
34    pub fn item(&self) -> Option<&T> {
35        match self {
36            Preference::Specific(ref item) => Some(item),
37            Preference::Any => None,
38        }
39    }
40
41    /// Consumes the container, returning the value in the `Specific` variant, if it is set.
42    pub fn into_item(self) -> Option<T> {
43        match self {
44            Preference::Specific(item) => Some(item),
45            Preference::Any => None,
46        }
47    }
48}
49
50impl<T: fmt::Display> fmt::Display for Preference<T> {
51    #[inline]
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Preference::Any => f.write_char('*'),
55            Preference::Specific(item) => fmt::Display::fmt(item, f),
56        }
57    }
58}
59
60impl<T: str::FromStr> str::FromStr for Preference<T> {
61    type Err = T::Err;
62
63    #[inline]
64    fn from_str(s: &str) -> Result<Self, Self::Err> {
65        match s.trim() {
66            "*" => Ok(Self::Any),
67            other => other.parse().map(Preference::Specific),
68        }
69    }
70}