# Copyright (c) 2025-2026 Buf Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from typing import TYPE_CHECKING, TypeVar, cast from protobuf import DescMessage, Message T = TypeVar("T", bound=Message) Self = TypeVar("Self", bound="AnyMixin") class AnyMixin: __slots__ = () if TYPE_CHECKING: def __init__(self, *, type_url: str = "", value: bytes = b"") -> None: pass type_url: str value: bytes @classmethod def pack(cls: type[Self], message: Message) -> Self: """Creates an `Any` from a message.""" type_url = f"type.googleapis.com/{message._desc.type_name}" return cls(type_url=type_url, value=message.to_binary()) def is_type(self, type_info: type[Message] | DescMessage | str) -> bool: """Returns true if the Any contains the type given by schema or type name.""" if self.type_url == "": return False type_name: str match type_info: case str(): type_name = type_info case DescMessage(): type_name = type_info.type_name case _: type_name = type_info._desc.type_name return type_name == type_url_to_name(self.type_url) def unpack(self, type_info: DescMessage | type[T]) -> T | None: """Unpacks the message the Any represents. Returns: Returns None if the Any is empty, or if it does not contain the type given by schema. Otherwise, the unpacked message. """ if not self.is_type(type_info): return None stub: type[T] match type_info: case DescMessage() as desc: stub = cast("type[T]", desc.type) case _: stub = type_info return stub.from_binary(self.value) def type_url_to_name(url: str) -> str: # rindex raises an error if sub str is not found name = url[(url.rindex("/") if "/" in url else -1) + 1 :] if len(name) == 0: msg = f"invalid type url: {url}" raise ValueError(msg) return name