rs_matter/data_model/objects/
encoder.rs

1/*
2 *
3 *    Copyright (c) 2020-2022 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use 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
37// TODO: Should this return an IMStatusCode Error? But if yes, the higher layer
38// may have already started encoding the 'success' headers, we might not want to manage
39// the tw.rewind() in that case, if we add this support
40pub type EncodeValueGen<'a> = &'a dyn Fn(TagType, &mut TLVWriter);
41
42#[derive(Clone)]
43/// A structure for encoding various types of values
44pub enum EncodeValue<'a> {
45    /// This indicates a value that is dynamically generated. This variant
46    /// is typically used in the transmit/to-tlv path where we want to encode a value at
47    /// run time
48    Closure(EncodeValueGen<'a>),
49    /// This indicates a value that is in the TLVElement form. this variant is
50    /// typically used in the receive/from-tlv path where we don't want to decode the
51    /// full value but it can be done at the time of its usage
52    Tlv(TLVElement<'a>),
53    /// This indicates a static value. This variant is typically used in the transmit/
54    /// to-tlv path
55    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            // Just claim false for now
82            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}