"""Immutable syntax model for MAS v0.1 addresses. These objects carry syntactic facts only. Nothing here encodes a semantic kind (person, actor, function): that requires statute or registry metadata. """ from __future__ import annotations from dataclasses import dataclass from enum import Enum from typing import Optional, Union class RequiredProfile(str, Enum): """The minimum MAS profile whose grammar the syntax requires.""" P1 = "P1" P2 = "P2" @dataclass(frozen=True) class OpaqueTarget: value: str kind = "opaque" def __str__(self) -> str: return self.value @dataclass(frozen=True) class ResourceTarget: resource_type: str resource_id: str kind = "resource" def __str__(self) -> str: return f"{self.resource_type}={self.resource_id}" Target = Union[OpaqueTarget, ResourceTarget] @dataclass(frozen=True) class Facet: value: str def __str__(self) -> str: return self.value @dataclass(frozen=True) class Interaction: mode: str def __str__(self) -> str: return self.mode @dataclass(frozen=True) class ParsedLocalPart: """A canonical MAS v0.1 local-part, parsed with the maximal (P2) grammar.""" raw: str target: Target facet: Optional[Facet] = None interaction: Optional[Interaction] = None @property def required_profile(self) -> RequiredProfile: return RequiredProfile.P2 if self.interaction is not None else RequiredProfile.P1 @property def base(self) -> str: """The base target: what remains if a mail system strips the facet.""" return str(self.target) @property def generator_allowed(self) -> bool: """v0.1 generators MUST NOT emit both a facet and an interaction.""" return not (self.facet is not None and self.interaction is not None) def without_facet(self) -> "ParsedLocalPart": return ParsedLocalPart(raw=_render(self.target, None, self.interaction), target=self.target, interaction=self.interaction) def to_fixture_dict(self) -> dict: """Shape used by the conformance corpus (`parsed` objects).""" out = { "target": str(self.target), "target_kind": self.target.kind, "facet": self.facet.value if self.facet else None, "interaction": self.interaction.mode if self.interaction else None, } if isinstance(self.target, ResourceTarget): out["resource_type"] = self.target.resource_type out["resource_id"] = self.target.resource_id return out def to_dict(self) -> dict: out = self.to_fixture_dict() out["required_profile"] = self.required_profile.value out["base"] = self.base out["generator_allowed"] = self.generator_allowed return out @dataclass(frozen=True) class ParsedAddress: local: ParsedLocalPart domain: str @property def required_profile(self) -> RequiredProfile: return self.local.required_profile def __str__(self) -> str: return f"{self.local.raw}@{self.domain}" def to_dict(self) -> dict: return {"address": str(self), "domain": self.domain, **self.local.to_dict()} def _render(target: Target, facet: Optional[Facet], interaction: Optional[Interaction]) -> str: s = str(target) if facet is not None: s += "+" + facet.value if interaction is not None: s += "!" + interaction.mode return s