%%%------------------------------------------------------------------- %%% @doc %%% ID generation utilities for Macula. %%% Provides functions for generating various types of IDs. %%% @end %%%------------------------------------------------------------------- -module(macula_id). %% API -export([ node_id/0, message_id/0, session_id/0, hash_id/1, to_uuid/1, from_uuid/1, to_hex/1, from_hex/1 ]). %%%=================================================================== %%% API Functions %%%=================================================================== %% @doc Generate 256-bit (32-byte) random node ID. -spec node_id() -> binary(). node_id() -> crypto:strong_rand_bytes(32). %% @doc Generate 128-bit (16-byte) random message ID. -spec message_id() -> binary(). message_id() -> crypto:strong_rand_bytes(16). %% @doc Generate 128-bit (16-byte) random session ID. -spec session_id() -> binary(). session_id() -> crypto:strong_rand_bytes(16). %% @doc Generate deterministic 256-bit hash ID from data. -spec hash_id(binary()) -> binary(). hash_id(Data) when is_binary(Data) -> crypto:hash(sha256, Data). %% @doc Convert 16-byte or 32-byte binary ID to UUID string format. %% For 16-byte: 8-4-4-4-12 (e.g., "12345678-90ab-cdef-1234-567890abcdef") %% For 32-byte: Uses first 16 bytes -spec to_uuid(binary()) -> binary(). to_uuid(<>) -> %% Handles both 16-byte and 32-byte inputs (uses first 16 bytes) list_to_binary( io_lib:format("~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b", [A, B, C, D, E]) ). %% @doc Convert UUID string to 16-byte binary ID. -spec from_uuid(binary()) -> {ok, binary()} | {error, invalid_uuid}. from_uuid(Uuid) when is_binary(Uuid), byte_size(Uuid) =:= 36 -> try %% Parse UUID format: 8-4-4-4-12 <> = Uuid, %% Convert hex strings to integers AInt = binary_to_integer(A, 16), BInt = binary_to_integer(B, 16), CInt = binary_to_integer(C, 16), DInt = binary_to_integer(D, 16), EInt = binary_to_integer(E, 16), %% Pack into binary {ok, <>} catch _:_ -> {error, invalid_uuid} end; from_uuid(_) -> {error, invalid_uuid}. %% @doc Convert binary to lowercase hex string. -spec to_hex(binary()) -> binary(). to_hex(Binary) when is_binary(Binary) -> list_to_binary([io_lib:format("~2.16.0b", [B]) || <> <= Binary]). %% @doc Convert hex string to binary. -spec from_hex(binary()) -> {ok, binary()} | {error, invalid_hex}. from_hex(Hex) when is_binary(Hex) -> try %% Ensure even length case byte_size(Hex) rem 2 of 0 -> Bytes = [binary_to_integer(<>, 16) || <> <= Hex], {ok, list_to_binary(Bytes)}; _ -> {error, invalid_hex} end catch _:_ -> {error, invalid_hex} end; from_hex(_) -> {error, invalid_hex}.