blob: a9afaa3e2bb26ed1fb2af8baea8437776db180f4 [file] [log] [blame]
#!/usr/bin/env python3
# coding=utf-8
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import dataclasses
import enum
from typing import Any, Iterator, List, Tuple, Union
class TLVList:
"""Represents a list in CHIP TLV.
A TLVList can be constructed from a `list` of tuples of tag and value. `None` stands for "anonymous tag".
e.g.
```
l = TLVList([(1, 'a'), (2, 'b'), (None, 'c')])
```
Constructs a list of three items, tag 1 is 'a', tag 2 is 'b' and with an anonymous item 'c'.
Since TLVLists are ordered, it is meanful to iterate over an list:
e.g.
```
for tag, val in l:
print(f"tag={tag}, val={val}")
```
Outputs:
```
tag=1, val=a
tag=2, val=b
tag=None, val=c
```
One can also append items into an list:
e.g.
```
l.append(3, 'd')
```
The content of `l` will be `[(1, 'a'), (2, 'b'), (None, 'c'), (3, 'd')]`
One can access an item in the list via the tag.
e.g.
```
val = l[1]
# val is 'a'
```
It is also possible to get an item via the index since it is ordered:
e.g.
```
tag, val = l[TLVList.IndexMethod.Tag:2]
# tag is None, val is 'c'
```
"""
@dataclasses.dataclass
class TLVListItem:
tag: Union[None, int]
value: Any
def as_tuple(self):
return (self.tag, self.value)
def as_rich_repr_tuple(self):
if self.tag is None:
return "Anonymous", repr(self.value)
else:
return str(self.tag), repr(self.value)
def __repr__(self):
if self.tag is None:
return "Anonymous: " + repr(self.value)
else:
return str(self.tag) + ": " + repr(self.value)
def __rich_repr__(self):
yield self.as_rich_repr_tuple()
class IndexMethod(enum.Enum):
Index = 0
Tag = 1
class Iterator:
def __init__(self, iter: Iterator):
self._iterator = iter
def __iter__(self):
return self
def __next__(self):
res = next(self._iterator)
return res.tag, res.value
def __init__(self, items: List[Tuple[Union[int, None], Any]] = []):
"""Constructs a TLVList.
items: A list of tuples for the tag and value for the items in the TLVList.
"""
self._data: List[TLVList.TLVListItem] = []
for tag, val in items:
self.append(tag, val)
def _get_item_by_tag(self, tag) -> Any:
if not isinstance(tag, int):
raise ValueError("Tag should be a integer for non-anonymous fields.")
for data in self._data:
if data.tag == tag:
return data.value
raise KeyError(f"Tag {tag} not found in the list.")
def __getitem__(self, access) -> Any:
"""Gets a item in the list by the tag or the index.
Examples:
```
tlv_list[1] # returns the item in the list with tag `1`
tlv_list[TLVList.IndexMethod.Tag:2] # returns the item in the list with tag `2`
tlv_list[TLVList.IndexMethod.Index:0] # returns the tag and value of the first item in the list
```
"""
if isinstance(access, slice):
tag, index = access.start, access.stop
if tag == TLVList.IndexMethod.Tag:
return self._get_item_by_tag(index)
elif tag == TLVList.IndexMethod.Index:
return self._data[index].as_tuple()
raise ValueError("Method should be TLVList.IndexMethod.Tag or TLVList.IndexMethod.Index")
elif isinstance(access, int):
return self._get_item_by_tag(access)
raise ValueError("Invalid access method")
def append(self, tag: Union[None, int], value: Any) -> None:
"""Appends an item to the list."""
if (tag is not None) and (not isinstance(tag, int)):
raise KeyError(f"Tag should be a integer or none for anonymous tag, {type(tag)} got")
self._data.append(TLVList.TLVListItem(tag, value))
def __repr__(self):
return "TLVList" + repr(self._data)
def __rich_repr__(self):
for items in self._data:
yield items.as_rich_repr_tuple()
def __iter__(self) -> """TLVList.Iterator""":
return TLVList.Iterator(iter(self._data))
def __eq__(self, rhs: "TLVList") -> bool:
if not isinstance(rhs, TLVList):
return False
return self._data == rhs._data