pkce_std/
method.rs

1//! PKCE code challenge methods.
2//!
3//! As per the [standard](https://datatracker.ietf.org/doc/html/rfc7636#section-4.2),
4//! there are two methods: plain and SHA-256.
5//!
6//! The former is discouraged and is only used as the last resort,
7//! while the latter is recommended and is marked as the default.
8//!
9//! # Examples
10//!
11//! ```
12//! use pkce_std::method::Method;
13//!
14//! let string = "S256";
15//!
16//! let method: Method = string.parse().unwrap();
17//!
18//! assert_eq!(method, Method::Sha256);
19//! ```
20
21use std::str::FromStr;
22
23#[cfg(feature = "diagnostics")]
24use miette::Diagnostic;
25
26#[cfg(feature = "serde")]
27use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
28
29use thiserror::Error;
30
31/// Represents errors that can occur when parsing PKCE methods.
32#[derive(Debug, Error)]
33#[error("unknown method `{unknown}`")]
34#[cfg_attr(
35    feature = "diagnostics",
36    derive(Diagnostic),
37    diagnostic(
38        code(pkce_std::method),
39        help("expected either `{PLAIN}` (discouraged) or `{SHA256}` (recommended)")
40    )
41)]
42pub struct Error {
43    /// The unknown method.
44    pub unknown: String,
45}
46
47impl Error {
48    /// Constructs [`Self`].
49    pub const fn new(unknown: String) -> Self {
50        Self { unknown }
51    }
52}
53
54/// The `plain` literal.
55pub const PLAIN: &str = "plain";
56
57/// The `S256` literal.
58pub const SHA256: &str = "S256";
59
60/// Represents PKCE code challenge methods.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
62pub enum Method {
63    /// The plain method, which is discouraged and only used as the last resort.
64    Plain,
65    /// The SHA-256 method, which is recommended and marked as the default.
66    #[default]
67    Sha256,
68}
69
70#[cfg(feature = "serde")]
71impl Serialize for Method {
72    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
73        self.static_str().serialize(serializer)
74    }
75}
76
77#[cfg(feature = "serde")]
78impl<'de> Deserialize<'de> for Method {
79    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
80        let string = <&str>::deserialize(deserializer)?;
81
82        string.parse().map_err(de::Error::custom)
83    }
84}
85
86type StaticStr = &'static str;
87
88impl Method {
89    /// Returns the static string representation of the method.
90    pub const fn static_str(&self) -> StaticStr {
91        match self {
92            Self::Plain => PLAIN,
93            Self::Sha256 => SHA256,
94        }
95    }
96}
97
98impl FromStr for Method {
99    type Err = Error;
100
101    fn from_str(string: &str) -> Result<Self, Self::Err> {
102        match string {
103            PLAIN => Ok(Self::Plain),
104            SHA256 => Ok(Self::Sha256),
105            _ => Err(Self::Err::new(string.to_owned())),
106        }
107    }
108}