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