Completed
Pull Request — master (#42)
by Frederik
02:26
created

ItemList::withOctet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Imap\MessageData;
5
6
use Genkgo\Mail\Protocol\Imap\MessageData\Item\NameItem;
7
use Genkgo\Mail\Protocol\Imap\MessageData\Item\PartialItem;
8
use Genkgo\Mail\Protocol\Imap\MessageData\Item\SectionItem;
9
10
final class ItemList
11
{
12
    /**
13
     *
14
     */
15
    private CONST STATE_NONE = 0;
16
    /**
17
     *
18
     */
19
    private CONST STATE_NAME = 1;
20
    /**
21
     *
22
     */
23
    private CONST STATE_SECTION = 2;
24
    /**
25
     *
26
     */
27
    private CONST STATE_PARTIAL = 3;
28
    /**
29
     *
30
     */
31
    private CONST STATE_OCTET = 4;
32
    /**
33
     * @var array
34
     */
35
    private $list = [];
36
    /**
37
     * @var int
38
     */
39
    private $size;
40
    /**
41
     * @var string
42
     */
43
    private $body;
44
45
    /**
46
     * ItemList constructor.
47
     * @param array $list
48
     */
49 22
    public function __construct(array $list = [])
50
    {
51 22
        $this->list = $list;
52 22
    }
53
54
    /**
55
     * @param ItemInterface $item
56
     * @return ItemList
57
     */
58 17
    public function withItem(ItemInterface $item): self
59
    {
60 17
        $clone = clone $this;
61 17
        $clone->list[$item->getName()] = $item;
62 17
        return $clone;
63
    }
64
65
    /**
66
     * @param int $size
67
     * @return ItemList
68
     */
69 6
    public function withOctet(int $size): self
70
    {
71 6
        $clone = clone $this;
72 6
        $clone->size = $size;
73 6
        return $clone;
74
    }
75
76
    /**
77
     * @param string $body
78
     * @return ItemList
79
     */
80 5
    public function withBody(string $body): self
81
    {
82 5
        $clone = clone $this;
83 5
        $clone->body = $body;
84 5
        return $clone;
85
    }
86
87
    /**
88
     * @return string
89
     */
90 1
    public function getBody(): string
91
    {
92 1
        return $this->body;
93
    }
94
95
    /**
96
     * @param string $name
97
     * @return ItemInterface
98
     */
99 5
    public function getItem(string $name): ItemInterface
100
    {
101 5
        if (!isset($this->list[$name])) {
102 1
            throw new \UnexpectedValueException(
103 1
                sprintf('Unknown name %s', $name)
104
            );
105
        }
106
107 4
        return $this->list[$name];
108
    }
109
110
    /**
111
     * @return ItemInterface
112
     */
113 6
    public function last(): ItemInterface
114
    {
115 6
        if (empty($this->list)) {
116 1
            throw new \OutOfBoundsException('Cannot return last item from empty list');
117
        }
118
119 5
        return end($this->list);
120
    }
121
122
    /**
123
     * @return string
124
     */
125 6
    public function __toString(): string
126
    {
127 6
        $items = implode(
128 6
            ' ',
129 6
            array_map(
130 6
                function (ItemInterface $item) {
131 6
                    return (string)$item;
132 6
                },
133 6
                $this->list
134
            )
135
        );
136
137 6
        if ($this->size) {
138 3
            $items .= ' {' . $this->size . '}';
139
        }
140
141 6
        if ($this->body) {
142 2
            $items .= "\n" . $this->body;
143
        }
144
145 6
        if (count($this->list) > 1 || $this->body) {
146 3
            return '(' . $items . ')';
147
        }
148
149 3
        return $items;
150
    }
151
152
    /**
153
     * @param string $serializedList
154
     * @return ItemList
155
     */
156 13
    public static function fromString(string $serializedList): self
157
    {
158 13
        $list = new self();
159
160 13
        $index = 0;
161 13
        $state = self::STATE_NAME;
162 13
        $sequence = '';
163
164 13
        if ($serializedList === '') {
165 1
            throw new \InvalidArgumentException('Cannot create list from empty string');
166
        }
167
168 12
        if ($serializedList[0] === '(' && $serializedList[-1] === ')') {
169 3
            $serializedList = substr($serializedList, 1, -1);
170
        }
171
172 12
        while (isset($serializedList[$index])) {
173 12
            $char = $serializedList[$index];
174 12
            $sequence .= $char;
175
176
            switch ($char) {
177 12
                case '[':
178 9
                    if ($state !== self::STATE_NAME) {
179 1
                        throw new \InvalidArgumentException('Invalid character [ found');
180
                    }
181
182 9
                    $list = $list->withItem(new NameItem(substr($sequence, 0, -1)));
183 9
                    $sequence = '[';
184 9
                    $state = self::STATE_SECTION;
185 9
                    break;
186 12
                case ']':
187 5
                    if ($state !== self::STATE_SECTION) {
188 1
                        throw new \InvalidArgumentException('Invalid character ] found');
189
                    }
190
191 4
                    $list = $list->withItem(
192 4
                        new SectionItem($list->last(), SectionList::fromString($sequence))
193
                    );
194
195 4
                    $sequence = '';
196 4
                    $state = self::STATE_NAME;
197 4
                    break;
198 12
                case '<':
199 2
                    if ($state !== self::STATE_NAME) {
200 1
                        throw new \InvalidArgumentException('Invalid character < found');
201
                    }
202
203 1
                    $state = self::STATE_PARTIAL;
204 1
                    break;
205 12
                case '>':
206 2
                    if ($state !== self::STATE_PARTIAL) {
207 1
                        throw new \InvalidArgumentException('Invalid character > found');
208
                    }
209
210 1
                    $list = $list->withItem(
211 1
                        new PartialItem($list->last(), Partial::fromString($sequence))
212
                    );
213
214 1
                    $sequence = '';
215 1
                    $state = self::STATE_NAME;
216 1
                    break;
217 12
                case '{':
218 4
                    if ($state !== self::STATE_NONE) {
219 1
                        throw new \InvalidArgumentException('Invalid character { found');
220
                    }
221
222 3
                    $state = self::STATE_OCTET;
223 3
                    break;
224 12
                case '}':
225 4
                    if ($state !== self::STATE_OCTET) {
226 1
                        throw new \InvalidArgumentException('Invalid characters } found');
227
                    }
228
229 3
                    $list = $list->withOctet((int)substr($sequence, 1, -1));
230 3
                    $sequence = '';
231
232 3
                    $state = self::STATE_NAME;
233 3
                    break;
234 12
                case ' ':
235 4
                    if ($sequence === ' ') {
236 3
                        $state = self::STATE_NONE;
237
                    }
238
239 4
                    if ($state === self::STATE_NONE) {
240 3
                        $sequence = '';
241
                    }
242
243 4
                    if ($state === self::STATE_NAME) {
244 1
                        $list = $list->withItem(new NameItem(substr($sequence, 0, -1)));
245 1
                        $sequence = '';
246 1
                        $state = self::STATE_NONE;
247
                    }
248
249 4
                    break;
250 12
                case "\n":
251 3
                    $list = $list->withBody(substr($serializedList, $index + 1));
252 3
                    $sequence = '';
253 3
                    break 2;
254
            }
255
256 12
            $index++;
257
        }
258
259 6
        if ($sequence) {
260 2
            $list = $list->withItem(new NameItem($sequence));
261
        }
262
263 6
        return $list;
264
    }
265
}