summaryrefslogtreecommitdiff
path: root/python/ovs/flow/list.py
blob: bc466ef89f08a2b4fd89c67d1a85ebd9837ad915 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import re

from ovs.flow.kv import KeyValue, KeyMetadata, ParseError
from ovs.flow.decoders import decode_default


class ListDecoders(object):
    """ListDecoders is used by ListParser to decode the elements in the list.

    A decoder is a function that accepts a value and returns its decoded
    object.

    ListDecoders is initialized with a list of tuples that contains the
    keyword and the decoding function associated with each position in the
    list. The order is, therefore, important.

    Args:
        decoders (list of tuples): Optional; a list of tuples.
            The first element in the tuple is the keyword associated with the
            value. The second element in the tuple is the decoder function.
    """

    def __init__(self, decoders=None):
        self._decoders = decoders or list()

    def decode(self, index, value_str):
        """Decode the index'th element of the list.

        Args:
            index (int): The position in the list of the element to decode.
            value_str (str): The value string to decode.
        """
        if index < 0 or index >= len(self._decoders):
            if self._default_decoder:
                return self._default_decoder(index, value_str)
            else:
                raise ParseError(
                    f"Cannot decode element {index} in list: {value_str}"
                )

        try:
            key = self._decoders[index][0]
            value = self._decoders[index][1](value_str)
            return key, value
        except Exception as e:
            raise ParseError(
                "Failed to decode value_str {}: {}".format(value_str, str(e))
            )

    @staticmethod
    def _default_decoder(index, value):
        key = "elem_{}".format(index)
        return key, decode_default(value)


class ListParser(object):
    """ListParser parses a list of values and stores them as key-value pairs.

    It uses a ListDecoders instance to decode each element in the list.

    Args:
        string (str): The string to parse.
        decoders (ListDecoders): Optional, the decoders to use.
        delims (list): Optional, list of delimiters of the list. Defaults to
            [','].
    """
    def __init__(self, string, decoders=None, delims=[","]):
        self._string = string
        self._decoders = decoders or ListDecoders()
        self._keyval = list()
        self._regexp = r"({})".format("|".join(delims))

    def kv(self):
        return self._keyval

    def __iter__(self):
        return iter(self._keyval)

    def parse(self):
        """Parse the list in string.

        Raises:
            ParseError if any parsing error occurs.
        """
        kpos = 0
        index = 0
        while kpos < len(self._string) and self._string[kpos] != "\n":
            split_parts = re.split(self._regexp, self._string[kpos:], 1)
            value_str = split_parts[0]

            key, value = self._decoders.decode(index, value_str)

            meta = KeyMetadata(
                kpos=kpos,
                vpos=kpos,
                kstring=value_str,
                vstring=value_str,
            )
            self._keyval.append(KeyValue(key, value, meta))

            kpos += len(value_str) + 1
            index += 1


def decode_nested_list(decoders, value, delims=[","]):
    """Decodes a string value that contains a list of elements and returns
    them in a dictionary.

    Args:
        decoders (ListDecoders): The ListDecoders to use.
        value (str): The value string to decode.
        delims (list(str)): Optional, the list of delimiters to use.
    """
    parser = ListParser(value, decoders, delims)
    parser.parse()
    return {kv.key: kv.value for kv in parser.kv()}


def nested_list_decoder(decoders=None, delims=[","]):
    """Helper function that creates a nested list decoder with given
    ListDecoders and delimiters.
    """
    def decoder(value):
        return decode_nested_list(decoders, value, delims)

    return decoder