-module(grpcbox_metadata). -export([new/1, new_incoming_ctx/1, append_to_outgoing_ctx/2, pairs/1, join/1, from_incoming_ctx/1, from_outgoing_ctx/1]). -export_type([t/0, key/0, value/0]). -type key() :: unicode:chardata(). %% but only ASCII allowed? -type value() :: unicode:chardata(). -type t() :: #{key() => value()}. %% New creates an MD from a given key-value map. %% %% Only the following ASCII characters are allowed in keys: %% - digits: 0-9 %% - uppercase letters: A-Z (normalized to lower) %% - lowercase letters: a-z %% - special characters: -_. %% Uppercase letters are automatically converted to lowercase. %% %% Keys beginning with "grpc-" are reserved for grpc-internal use only and may %% result in errors if set in metadata. -spec new(#{unicode:chardata() => unicode:chardata()}) -> t(). new(Map) -> maps:fold(fun(K, V, Acc) -> maps:put(string:lowercase(K), V, Acc) end, #{}, Map). new_incoming_ctx(Map) -> ctx:with_value(md_incoming_key, new(Map)). append_to_outgoing_ctx(Ctx, Map) -> ctx:with_value(Ctx, md_outgoing_key, join([from_outgoing_ctx(Ctx), new(Map)])). -spec pairs([{key(), value()}]) -> t(). pairs(List) -> lists:foldl(fun({Key, Value}, Map) -> update(Key, Value, Map) end, #{}, List). -spec join([t()]) -> t(). join(Metadatas) -> lists:foldl(fun(Map, Acc) -> maps:fold(fun(Key, Value, Acc1) -> update(Key, Value, Acc1) end, Acc, Map) end, #{}, Metadatas). -spec from_incoming_ctx(ctx:t()) -> t(). from_incoming_ctx(Ctx) -> ctx:get(Ctx, md_incoming_key, #{}). -spec from_outgoing_ctx(ctx:t()) -> t(). from_outgoing_ctx(Ctx) -> ctx:get(Ctx, md_outgoing_key, #{}). %% internal update(Key, Value, Map) -> maps:update_with(Key, fun(V) when is_list(V) -> [Value | V ]; (V) -> [Value, V] end, Value, Map).