"""Path handling""" from .identity import Identity from .types import ALPHA_LOWER, ALPHA_UPPER, DIGIT PATH_PUNCTUATION = "/'()-._~!$&+,:=@%" PATH_CHARACTER = ALPHA_LOWER + ALPHA_UPPER + DIGIT + PATH_PUNCTUATION class Path: """A document path""" _SEGMENT_PATTERN = f'/[{PATH_CHARACTER}]+' _PATTERN = f'^({_SEGMENT_PATTERN})+$' def __init__(self, path: str) -> None: self.validate(path, allow_ephemeral=True) self.path = path @staticmethod def validate(path: str, allow_ephemeral: bool = False) -> None: """Validate a path""" if not 2 <= len(path) <= 512: raise ValueError('Path length must be between 2 and 512') if not path.startswith('/'): raise ValueError('Paths must start with a /') if path.endswith('/'): raise ValueError('Paths must not end with a /') if path.startswith('/@'): raise ValueError('Paths must not start with /@') if '//' in path: raise ValueError('Paths must not contain //') if path.count('!') > 1: raise ValueError('Only one ! is allowed in paths') if '!' in path and not allow_ephemeral: raise ValueError('Only ephemeral paths may contain !') @property def is_shared(self) -> bool: """Check if the path is shared""" return '~' not in self.path @property def is_ephemeral(self) -> bool: """Check if the path is ephemeral""" return '!' in self.path def can_write(self, author: Identity) -> bool: """Check if a specific author has write access over a document""" if self.is_shared: return True segments = self.path.split('/') for segment in segments: for allowed_author in segment.split('~'): if Identity.valid_address(allowed_author) and str(author) == allowed_author: return True return False