1#[cfg(feature = "auth")]
6use miette::Diagnostic;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[cfg(feature = "auth")]
12use thiserror::Error;
13
14use crate::{base::Base, hotp::Hotp, otp::type_of::Type, totp::Totp};
15
16#[cfg(feature = "auth")]
17use crate::{
18 auth::{query::Query, url::Url},
19 hotp, totp,
20};
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
26pub enum Otp<'o> {
27 Hotp(Hotp<'o>),
29 Totp(Totp<'o>),
31}
32
33impl<'o> Otp<'o> {
34 pub const fn base(&self) -> &Base<'o> {
36 match self {
37 Self::Hotp(hotp) => hotp.base(),
38 Self::Totp(totp) => totp.base(),
39 }
40 }
41
42 pub fn base_mut(&mut self) -> &mut Base<'o> {
44 match self {
45 Self::Hotp(hotp) => hotp.base_mut(),
46 Self::Totp(totp) => totp.base_mut(),
47 }
48 }
49
50 pub fn into_base(self) -> Base<'o> {
52 match self {
53 Self::Hotp(hotp) => hotp.into_base(),
54 Self::Totp(totp) => totp.into_base(),
55 }
56 }
57}
58
59impl Otp<'_> {
60 pub const fn type_of(&self) -> Type {
62 match self {
63 Self::Hotp(_) => Type::Hotp,
64 Self::Totp(_) => Type::Totp,
65 }
66 }
67}
68
69#[cfg(feature = "auth")]
71#[derive(Debug, Error, Diagnostic)]
72#[error(transparent)]
73#[diagnostic(transparent)]
74pub enum ErrorSource {
75 Hotp(#[from] hotp::Error),
77 Totp(#[from] totp::Error),
79}
80
81#[cfg(feature = "auth")]
83#[derive(Debug, Error, Diagnostic)]
84#[error("failed to extract OTP from URL")]
85#[diagnostic(code(otp_std::otp), help("see the report for more information"))]
86pub struct Error {
87 #[source]
89 #[diagnostic_source]
90 pub source: ErrorSource,
91}
92
93#[cfg(feature = "auth")]
94impl Error {
95 pub const fn new(source: ErrorSource) -> Self {
97 Self { source }
98 }
99
100 pub fn hotp(error: hotp::Error) -> Self {
102 Self::new(error.into())
103 }
104
105 pub fn totp(error: totp::Error) -> Self {
107 Self::new(error.into())
108 }
109}
110
111#[cfg(feature = "auth")]
112impl Otp<'_> {
113 pub fn query_for(&self, url: &mut Url) {
115 match self {
116 Self::Hotp(hotp) => hotp.query_for(url),
117 Self::Totp(totp) => totp.query_for(url),
118 }
119 }
120
121 pub fn extract_from(query: &mut Query<'_>, type_of: Type) -> Result<Self, Error> {
127 match type_of {
128 Type::Hotp => Hotp::extract_from(query)
129 .map(Self::Hotp)
130 .map_err(Error::hotp),
131
132 Type::Totp => Totp::extract_from(query)
133 .map(Self::Totp)
134 .map_err(Error::totp),
135 }
136 }
137}
138
139impl<'h> From<Hotp<'h>> for Otp<'h> {
140 fn from(hotp: Hotp<'h>) -> Self {
141 Self::Hotp(hotp)
142 }
143}
144
145impl<'t> From<Totp<'t>> for Otp<'t> {
146 fn from(totp: Totp<'t>) -> Self {
147 Self::Totp(totp)
148 }
149}
150
151pub type Owned = Otp<'static>;
153
154impl Otp<'_> {
155 pub fn into_owned(self) -> Owned {
157 match self {
158 Self::Hotp(hotp) => Owned::Hotp(hotp.into_owned()),
159 Self::Totp(totp) => Owned::Totp(totp.into_owned()),
160 }
161 }
162}