Current section
Files
Jump to
Current section
Files
lib/grpc/transport/utils.ex
defmodule GRPC.Transport.Utils do
@moduledoc false
# unit: ns
@ns_ceiling 1000
@us_ceiling 1000_000
# @ms_ceiling @us_ceiling * 1000
# unit: ms
@ms_ceiling 1000
@second_ceiling @ms_ceiling * 60
@minute_ceiling @second_ceiling * 60
@doc """
Encode timeout(nanoseconds) by gRPC protocol
"""
def encode_ns(timeout) when timeout <= 0, do: "0u"
def encode_ns(timeout) when is_integer(timeout) and timeout < @ns_ceiling do
to_string(timeout) <> "n"
end
def encode_ns(timeout) when timeout < @us_ceiling do
to_string(div(timeout, 1000)) <> "u"
end
@doc """
Encode timeout(milliseconds) by gRPC protocol
"""
def encode_timeout(timeout) when timeout <= 0, do: "0u"
def encode_timeout(timeout) when is_integer(timeout) and timeout < @ms_ceiling do
to_string(timeout) <> "m"
end
def encode_timeout(timeout) when timeout < @second_ceiling do
to_string(div(timeout, 1000)) <> "S"
end
def encode_timeout(timeout) when timeout < @minute_ceiling do
to_string(div(timeout, 1000 * 60)) <> "M"
end
def encode_timeout(timeout) do
to_string(div(timeout, 1000 * 3600)) <> "H"
end
@spec decode_timeout(String.t()) :: non_neg_integer()
def decode_timeout(timeout) do
{timeout, unit} = String.split_at(timeout, -1)
decode_timeout(unit, String.to_integer(timeout))
end
defp decode_timeout("n", timeout) do
div(timeout, 1000_000)
end
defp decode_timeout("u", timeout) do
div(timeout, 1000)
end
defp decode_timeout("m", timeout) do
timeout
end
defp decode_timeout("S", timeout) do
timeout * 1000
end
defp decode_timeout("M", timeout) do
timeout * 60_000
end
defp decode_timeout("H", timeout) do
timeout * 3_600_000
end
@doc """
Encode Google.Rpc.Status message with rich error details.
"""
@spec encode_status_details(integer, list()) :: String.t()
def encode_status_details(_errorcode, nil), do: ""
def encode_status_details(errorcode, details) do
Google.Rpc.Status.new(code: errorcode, details: Enum.map(details, &build_any/1))
|> Google.Rpc.Status.encode()
end
@doc """
Decode Google.Rpc.Status message with rich error details.
"""
@spec decode_status_details(String.t()) :: Google.Rpc.Status.t()
def decode_status_details(details)
when is_binary(details) do
%Google.Rpc.Status{code: _code, message: _message, details: details} =
Google.Rpc.Status.decode(details)
Enum.map(details, &decode_any/1)
end
defp build_any(message = %{__struct__: type}) do
Google.Protobuf.Any.new(type_url: get_type_url(type), value: Protobuf.encode(message))
end
def get_type_url(type) do
[_ | type] = type |> to_string |> String.split(".")
{type_name, package} = type |> List.pop_at(-1)
package_name = package |> Enum.map(&String.downcase(&1)) |> Enum.join(".")
"type.googleapis.com/#{package_name}.#{type_name}"
end
defp decode_any(%Google.Protobuf.Any{type_url: type_url, value: value}) do
[_, type] = String.split(type_url, "/")
msg_module = string_to_module(type)
msg_module.decode(value)
end
defp string_to_module(type) do
module =
type
|> String.split(".")
|> Enum.map(&Macro.camelize/1)
|> (&Enum.concat(["Elixir"], &1)).()
|> Enum.join(".")
|> String.to_atom()
case Code.ensure_loaded(module) do
{:module, module} -> module
{:error, reason} -> raise "Failed to load module. Reason: #{inspect(reason)}"
end
end
end