Skip to main content

chuchi_crypto/token/
mod.rs

1#[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/// A random Token
16#[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	/// Creates a new random Token
27	pub fn new() -> Self {
28		let mut bytes = [0u8; S];
29
30		SysRngPanic.fill_bytes(&mut bytes);
31
32		Self { bytes }
33	}
34
35	/// ## Panics
36	/// if the slice is not `S` bytes long.
37	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}