1use core::fmt::{Debug, Formatter};
19use core::marker::PhantomData;
20use core::ops::{Deref, DerefMut};
21
22use crate::interaction_model::core::IMStatusCode;
23use crate::interaction_model::messages::ib::{
24 AttrPath, AttrResp, AttrStatus, CmdDataTag, CmdPath, CmdStatus, InvResp, InvRespTag,
25};
26use crate::tlv::UtfStr;
27use crate::transport::exchange::Exchange;
28use crate::{
29 error::{Error, ErrorCode},
30 interaction_model::messages::ib::{AttrDataTag, AttrRespTag},
31 tlv::{FromTLV, TLVElement, TLVWriter, TagType, ToTLV},
32};
33use log::error;
34
35use super::{AttrDetails, CmdDetails, DataModelHandler};
36
37pub type EncodeValueGen<'a> = &'a dyn Fn(TagType, &mut TLVWriter);
41
42#[derive(Clone)]
43pub enum EncodeValue<'a> {
45 Closure(EncodeValueGen<'a>),
49 Tlv(TLVElement<'a>),
53 Value(&'a dyn ToTLV),
56}
57
58impl<'a> EncodeValue<'a> {
59 pub fn unwrap_tlv(self) -> Option<TLVElement<'a>> {
60 match self {
61 EncodeValue::Tlv(t) => Some(t),
62 _ => None,
63 }
64 }
65}
66
67impl<'a> PartialEq for EncodeValue<'a> {
68 fn eq(&self, other: &Self) -> bool {
69 match self {
70 EncodeValue::Closure(_) => {
71 error!("PartialEq not yet supported");
72 false
73 }
74 EncodeValue::Tlv(a) => {
75 if let EncodeValue::Tlv(b) = other {
76 a == b
77 } else {
78 false
79 }
80 }
81 EncodeValue::Value(_) => {
83 error!("PartialEq not yet supported");
84 false
85 }
86 }
87 }
88}
89
90impl<'a> Debug for EncodeValue<'a> {
91 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
92 match self {
93 EncodeValue::Closure(_) => write!(f, "Contains closure"),
94 EncodeValue::Tlv(t) => write!(f, "{:?}", t),
95 EncodeValue::Value(_) => write!(f, "Contains EncodeValue"),
96 }?;
97 Ok(())
98 }
99}
100
101impl<'a> ToTLV for EncodeValue<'a> {
102 fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
103 match self {
104 EncodeValue::Closure(f) => {
105 (f)(tag_type, tw);
106 Ok(())
107 }
108 EncodeValue::Tlv(_) => panic!("This looks invalid"),
109 EncodeValue::Value(v) => v.to_tlv(tw, tag_type),
110 }
111 }
112}
113
114impl<'a> FromTLV<'a> for EncodeValue<'a> {
115 fn from_tlv(data: &TLVElement<'a>) -> Result<Self, Error> {
116 Ok(EncodeValue::Tlv(data.clone()))
117 }
118}
119
120pub struct AttrDataEncoder<'a, 'b, 'c> {
121 dataver_filter: Option<u32>,
122 path: AttrPath,
123 tw: &'a mut TLVWriter<'b, 'c>,
124}
125
126impl<'a, 'b, 'c> AttrDataEncoder<'a, 'b, 'c> {
127 pub async fn handle_read<T: DataModelHandler>(
128 item: &Result<AttrDetails<'_>, AttrStatus>,
129 handler: &T,
130 tw: &mut TLVWriter<'_, '_>,
131 ) -> Result<bool, Error> {
132 let status = match item {
133 Ok(attr) => {
134 let encoder = AttrDataEncoder::new(attr, tw);
135
136 let result = {
137 #[cfg(not(feature = "nightly"))]
138 {
139 handler.read(attr, encoder)
140 }
141
142 #[cfg(feature = "nightly")]
143 {
144 handler.read(attr, encoder).await
145 }
146 };
147
148 match result {
149 Ok(()) => None,
150 Err(e) => {
151 if e.code() == ErrorCode::NoSpace {
152 return Ok(false);
153 } else {
154 attr.status(e.into())?
155 }
156 }
157 }
158 }
159 Err(status) => Some(status.clone()),
160 };
161
162 if let Some(status) = status {
163 AttrResp::Status(status).to_tlv(tw, TagType::Anonymous)?;
164 }
165
166 Ok(true)
167 }
168
169 pub async fn handle_write<T: DataModelHandler>(
170 item: &Result<(AttrDetails<'_>, TLVElement<'_>), AttrStatus>,
171 handler: &T,
172 tw: &mut TLVWriter<'_, '_>,
173 ) -> Result<(), Error> {
174 let status = match item {
175 Ok((attr, data)) => {
176 let result = {
177 #[cfg(not(feature = "nightly"))]
178 {
179 handler.write(attr, AttrData::new(attr.dataver, data))
180 }
181
182 #[cfg(feature = "nightly")]
183 {
184 handler.write(attr, AttrData::new(attr.dataver, data)).await
185 }
186 };
187
188 match result {
189 Ok(()) => attr.status(IMStatusCode::Success)?,
190 Err(error) => attr.status(error.into())?,
191 }
192 }
193 Err(status) => Some(status.clone()),
194 };
195
196 if let Some(status) = status {
197 status.to_tlv(tw, TagType::Anonymous)?;
198 }
199
200 Ok(())
201 }
202
203 pub fn new(attr: &AttrDetails, tw: &'a mut TLVWriter<'b, 'c>) -> Self {
204 Self {
205 dataver_filter: attr.dataver,
206 path: attr.path(),
207 tw,
208 }
209 }
210
211 pub fn with_dataver(self, dataver: u32) -> Result<Option<AttrDataWriter<'a, 'b, 'c>>, Error> {
212 if self
213 .dataver_filter
214 .map(|dataver_filter| dataver_filter != dataver)
215 .unwrap_or(true)
216 {
217 let mut writer = AttrDataWriter::new(self.tw);
218
219 writer.start_struct(TagType::Anonymous)?;
220 writer.start_struct(TagType::Context(AttrRespTag::Data as _))?;
221 writer.u32(TagType::Context(AttrDataTag::DataVer as _), dataver)?;
222 self.path
223 .to_tlv(&mut writer, TagType::Context(AttrDataTag::Path as _))?;
224
225 Ok(Some(writer))
226 } else {
227 Ok(None)
228 }
229 }
230}
231
232pub struct AttrDataWriter<'a, 'b, 'c> {
233 tw: &'a mut TLVWriter<'b, 'c>,
234 anchor: usize,
235 completed: bool,
236}
237
238impl<'a, 'b, 'c> AttrDataWriter<'a, 'b, 'c> {
239 pub const TAG: TagType = TagType::Context(AttrDataTag::Data as _);
240
241 fn new(tw: &'a mut TLVWriter<'b, 'c>) -> Self {
242 let anchor = tw.get_tail();
243
244 Self {
245 tw,
246 anchor,
247 completed: false,
248 }
249 }
250
251 pub fn set<T: ToTLV>(self, value: T) -> Result<(), Error> {
252 value.to_tlv(self.tw, Self::TAG)?;
253 self.complete()
254 }
255
256 pub fn complete(mut self) -> Result<(), Error> {
257 self.tw.end_container()?;
258 self.tw.end_container()?;
259
260 self.completed = true;
261
262 Ok(())
263 }
264
265 fn reset(&mut self) {
266 self.tw.rewind_to(self.anchor);
267 }
268}
269
270impl<'a, 'b, 'c> Drop for AttrDataWriter<'a, 'b, 'c> {
271 fn drop(&mut self) {
272 if !self.completed {
273 self.reset();
274 }
275 }
276}
277
278impl<'a, 'b, 'c> Deref for AttrDataWriter<'a, 'b, 'c> {
279 type Target = TLVWriter<'b, 'c>;
280
281 fn deref(&self) -> &Self::Target {
282 self.tw
283 }
284}
285
286impl<'a, 'b, 'c> DerefMut for AttrDataWriter<'a, 'b, 'c> {
287 fn deref_mut(&mut self) -> &mut Self::Target {
288 self.tw
289 }
290}
291
292pub struct AttrData<'a> {
293 for_dataver: Option<u32>,
294 data: &'a TLVElement<'a>,
295}
296
297impl<'a> AttrData<'a> {
298 pub fn new(for_dataver: Option<u32>, data: &'a TLVElement<'a>) -> Self {
299 Self { for_dataver, data }
300 }
301
302 pub fn with_dataver(self, dataver: u32) -> Result<&'a TLVElement<'a>, Error> {
303 if let Some(req_dataver) = self.for_dataver {
304 if req_dataver != dataver {
305 Err(ErrorCode::DataVersionMismatch)?;
306 }
307 }
308
309 Ok(self.data)
310 }
311}
312
313#[derive(Default)]
314pub struct CmdDataTracker {
315 skip_status: bool,
316}
317
318impl CmdDataTracker {
319 pub const fn new() -> Self {
320 Self { skip_status: false }
321 }
322
323 pub(crate) fn complete(&mut self) {
324 self.skip_status = true;
325 }
326
327 pub fn needs_status(&self) -> bool {
328 !self.skip_status
329 }
330}
331
332pub struct CmdDataEncoder<'a, 'b, 'c> {
333 tracker: &'a mut CmdDataTracker,
334 path: CmdPath,
335 tw: &'a mut TLVWriter<'b, 'c>,
336}
337
338impl<'a, 'b, 'c> CmdDataEncoder<'a, 'b, 'c> {
339 pub async fn handle<T: DataModelHandler>(
340 item: &Result<(CmdDetails<'_>, TLVElement<'_>), CmdStatus>,
341 handler: &T,
342 tw: &mut TLVWriter<'_, '_>,
343 exchange: &Exchange<'_>,
344 ) -> Result<(), Error> {
345 let status = match item {
346 Ok((cmd, data)) => {
347 let mut tracker = CmdDataTracker::new();
348 let encoder = CmdDataEncoder::new(cmd, &mut tracker, tw);
349
350 let result = {
351 #[cfg(not(feature = "nightly"))]
352 {
353 handler.invoke(exchange, cmd, data, encoder)
354 }
355
356 #[cfg(feature = "nightly")]
357 {
358 handler.invoke(exchange, cmd, data, encoder).await
359 }
360 };
361
362 match result {
363 Ok(()) => cmd.success(&tracker),
364 Err(error) => {
365 error!("Error invoking command: {}", error);
366 cmd.status(error.into())
367 }
368 }
369 }
370 Err(status) => {
371 error!("Error invoking command: {:?}", status);
372 Some(status.clone())
373 }
374 };
375
376 if let Some(status) = status {
377 InvResp::Status(status).to_tlv(tw, TagType::Anonymous)?;
378 }
379
380 Ok(())
381 }
382
383 pub fn new(
384 cmd: &CmdDetails,
385 tracker: &'a mut CmdDataTracker,
386 tw: &'a mut TLVWriter<'b, 'c>,
387 ) -> Self {
388 Self {
389 tracker,
390 path: cmd.path(),
391 tw,
392 }
393 }
394
395 pub fn with_command(mut self, cmd: u16) -> Result<CmdDataWriter<'a, 'b, 'c>, Error> {
396 let mut writer = CmdDataWriter::new(self.tracker, self.tw);
397
398 writer.start_struct(TagType::Anonymous)?;
399 writer.start_struct(TagType::Context(InvRespTag::Cmd as _))?;
400
401 self.path.path.leaf = Some(cmd as _);
402 self.path
403 .to_tlv(&mut writer, TagType::Context(CmdDataTag::Path as _))?;
404
405 Ok(writer)
406 }
407}
408
409pub struct CmdDataWriter<'a, 'b, 'c> {
410 tracker: &'a mut CmdDataTracker,
411 tw: &'a mut TLVWriter<'b, 'c>,
412 anchor: usize,
413 completed: bool,
414}
415
416impl<'a, 'b, 'c> CmdDataWriter<'a, 'b, 'c> {
417 pub const TAG: TagType = TagType::Context(CmdDataTag::Data as _);
418
419 fn new(tracker: &'a mut CmdDataTracker, tw: &'a mut TLVWriter<'b, 'c>) -> Self {
420 let anchor = tw.get_tail();
421
422 Self {
423 tracker,
424 tw,
425 anchor,
426 completed: false,
427 }
428 }
429
430 pub fn set<T: ToTLV>(self, value: T) -> Result<(), Error> {
431 value.to_tlv(self.tw, Self::TAG)?;
432 self.complete()
433 }
434
435 pub fn complete(mut self) -> Result<(), Error> {
436 self.tw.end_container()?;
437 self.tw.end_container()?;
438
439 self.completed = true;
440 self.tracker.complete();
441
442 Ok(())
443 }
444
445 fn reset(&mut self) {
446 self.tw.rewind_to(self.anchor);
447 }
448}
449
450impl<'a, 'b, 'c> Drop for CmdDataWriter<'a, 'b, 'c> {
451 fn drop(&mut self) {
452 if !self.completed {
453 self.reset();
454 }
455 }
456}
457
458impl<'a, 'b, 'c> Deref for CmdDataWriter<'a, 'b, 'c> {
459 type Target = TLVWriter<'b, 'c>;
460
461 fn deref(&self) -> &Self::Target {
462 self.tw
463 }
464}
465
466impl<'a, 'b, 'c> DerefMut for CmdDataWriter<'a, 'b, 'c> {
467 fn deref_mut(&mut self) -> &mut Self::Target {
468 self.tw
469 }
470}
471
472#[derive(Copy, Clone, Debug)]
473pub struct AttrType<T>(PhantomData<fn() -> T>);
474
475impl<T> AttrType<T> {
476 pub const fn new() -> Self {
477 Self(PhantomData)
478 }
479
480 pub fn encode(&self, writer: AttrDataWriter, value: T) -> Result<(), Error>
481 where
482 T: ToTLV,
483 {
484 writer.set(value)
485 }
486
487 pub fn decode<'a>(&self, data: &'a TLVElement) -> Result<T, Error>
488 where
489 T: FromTLV<'a>,
490 {
491 T::from_tlv(data)
492 }
493}
494
495impl<T> Default for AttrType<T> {
496 fn default() -> Self {
497 Self::new()
498 }
499}
500
501#[derive(Copy, Clone, Debug, Default)]
502pub struct AttrUtfType;
503
504impl AttrUtfType {
505 pub const fn new() -> Self {
506 Self
507 }
508
509 pub fn encode(&self, writer: AttrDataWriter, value: &str) -> Result<(), Error> {
510 writer.set(UtfStr::new(value.as_bytes()))
511 }
512
513 pub fn decode<'a>(&self, data: &'a TLVElement) -> Result<&'a str, IMStatusCode> {
514 data.str().map_err(|_| IMStatusCode::InvalidDataType)
515 }
516}
517
518#[allow(unused_macros)]
519#[macro_export]
520macro_rules! attribute_enum {
521 ($en:ty) => {
522 impl core::convert::TryFrom<$crate::data_model::objects::AttrId> for $en {
523 type Error = $crate::error::Error;
524
525 fn try_from(id: $crate::data_model::objects::AttrId) -> Result<Self, Self::Error> {
526 <$en>::from_repr(id)
527 .ok_or_else(|| $crate::error::ErrorCode::AttributeNotFound.into())
528 }
529 }
530 };
531}
532
533#[allow(unused_macros)]
534#[macro_export]
535macro_rules! command_enum {
536 ($en:ty) => {
537 impl core::convert::TryFrom<$crate::data_model::objects::CmdId> for $en {
538 type Error = $crate::error::Error;
539
540 fn try_from(id: $crate::data_model::objects::CmdId) -> Result<Self, Self::Error> {
541 <$en>::from_repr(id).ok_or_else(|| $crate::error::ErrorCode::CommandNotFound.into())
542 }
543 }
544 };
545}