1use std::fmt;
2use std::str::FromStr;
3
4use base64::Engine;
5use base64::engine::general_purpose::URL_SAFE_NO_PAD;
6
7use crate::error::{Error, Result};
8use crate::limits::{NAME_LEN, NAME_TEXT_LEN};
9
10#[derive(Clone, Copy, PartialEq, Eq, Hash)]
12pub struct Name([u8; NAME_TEXT_LEN]);
13
14impl Name {
15 pub fn as_str(&self) -> &str {
17 std::str::from_utf8(&self.0).expect("base64url is ASCII")
18 }
19}
20
21impl FromStr for Name {
22 type Err = Error;
23
24 fn from_str(name: &str) -> Result<Self> {
26 if name.len() != NAME_TEXT_LEN {
27 return Err(Error::Identity);
28 }
29 let mut material = [0u8; NAME_LEN];
30 let n = URL_SAFE_NO_PAD
31 .decode_slice(name.as_bytes(), &mut material)
32 .map_err(|_| Error::Identity)?;
33 if n != NAME_LEN {
34 return Err(Error::Identity);
35 }
36 let mut out = [0u8; NAME_TEXT_LEN];
37 out.copy_from_slice(name.as_bytes());
38 Ok(Self(out))
39 }
40}
41
42impl fmt::Debug for Name {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 f.debug_tuple("Name").field(&self.as_str()).finish()
45 }
46}
47
48impl fmt::Display for Name {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str(self.as_str())
51 }
52}
53
54impl AsRef<str> for Name {
55 fn as_ref(&self) -> &str {
56 self.as_str()
57 }
58}
59
60pub(crate) fn encode(material: [u8; NAME_LEN]) -> Name {
62 let mut out = [0u8; NAME_TEXT_LEN];
63 let n = URL_SAFE_NO_PAD
64 .encode_slice(material, &mut out)
65 .expect("16 bytes encode to 22 base64url characters");
66 debug_assert_eq!(n, NAME_TEXT_LEN);
67 Name(out)
68}