use coap_message::{MessageOption, ReadableMessage};
use coap_numbers::option;
pub trait TryFromOption: Sized {
fn try_from(value: &impl MessageOption) -> Option<Self>;
}
struct BlockDataWithoutM {
blknum: u32,
szx: u8,
}
struct BlockDataWithM {
coordinates: BlockDataWithoutM,
m: bool,
}
impl core::ops::Deref for BlockDataWithM {
type Target = BlockDataWithoutM;
fn deref(&self) -> &BlockDataWithoutM {
&self.coordinates
}
}
pub struct Block1Data(BlockDataWithM);
pub struct Block2RequestData(BlockDataWithoutM);
#[derive(Debug)]
pub struct BadBlock2Option;
const M_BIT: u32 = 0x08;
const SZX_MASK: u32 = 0x07;
const BLOCK_MAX: u32 = 0xffffff;
impl BlockDataWithoutM {
fn from_u32(o: u32) -> Option<Self> {
if o > BLOCK_MAX {
return None;
}
Some(Self {
szx: (o & SZX_MASK) as u8,
blknum: o >> 4,
})
}
fn to_u32(&self) -> u32 {
(self.blknum << 4) | self.szx as u32
}
pub fn size(&self) -> u16 {
1 << (4 + self.szx)
}
pub fn start(&self) -> u32 {
self.size() as u32 * self.blknum
}
}
impl BlockDataWithM {
fn from_u32(o: u32) -> Option<Self> {
Some(Self {
coordinates: BlockDataWithoutM::from_u32(o)?,
m: o & M_BIT != 0,
})
}
fn to_u32(&self) -> u32 {
self.coordinates.to_u32() | if self.m { M_BIT } else { 0 }
}
}
impl Block2RequestData {
pub fn from_message(message: &impl ReadableMessage) -> Result<Self, BadBlock2Option> {
let mut b2options = message.options().filter(|o| o.number() == option::BLOCK2);
match b2options.next() {
None => Ok(Self::default()),
Some(o) => {
if b2options.next().is_none() {
Self::from_option(&o)
} else {
Err(BadBlock2Option)
}
}
}
}
pub fn from_option(option: &impl MessageOption) -> Result<Self, BadBlock2Option> {
debug_assert!(option.number() == option::BLOCK2);
let o: u32 = option.value_uint().ok_or(BadBlock2Option)?;
BlockDataWithoutM::from_u32(o)
.map(Self)
.ok_or(BadBlock2Option)
}
pub fn to_option_value(&self, more: bool) -> u32 {
self.0.to_u32() | if more { 0x08 } else { 0 }
}
pub fn size(&self) -> u16 {
self.0.size()
}
pub fn start(&self) -> usize {
self.0.start() as _
}
pub fn shrink(mut self, size: u16) -> Option<Self> {
while self.size() > size {
if self.0.szx == 0 {
return None;
}
self.0.szx -= 1;
self.0.blknum *= 2;
}
Some(self)
}
}
impl Block1Data {
pub fn start(&self) -> usize {
self.0.start() as _
}
pub fn more(&self) -> bool {
self.0.m
}
pub fn to_option_value(&self) -> u32 {
self.0.to_u32()
}
}
impl Default for Block2RequestData {
fn default() -> Self {
Self(BlockDataWithoutM { szx: 6, blknum: 0 })
}
}
impl TryFromOption for Block2RequestData {
fn try_from(value: &impl MessageOption) -> Option<Self> {
if value.number() != coap_numbers::option::BLOCK2 {
return None;
}
Self::from_option(value).ok()
}
}
impl TryFromOption for Block1Data {
fn try_from(o: &impl MessageOption) -> Option<Self> {
if o.number() != coap_numbers::option::BLOCK1 {
return None;
}
Some(Self(BlockDataWithM::from_u32(o.value_uint()?)?))
}
}