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
|
# this should really be in the stdlib imo
from dataclasses import dataclass
from typing import List as ListT, Tuple, Optional
from .parse_utils import (
Parser, take_while1, tag, delimited, take_n, alt, separated_many0, separated_triple, all_consuming, verify,
preceded, and_then, itag, take_while0, as_predicate
)
__all__ = [
'List',
]
# level 0
ctl: Parser[str] = verify(take_n(1), lambda c: not c.isprintable())
digit: Parser[str] = verify(take_n(1), lambda c: c in '0123456789')
dquote: Parser[str] = tag('"')
list_wildcards: Parser[str] = alt(tag('%'), tag('*'))
nil: Parser[str] = itag('NIL')
resp_specials: Parser[str] = tag(']')
text_char: Parser[str] = verify(take_n(1), lambda c: c not in '\r\n')
# level 1
number: Parser[str] = take_while1(as_predicate(digit))
quoted_specials: Parser[str] = alt(dquote, tag('\\'))
# level 2
atom_specials: Parser[str] = alt(verify(take_n(1), lambda c: c in '(){ '), ctl, list_wildcards, quoted_specials, resp_specials)
literal: Parser[str] = and_then(
delimited(tag('{'), number, tag('}\r\n')),
lambda n: preceded(delimited(tag('{'), number, tag('}\r\n')), take_n(int(n)))
)
quoted_char: Parser[str] = alt(verify(text_char, lambda c: quoted_specials(c) is None), preceded(tag('\\'), quoted_specials))
# level 3
atom_char: Parser[str] = verify(take_n(1), lambda c: atom_specials(c) is None)
quoted: Parser[str] = delimited(dquote, take_while0(as_predicate(quoted_char)), dquote)
# level 4
astring_char: Parser[str] = alt(atom_char, resp_specials)
atom: Parser[str] = take_while1(as_predicate(atom_char))
string: Parser[str] = alt(quoted, literal)
# level 5
astring: Parser[str] = alt(take_while1(as_predicate(astring_char)), string)
flag_extension: Parser[str] = preceded(tag('\\'), atom)
# level 6
mailbox: Parser[str] = alt(itag('INBOX'), astring)
mbx_list_flag: Parser[str] = alt(itag(r'\Noselect'), itag(r'\Marked'), itag(r'\Unmarked'), itag(r'\Noinferiors'), flag_extension)
# level 7
mbx_list_flags: Parser[ListT[str]] = separated_many0(mbx_list_flag, tag(' '))
# level 8
mailbox_list: Parser[Tuple[ListT[str], Optional[str], str]] = separated_triple(
delimited(tag('('), mbx_list_flags, tag(')')),
tag(' '),
alt(delimited(dquote, quoted_char, dquote), nil),
tag(' '),
mailbox,
)
@dataclass
class List:
attributes: ListT[str]
delimiter: Optional[str]
name: str
@staticmethod
def parse(response_bytes: bytes) -> 'List':
response = response_bytes.decode('ASCII')
parser = all_consuming(mailbox_list, debug=True)
parse_result = parser(response)
if parse_result is None:
raise ValueError('invalid List.parse argument {}', repr(response))
(attributes, delimiter, name), _ = parse_result
return List(attributes, delimiter, name)
|