Source code for pymap.parsing.specials.objectid


from __future__ import annotations

import binascii
import random as _random
import re
from typing import Any, AnyStr

from .. import Params, Parseable
from ..exceptions import NotParseable

__all__ = ['ObjectId']


[docs] class ObjectId(Parseable[bytes]): """Represents an object ID, used to identify a mailbox, message, or thread. An *object_id* value of ``None`` is a special case indicating that this object ID is not defined. Args: object_id: The object ID bytestring, or ``None``. See Also: `RFC 8474 <https://tools.ietf.org/html/rfc8474>`_ """ _pattern = re.compile(br'[a-zA-Z0-9_-]{1,255}') def __init__(self, object_id: bytes | None = None) -> None: super().__init__() if object_id == b'': raise ValueError(object_id) self.object_id = object_id @property def value(self) -> bytes: """The object ID value.""" if self.object_id is None: raise ValueError(self.object_id) return self.object_id @property def parens(self) -> bytes: """The object ID value surrounded by parentheses.""" return b'(%b)' % (self.value, )
[docs] @classmethod def parse(cls, buf: memoryview, params: Params) \ -> tuple[ObjectId, memoryview]: start = cls._whitespace_length(buf) match = cls._pattern.match(buf, start) if not match: raise NotParseable(buf) return cls(match.group(0)), buf[match.end(0):]
[docs] @classmethod def new(cls, prefix: bytes, digest: bytes) -> ObjectId: """Return a new object ID from a hash digest. Args: prefix: The prefix for the object ID. digest: The hash digest. """ return cls(prefix + binascii.hexlify(digest))
[docs] @classmethod def new_mailbox_id(cls, digest: bytes) -> ObjectId: """Return a new mailbox ID from a hash digest. Args: digest: The hash digest. """ return cls.new(b'F', digest)
[docs] @classmethod def new_email_id(cls, digest: bytes) -> ObjectId: """Return a new email ID from a hash digest. Args: digest: The hash digest. """ return cls.new(b'M', digest)
[docs] @classmethod def new_thread_id(cls, digest: bytes) -> ObjectId: """Return a new thread ID from a hash digest. Args: digest: The hash digest. """ return cls.new(b'T', digest)
[docs] @classmethod def random(cls, prefix: bytes) -> ObjectId: """Return a new randomized object ID. Args: prefix: The prefix for the object ID. """ return cls(b'%b%032x' % (prefix, _random.getrandbits(128)))
[docs] @classmethod def random_mailbox_id(cls) -> ObjectId: """Return a new randomized mailbox ID.""" return cls.random(b'F')
[docs] @classmethod def random_email_id(cls) -> ObjectId: """Return a new randomized email ID.""" return cls.random(b'M')
[docs] @classmethod def random_thread_id(cls) -> ObjectId: """Return a new randomized thread ID.""" return cls.random(b'T')
[docs] @classmethod def maybe(cls, value: AnyStr | None) -> ObjectId: """Return an object ID representing the string or bytestring value. If the input is empty or ``None``, the object ID returned will have :attr:`.not_defined` be true. Args: value: The object ID string or bytestring. """ if not value: return cls(None) elif isinstance(value, str): return cls(value.encode('ascii', 'ignore')) else: return cls(value)
def __eq__(self, other: Any) -> bool: if isinstance(other, ObjectId): return self.object_id == other.object_id elif isinstance(other, bytes): return self.object_id == other return super().__eq__(other) def __hash__(self) -> int: return hash(self.object_id) def __str__(self) -> str: try: return self.parens.decode('ascii') except ValueError: return 'NIL' def __repr__(self) -> str: return '<ObjectId {0!s}>'.format(self) def __bytes__(self) -> bytes: return self.value