mail-address-semantics/mas_core/generator.py

39 lines
1.6 KiB
Python
Raw Normal View History

"""Canonical MAS v0.1 local-part generator.
Generators are stricter than parsers: they never emit both a facet and an
interaction, and they enforce the 64-octet SMTP limit after encoding.
"""
from __future__ import annotations
from typing import Optional
from .parser import MasSyntaxError, parse_local_part
def generate_local_part(target: str, *, facet: Optional[str] = None,
interaction: Optional[str] = None) -> str:
if facet is not None and interaction is not None:
raise MasSyntaxError("generator-facet-and-interaction",
"v0.1 generators MUST NOT emit both '+' and '!'")
s = target
if facet is not None:
s += "+" + facet
if interaction is not None:
s += "!" + interaction
parsed = parse_local_part(s) # enforces grammar, emptiness, reserved ops, length
if str(parsed.target) != target or (parsed.facet and parsed.facet.value) != facet \
or (parsed.interaction and parsed.interaction.mode) != interaction:
raise MasSyntaxError("generator-component", "a component contains a MAS operator")
return s
def resource_local_part(resource_type: str, resource_id: str, *, facet: Optional[str] = None,
interaction: Optional[str] = None) -> str:
if not resource_type or not resource_id:
raise MasSyntaxError("generator-empty", "resource type and id must be non-empty")
if "=" in resource_type:
raise MasSyntaxError("invalid-resource-type", "resource type must not contain '='")
return generate_local_part(f"{resource_type}={resource_id}", facet=facet,
interaction=interaction)