1use dcbor::prelude::*;
2use ur::decode;
3
4use crate::{Error, Result, URType};
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct UR {
9 ur_type: URType,
10 cbor: CBOR,
11}
12
13impl UR {
14 pub fn new(
16 ur_type: impl TryInto<URType, Error = Error>,
17 cbor: impl Into<CBOR>,
18 ) -> Result<UR> {
19 let ur_type = ur_type.try_into()?;
20 let cbor = cbor.into();
21 Ok(UR { ur_type, cbor })
22 }
23
24 pub fn from_ur_string(ur_string: impl Into<String>) -> Result<UR> {
26 let ur_string = ur_string.into().to_lowercase();
27 let strip_scheme =
28 ur_string.strip_prefix("ur:").ok_or(Error::InvalidScheme)?;
29 let (ur_type, _) =
30 strip_scheme.split_once('/').ok_or(Error::TypeUnspecified)?;
31 let ur_type = URType::new(ur_type)?;
32 let a = decode(&ur_string);
33 let (kind, data) = a.map_err(Error::UR)?;
34 if kind != ur::ur::Kind::SinglePart {
35 return Err(Error::NotSinglePart);
36 }
37 let cbor = CBOR::try_from_data(data)?;
38 Ok(UR { ur_type, cbor })
39 }
40
41 pub fn string(&self) -> String {
43 let data = self.cbor.to_cbor_data();
44 ur::encode(&data, &ur::Type::Custom(self.ur_type.string()))
45 }
46
47 pub fn qr_string(&self) -> String { self.string().to_uppercase() }
50
51 pub fn qr_data(&self) -> Vec<u8> { self.qr_string().as_bytes().to_vec() }
54
55 pub fn check_type(
57 &self,
58 other_type: impl TryInto<URType, Error = Error>,
59 ) -> Result<()> {
60 let other_type = other_type.try_into()?;
61 if self.ur_type != other_type {
62 Err(Error::UnexpectedType(
63 other_type.string().to_string(),
64 self.ur_type.string().to_string(),
65 ))?;
66 }
67 Ok(())
68 }
69
70 pub fn ur_type(&self) -> &URType { &self.ur_type }
71
72 pub fn ur_type_str(&self) -> &str { self.ur_type.string() }
74
75 pub fn cbor(&self) -> CBOR { self.cbor.clone() }
76}
77
78impl From<UR> for CBOR {
79 fn from(ur: UR) -> Self { ur.cbor }
80}
81
82impl From<UR> for String {
83 fn from(ur: UR) -> Self { ur.string() }
84}
85
86impl TryFrom<String> for UR {
87 type Error = Error;
88
89 fn try_from(value: String) -> Result<Self> { UR::from_ur_string(value) }
90}
91
92impl std::fmt::Display for UR {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(f, "{}", self.string())
95 }
96}
97
98impl AsRef<UR> for UR {
99 fn as_ref(&self) -> &UR { self }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_ur() {
108 let cbor: CBOR = vec![1, 2, 3].into();
109 let ur = UR::new("test", cbor.clone()).unwrap();
110 let ur_string = ur.string();
111 assert_eq!(ur_string, "ur:test/lsadaoaxjygonesw");
112 let ur = UR::from_ur_string(ur_string).unwrap();
113 assert_eq!(ur.ur_type_str(), "test");
114 assert_eq!(&ur.cbor, &cbor);
115
116 let caps_ur_string = "UR:TEST/LSADAOAXJYGONESW";
117 let ur = UR::from_ur_string(caps_ur_string).unwrap();
118 assert_eq!(ur.ur_type_str(), "test");
119 assert_eq!(&ur.cbor, &cbor);
120 }
121}