foundation_urtypes/
value.rs1use core::fmt::{Display, Formatter};
30
31use minicbor::{bytes::ByteSlice, encode::Write, Encode, Encoder};
32
33use crate::registry::{HDKeyRef, PassportRequest, PassportResponse};
34
35#[derive(Debug, PartialEq)]
36pub enum Value<'a> {
37 Bytes(&'a [u8]),
39 HDKey(HDKeyRef<'a>),
41 Psbt(&'a [u8]),
43 PassportRequest(PassportRequest),
45 PassportResponse(PassportResponse<'a>),
47}
48
49impl<'a> Value<'a> {
50 pub fn from_ur(ur_type: &str, payload: &'a [u8]) -> Result<Self, Error> {
52 let value = match ur_type {
53 "bytes" => Self::Bytes(minicbor::decode::<&ByteSlice>(payload)?),
54 "hdkey" | "crypto-hdkey" => Self::HDKey(minicbor::decode(payload)?),
55 "psbt" | "crypto-psbt" => Self::Psbt(minicbor::decode::<&ByteSlice>(payload)?),
56 "x-passport-request" | "crypto-request" => {
59 Self::PassportRequest(minicbor::decode(payload)?)
60 }
61 "x-passport-response" | "crypto-response" => {
62 Self::PassportResponse(minicbor::decode(payload)?)
63 }
64 _ => return Err(Error::UnsupportedResource),
65 };
66
67 Ok(value)
68 }
69
70 pub fn ur_type(&self) -> &'static str {
80 match self {
81 Value::Bytes(_) => "bytes",
82 Value::HDKey(_) => "hdkey",
83 Value::Psbt(_) => "crypto-psbt",
84 Value::PassportRequest(_) => "crypto-request",
85 Value::PassportResponse(_) => "crypto-response",
86 }
87 }
88}
89
90impl<'a, C> Encode<C> for Value<'a> {
91 fn encode<W: Write>(
92 &self,
93 e: &mut Encoder<W>,
94 ctx: &mut C,
95 ) -> Result<(), minicbor::encode::Error<W::Error>> {
96 match self {
97 Value::Bytes(v) => minicbor::bytes::encode(v, e, ctx),
98 Value::HDKey(v) => v.encode(e, ctx),
99 Value::Psbt(v) => minicbor::bytes::encode(v, e, ctx),
100 Value::PassportRequest(v) => v.encode(e, ctx),
101 Value::PassportResponse(v) => v.encode(e, ctx),
102 }
103 }
104}
105
106#[derive(Debug)]
108pub enum Error {
109 UnsupportedResource,
111 InvalidCbor(minicbor::decode::Error),
113}
114
115impl Display for Error {
116 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
117 match self {
118 Self::UnsupportedResource => write!(f, "unsupported Uniform Resource type"),
119 Self::InvalidCbor(_) => write!(f, "failed to decode CBOR payload"),
120 }
121 }
122}
123
124#[cfg(feature = "std")]
125impl std::error::Error for Error {
126 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
127 match self {
128 Self::InvalidCbor(e) => Some(e),
129 _ => None,
130 }
131 }
132}
133
134impl From<minicbor::decode::Error> for Error {
135 fn from(error: minicbor::decode::Error) -> Self {
136 Self::InvalidCbor(error)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_byte_string_bytes() {
146 const BYTES_PAYLOAD: &[u8] = &[
147 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
148 0xEE, 0xFF,
149 ];
150 const CBOR_PAYLOAD: &[u8] = &[
151 0x50, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC,
152 0xDD, 0xEE, 0xFF,
153 ];
154
155 let value = Value::from_ur("bytes", CBOR_PAYLOAD).unwrap();
156 assert_eq!(value, Value::Bytes(BYTES_PAYLOAD));
157
158 let cbor = minicbor::to_vec(&value).unwrap();
159 assert_eq!(cbor, CBOR_PAYLOAD);
160 }
161}