"""
Define portable machine names.
"""
from __future__ import annotations
import re
from typing import TypeAlias, TypeGuard, Self, Any
from betty.assertion import assert_str, AssertionChain
from betty.error import UserFacingError
from betty.locale.localizable import _
MachineName: TypeAlias = str
"""
A machine name is a string that meets these criteria:
- At most 250 characters long.
- Lowercase letters, numbers, and hyphens (-).
See :py:func:`betty.machine_name.validate_machine_name`.
"""
_MACHINE_NAME_PATTERN = re.compile(r"^[a-z0-9\-]{1,250}$")
[docs]
def validate_machine_name(alleged_machine_name: str) -> TypeGuard[MachineName]:
"""
Validate that a string is a machine name.
"""
return _MACHINE_NAME_PATTERN.fullmatch(alleged_machine_name) is not None
[docs]
class InvalidMachineName(UserFacingError, ValueError):
"""
Raised when something is not a valid machine name.
"""
[docs]
@classmethod
def new(cls, value: str) -> Self:
"""
Create a new instance.
"""
return cls(
_(
'"{value}" is not a valid machine name: only lowercase letters, digits, and hyphens (-) are allowed.'
).format(value=value)
)
[docs]
def assert_machine_name() -> AssertionChain[Any, MachineName]:
"""
Assert that something is a machine name.
"""
def _assert(value: Any) -> MachineName:
if not validate_machine_name(value):
raise InvalidMachineName.new(value)
return value
return assert_str() | _assert