# 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)