108 lines
2.4 KiB
Python
108 lines
2.4 KiB
Python
"""
|
|
©AngelaMos | 2026
|
|
encoders.py
|
|
"""
|
|
|
|
import base64 as b64
|
|
import binascii
|
|
from collections.abc import Callable
|
|
from urllib.parse import (
|
|
quote,
|
|
quote_plus,
|
|
unquote,
|
|
unquote_plus,
|
|
)
|
|
from base64_tool.constants import EncodingFormat
|
|
|
|
|
|
type EncoderFn = Callable[[bytes], str]
|
|
type DecoderFn = Callable[[str], bytes]
|
|
|
|
|
|
def encode_base64(data: bytes) -> str:
|
|
return b64.b64encode(data).decode("ascii")
|
|
|
|
|
|
def decode_base64(data: str) -> bytes:
|
|
cleaned = "".join(data.split())
|
|
return b64.b64decode(cleaned, validate=True)
|
|
|
|
|
|
def encode_base64url(data: bytes) -> str:
|
|
return b64.urlsafe_b64encode(data).decode("ascii")
|
|
|
|
|
|
def decode_base64url(data: str) -> bytes:
|
|
cleaned = "".join(data.split())
|
|
return b64.urlsafe_b64decode(cleaned)
|
|
|
|
|
|
def encode_base32(data: bytes) -> str:
|
|
return b64.b32encode(data).decode("ascii")
|
|
|
|
|
|
def decode_base32(data: str) -> bytes:
|
|
cleaned = "".join(data.split()).upper()
|
|
return b64.b32decode(cleaned)
|
|
|
|
|
|
def encode_hex(data: bytes) -> str:
|
|
return data.hex()
|
|
|
|
|
|
def decode_hex(data: str) -> bytes:
|
|
cleaned = data.strip()
|
|
for sep in (" ", ":", "-", "."):
|
|
cleaned = cleaned.replace(sep, "")
|
|
return bytes.fromhex(cleaned)
|
|
|
|
|
|
def encode_url(data: bytes, *, form: bool = False) -> str:
|
|
text = data.decode("utf-8")
|
|
if form:
|
|
return quote_plus(text)
|
|
return quote(text, safe="")
|
|
|
|
|
|
def decode_url(data: str, *, form: bool = False) -> bytes:
|
|
if form:
|
|
return unquote_plus(data).encode("utf-8")
|
|
return unquote(data).encode("utf-8")
|
|
|
|
|
|
ENCODER_REGISTRY: dict[
|
|
EncodingFormat,
|
|
tuple[EncoderFn, DecoderFn],
|
|
] = {
|
|
EncodingFormat.BASE64: (encode_base64, decode_base64),
|
|
EncodingFormat.BASE64URL: (encode_base64url, decode_base64url),
|
|
EncodingFormat.BASE32: (encode_base32, decode_base32),
|
|
EncodingFormat.HEX: (encode_hex, decode_hex),
|
|
EncodingFormat.URL: (
|
|
lambda data: encode_url(data),
|
|
lambda data: decode_url(data),
|
|
),
|
|
}
|
|
|
|
|
|
def encode(data: bytes, fmt: EncodingFormat) -> str:
|
|
encoder_fn, _ = ENCODER_REGISTRY[fmt]
|
|
return encoder_fn(data)
|
|
|
|
|
|
def decode(data: str, fmt: EncodingFormat) -> bytes:
|
|
_, decoder_fn = ENCODER_REGISTRY[fmt]
|
|
return decoder_fn(data)
|
|
|
|
|
|
def try_decode(data: str, fmt: EncodingFormat) -> bytes | None:
|
|
try:
|
|
return decode(data, fmt)
|
|
except (
|
|
ValueError,
|
|
binascii.Error,
|
|
UnicodeDecodeError,
|
|
UnicodeEncodeError,
|
|
):
|
|
return None
|