use core::fmt::{Display, Formatter};
use minicbor::{bytes::ByteSlice, encode::Write, Encode, Encoder};
use crate::registry::{HDKeyRef, PassportRequest, PassportResponse};
#[derive(Debug, PartialEq)]
pub enum Value<'a> {
Bytes(&'a [u8]),
HDKey(HDKeyRef<'a>),
Psbt(&'a [u8]),
PassportRequest(PassportRequest),
PassportResponse(PassportResponse<'a>),
}
impl<'a> Value<'a> {
pub fn from_ur(ur_type: &str, payload: &'a [u8]) -> Result<Self, Error> {
let value = match ur_type {
"bytes" => Self::Bytes(minicbor::decode::<&ByteSlice>(payload)?),
"hdkey" | "crypto-hdkey" => Self::HDKey(minicbor::decode(payload)?),
"psbt" | "crypto-psbt" => Self::Psbt(minicbor::decode::<&ByteSlice>(payload)?),
"x-passport-request" | "crypto-request" => {
Self::PassportRequest(minicbor::decode(payload)?)
}
"x-passport-response" | "crypto-response" => {
Self::PassportResponse(minicbor::decode(payload)?)
}
_ => return Err(Error::UnsupportedResource),
};
Ok(value)
}
pub fn ur_type(&self) -> &'static str {
match self {
Value::Bytes(_) => "bytes",
Value::HDKey(_) => "hdkey",
Value::Psbt(_) => "crypto-psbt",
Value::PassportRequest(_) => "crypto-request",
Value::PassportResponse(_) => "crypto-response",
}
}
}
impl<'a, C> Encode<C> for Value<'a> {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
Value::Bytes(v) => minicbor::bytes::encode(v, e, ctx),
Value::HDKey(v) => v.encode(e, ctx),
Value::Psbt(v) => minicbor::bytes::encode(v, e, ctx),
Value::PassportRequest(v) => v.encode(e, ctx),
Value::PassportResponse(v) => v.encode(e, ctx),
}
}
}
#[derive(Debug)]
pub enum Error {
UnsupportedResource,
InvalidCbor(minicbor::decode::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnsupportedResource => write!(f, "unsupported Uniform Resource type"),
Self::InvalidCbor(_) => write!(f, "failed to decode CBOR payload"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InvalidCbor(e) => Some(e),
_ => None,
}
}
}
impl From<minicbor::decode::Error> for Error {
fn from(error: minicbor::decode::Error) -> Self {
Self::InvalidCbor(error)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_string_bytes() {
const BYTES_PAYLOAD: &[u8] = &[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF,
];
const CBOR_PAYLOAD: &[u8] = &[
0x50, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC,
0xDD, 0xEE, 0xFF,
];
let value = Value::from_ur("bytes", CBOR_PAYLOAD).unwrap();
assert_eq!(value, Value::Bytes(BYTES_PAYLOAD));
let cbor = minicbor::to_vec(&value).unwrap();
assert_eq!(cbor, CBOR_PAYLOAD);
}
}