chuchi_crypto/token/
mod.rs1#[cfg(feature = "b64")]
2use crate::error::DecodeError;
3use crate::error::TryFromError;
4use crate::utils::SysRngPanic;
5
6use std::convert::{TryFrom, TryInto};
7use std::fmt;
8
9#[cfg(feature = "b64")]
10use base64::Engine;
11#[cfg(feature = "b64")]
12use base64::engine::general_purpose::URL_SAFE_NO_PAD;
13use rand::Rng;
14
15#[derive(Clone, PartialEq, Eq, Hash)]
17pub struct Token<const S: usize> {
18 bytes: [u8; S],
19}
20
21impl<const S: usize> Token<S> {
22 pub const LEN: usize = S;
23
24 pub const STR_LEN: usize = crate::calculate_b64_len(S);
25
26 pub fn new() -> Self {
28 let mut bytes = [0u8; S];
29
30 SysRngPanic.fill_bytes(&mut bytes);
31
32 Self { bytes }
33 }
34
35 pub fn from_slice(slice: &[u8]) -> Self {
38 slice.try_into().unwrap()
39 }
40
41 pub fn to_bytes(&self) -> [u8; S] {
42 self.bytes
43 }
44}
45
46#[cfg(not(feature = "b64"))]
47impl<const S: usize> fmt::Debug for Token<S> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 f.debug_tuple("Token").field(&self.as_ref()).finish()
50 }
51}
52
53#[cfg(feature = "b64")]
54impl<const S: usize> fmt::Debug for Token<S> {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.debug_tuple("Token").field(&self.to_string()).finish()
57 }
58}
59
60#[cfg(feature = "b64")]
61impl<const S: usize> fmt::Display for Token<S> {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 base64::display::Base64Display::new(self.as_ref(), &URL_SAFE_NO_PAD)
64 .fmt(f)
65 }
66}
67
68impl<const S: usize> From<[u8; S]> for Token<S> {
69 fn from(bytes: [u8; S]) -> Self {
70 Self { bytes }
71 }
72}
73
74impl<const S: usize> TryFrom<&[u8]> for Token<S> {
75 type Error = TryFromError;
76
77 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
78 <[u8; S]>::try_from(v)
79 .map_err(TryFromError::from_any)
80 .map(Self::from)
81 }
82}
83
84#[cfg(feature = "b64")]
85impl<const S: usize> crate::FromStr for Token<S> {
86 type Err = DecodeError;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 if s.len() != crate::calculate_b64_len(S) {
90 return Err(DecodeError::InvalidLength);
91 }
92
93 let mut bytes = [0u8; S];
94 URL_SAFE_NO_PAD
95 .decode_slice_unchecked(s, &mut bytes)
96 .map_err(DecodeError::inv_bytes)
97 .map(|_| Self::from(bytes))
98 }
99}
100
101impl<const S: usize> AsRef<[u8]> for Token<S> {
102 fn as_ref(&self) -> &[u8] {
103 &self.bytes
104 }
105}
106
107#[cfg(all(feature = "b64", feature = "serde"))]
108mod impl_serde {
109 use super::*;
110
111 use std::borrow::Cow;
112 use std::str::FromStr;
113
114 use _serde::de::Error;
115 use _serde::{Deserialize, Deserializer, Serialize, Serializer};
116
117 impl<const SI: usize> Serialize for Token<SI> {
118 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119 where
120 S: Serializer,
121 {
122 serializer.collect_str(&self)
123 }
124 }
125
126 impl<'de, const S: usize> Deserialize<'de> for Token<S> {
127 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128 where
129 D: Deserializer<'de>,
130 {
131 let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
132 Self::from_str(s.as_ref()).map_err(D::Error::custom)
133 }
134 }
135}
136
137#[cfg(feature = "protobuf")]
138mod protobuf {
139 use super::*;
140
141 use protopuffer::{
142 WireType,
143 bytes::BytesWrite,
144 decode::{DecodeError, DecodeMessage, FieldKind},
145 encode::{
146 EncodeError, EncodeMessage, FieldOpt, MessageEncoder, SizeBuilder,
147 },
148 };
149
150 impl<const SI: usize> EncodeMessage for Token<SI> {
151 const WIRE_TYPE: WireType = WireType::Len;
152
153 fn is_default(&self) -> bool {
154 false
155 }
156
157 fn encoded_size(
158 &mut self,
159 field: Option<FieldOpt>,
160 builder: &mut SizeBuilder,
161 ) -> Result<(), EncodeError> {
162 self.bytes.encoded_size(field, builder)
163 }
164
165 fn encode<B>(
166 &mut self,
167 field: Option<FieldOpt>,
168 encoder: &mut MessageEncoder<B>,
169 ) -> Result<(), EncodeError>
170 where
171 B: BytesWrite,
172 {
173 self.bytes.encode(field, encoder)
174 }
175 }
176
177 impl<'m, const SI: usize> DecodeMessage<'m> for Token<SI> {
178 const WIRE_TYPE: WireType = WireType::Len;
179
180 fn decode_default() -> Self {
181 [0; SI].into()
182 }
183
184 fn merge(
185 &mut self,
186 kind: FieldKind<'m>,
187 is_field: bool,
188 ) -> Result<(), DecodeError> {
189 self.bytes.merge(kind, is_field)
190 }
191 }
192}
193
194#[cfg(all(feature = "b64", feature = "postgres"))]
195mod impl_postgres {
196 use super::*;
197
198 use bytes::BytesMut;
199 use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
200
201 impl<const SI: usize> ToSql for Token<SI> {
202 fn to_sql(
203 &self,
204 ty: &Type,
205 out: &mut BytesMut,
206 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
207 where
208 Self: Sized,
209 {
210 self.to_string().to_sql(ty, out)
211 }
212
213 fn accepts(ty: &Type) -> bool
214 where
215 Self: Sized,
216 {
217 <&str as ToSql>::accepts(ty)
218 }
219
220 to_sql_checked!();
221 }
222
223 impl<'r, const SI: usize> FromSql<'r> for Token<SI> {
224 fn from_sql(
225 ty: &Type,
226 raw: &'r [u8],
227 ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
228 let s = <&str as FromSql>::from_sql(ty, raw)?;
229 s.parse().map_err(Into::into)
230 }
231
232 fn accepts(ty: &Type) -> bool {
233 <&str as FromSql>::accepts(ty)
234 }
235 }
236}
237
238#[cfg(all(test, feature = "b64"))]
239mod tests {
240
241 use super::*;
242
243 use std::str::FromStr;
244
245 pub fn b64<const S: usize>() {
246 let tok = Token::<S>::new();
247
248 let b64 = tok.to_string();
249 let tok_2 = Token::<S>::from_str(&b64).unwrap();
250
251 assert_eq!(b64, tok_2.to_string());
252 }
253
254 #[test]
255 pub fn test_b64() {
256 b64::<1>();
257 b64::<2>();
258 b64::<3>();
259 b64::<13>();
260 b64::<24>();
261 b64::<200>();
262 b64::<213>();
263 }
264}