use crate::error::Error;
use crate::im::encoding::{AttrDataTag, AttrPath, AttrRespTag, CmdDataTag, CmdPath, CmdRespTag};
use crate::tlv::{TLVTag, TLVWrite, TagType, ToTLV};
use super::{AttrDetails, CmdDetails};
pub trait Reply {
const TAG: TagType;
fn set<T: ToTLV>(self, value: T) -> Result<(), Error>;
fn tag(&self) -> &'static TagType {
&Self::TAG
}
fn reset(&mut self);
fn writer(&mut self) -> impl TLVWrite + Send + '_;
fn complete(self) -> Result<(), Error>;
}
pub trait ReadReply {
fn with_dataver(self, dataver: u32) -> Result<Option<impl Reply>, Error>;
}
pub trait InvokeReply {
fn with_command(self, cmd: u32) -> Result<impl Reply, Error>;
}
pub struct ReadReplyInstance<T> {
dataver_filter: Option<u32>,
path: AttrPath,
tw: T,
}
impl<T> ReadReplyInstance<T>
where
T: TLVWrite,
{
pub fn new(attr: &AttrDetails, tw: T) -> Self {
Self {
dataver_filter: attr.dataver,
path: attr.reply_path(),
tw,
}
}
}
impl<T> ReadReply for ReadReplyInstance<T>
where
T: TLVWrite + Send,
{
fn with_dataver(self, dataver: u32) -> Result<Option<impl Reply>, Error> {
if self
.dataver_filter
.map(|dataver_filter| dataver_filter != dataver)
.unwrap_or(true)
{
let mut writer = AttrReadReplyInstance::new(self.tw);
let mut tw = writer.writer();
tw.start_struct(&TLVTag::Anonymous)?;
tw.start_struct(&TLVTag::Context(AttrRespTag::Data as _))?;
tw.u32(&TLVTag::Context(AttrDataTag::DataVer as _), dataver)?;
self.path
.to_tlv(&TagType::Context(AttrDataTag::Path as _), tw)?;
Ok(Some(writer))
} else {
Ok(None)
}
}
}
pub(crate) struct AttrReadReplyInstance<T>
where
T: TLVWrite,
{
anchor: T::Position,
tw: T,
}
impl<T> AttrReadReplyInstance<T>
where
T: TLVWrite,
{
pub(crate) const TAG: TLVTag = TLVTag::Context(AttrDataTag::Data as _);
fn new(tw: T) -> Self {
Self {
anchor: tw.get_tail(),
tw,
}
}
}
impl<T> Reply for AttrReadReplyInstance<T>
where
T: TLVWrite + Send,
{
const TAG: TagType = Self::TAG;
fn set<P: ToTLV>(mut self, value: P) -> Result<(), Error> {
value.to_tlv(&Self::TAG, &mut self.tw)?;
self.complete()
}
fn complete(mut self) -> Result<(), Error> {
self.tw.end_container()?;
self.tw.end_container()?;
Ok(())
}
fn writer(&mut self) -> impl TLVWrite + Send + '_ {
&mut self.tw
}
fn reset(&mut self) {
self.tw.rewind_to(self.anchor);
}
}
pub struct InvokeReplyInstance<T> {
path: CmdPath,
command_ref: Option<u16>,
tw: T,
}
impl<T> InvokeReplyInstance<T>
where
T: TLVWrite,
{
pub const fn new(cmd: &CmdDetails, tw: T) -> Self {
Self {
path: cmd.reply_path(),
command_ref: cmd.command_ref,
tw,
}
}
}
impl<T> InvokeReply for InvokeReplyInstance<T>
where
T: TLVWrite + Send,
{
fn with_command(mut self, cmd: u32) -> Result<impl Reply, Error> {
let mut writer = CmdInvokeReplyInstance::new(self.tw, self.command_ref);
let mut tw = writer.writer();
tw.start_struct(&TLVTag::Anonymous)?;
tw.start_struct(&TLVTag::Context(CmdRespTag::Cmd as _))?;
self.path.cmd = Some(cmd as _);
self.path
.to_tlv(&TagType::Context(CmdDataTag::Path as _), tw)?;
Ok(writer)
}
}
struct CmdInvokeReplyInstance<T>
where
T: TLVWrite,
{
anchor: T::Position,
command_ref: Option<u16>,
tw: T,
}
impl<T> CmdInvokeReplyInstance<T>
where
T: TLVWrite,
{
const TAG: TagType = TagType::Context(CmdDataTag::Data as _);
fn new(tw: T, command_ref: Option<u16>) -> Self {
Self {
anchor: tw.get_tail(),
command_ref,
tw,
}
}
}
impl<T> Reply for CmdInvokeReplyInstance<T>
where
T: TLVWrite + Send,
{
const TAG: TagType = Self::TAG;
fn set<P: ToTLV>(mut self, value: P) -> Result<(), Error> {
value.to_tlv(&Self::TAG, &mut self.tw)?;
self.complete()
}
fn complete(mut self) -> Result<(), Error> {
if let Some(command_ref) = self.command_ref {
self.tw
.u16(&TLVTag::Context(CmdDataTag::CommandRef as _), command_ref)?;
}
self.tw.end_container()?;
self.tw.end_container()?;
Ok(())
}
fn writer(&mut self) -> impl TLVWrite + Send + '_ {
&mut self.tw
}
fn reset(&mut self) {
self.tw.rewind_to(self.anchor);
}
}