Packages

An Elixir-first XMTP SDK backed by the official Rust XMTP SDK.

Current section

Files

Jump to
xmtp_elixir_sdk lib xmtp principal.ex
Raw

lib/xmtp/principal.ex

defmodule Xmtp.Principal do
@moduledoc false
@type kind :: :human | :agent | :system
@type t :: %__MODULE__{
id: term() | nil,
kind: kind(),
wallet_address: String.t() | nil,
wallet_addresses: [String.t()],
inbox_id: String.t() | nil,
display_name: String.t() | nil,
metadata: map()
}
defstruct id: nil,
kind: :human,
wallet_address: nil,
wallet_addresses: [],
inbox_id: nil,
display_name: nil,
metadata: %{}
@field_keys [
:id,
:kind,
:wallet_address,
:wallet_addresses,
:inbox_id,
:display_name,
:metadata
]
@string_field_keys Map.new(@field_keys, &{Atom.to_string(&1), &1})
@spec from(nil | t() | map()) :: nil | t()
def from(nil), do: nil
def from(%__MODULE__{} = principal), do: normalize(principal)
def from(%{} = attrs) do
attrs
|> normalize_attrs()
|> then(fn normalized ->
kind = Map.get(normalized, :kind, :human)
%__MODULE__{
id: Map.get(normalized, :id),
kind: normalize_kind(kind),
wallet_address: Map.get(normalized, :wallet_address),
wallet_addresses: List.wrap(Map.get(normalized, :wallet_addresses, [])),
inbox_id: Map.get(normalized, :inbox_id),
display_name: Map.get(normalized, :display_name),
metadata: Map.get(normalized, :metadata, %{})
}
|> normalize()
end)
end
@spec human(map()) :: t()
def human(attrs), do: from(Map.put(attrs, :kind, :human))
@spec agent(map()) :: t()
def agent(attrs), do: from(Map.put(attrs, :kind, :agent))
@spec wallet(t() | nil) :: String.t() | nil
def wallet(nil), do: nil
def wallet(%__MODULE__{wallet_address: wallet_address, wallet_addresses: wallet_addresses}) do
[wallet_address | wallet_addresses]
|> Enum.find_value(&normalize_wallet/1)
end
@spec kind(t() | nil) :: kind() | nil
def kind(nil), do: nil
def kind(%__MODULE__{kind: kind}), do: kind
@spec label(t() | nil) :: String.t()
def label(nil), do: "guest"
def label(%__MODULE__{display_name: display_name} = principal) when is_binary(display_name) do
trimmed = String.trim(display_name)
if trimmed == "", do: short(wallet(principal)), else: trimmed
end
def label(%__MODULE__{} = principal), do: short(wallet(principal))
@spec short(String.t() | nil) :: String.t()
def short(nil), do: "guest"
def short(value) when is_binary(value) do
trimmed = String.trim(value)
if String.length(trimmed) <= 12 do
trimmed
else
"#{String.slice(trimmed, 0, 6)}...#{String.slice(trimmed, -4, 4)}"
end
end
@spec normalize_wallet(String.t() | nil) :: String.t() | nil
def normalize_wallet(nil), do: nil
def normalize_wallet(value) when is_binary(value) do
case String.trim(value) do
"" -> nil
wallet -> String.downcase(wallet)
end
end
def normalize_wallet(_value), do: nil
defp normalize(%__MODULE__{} = principal) do
wallet_address = normalize_wallet(principal.wallet_address)
wallet_addresses =
principal.wallet_addresses
|> List.wrap()
|> Enum.map(&normalize_wallet/1)
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
%__MODULE__{
principal
| wallet_address: wallet_address || List.first(wallet_addresses),
wallet_addresses:
Enum.uniq([wallet_address | wallet_addresses] |> Enum.reject(&is_nil/1)),
kind: normalize_kind(principal.kind),
display_name: normalize_label(principal.display_name),
metadata: principal.metadata || %{}
}
end
defp normalize_kind(:human), do: :human
defp normalize_kind(:agent), do: :agent
defp normalize_kind(:system), do: :system
defp normalize_kind("human"), do: :human
defp normalize_kind("agent"), do: :agent
defp normalize_kind("system"), do: :system
defp normalize_kind(_), do: :human
defp normalize_label(nil), do: nil
defp normalize_label(value) when is_binary(value) do
case String.trim(value) do
"" -> nil
trimmed -> trimmed
end
end
defp normalize_label(_value), do: nil
defp normalize_attrs(attrs) do
Enum.reduce(attrs, %{}, fn
{key, value}, acc when is_atom(key) and key in @field_keys ->
Map.put(acc, key, value)
{key, value}, acc when is_binary(key) ->
case Map.fetch(@string_field_keys, key) do
{:ok, normalized_key} -> Map.put(acc, normalized_key, value)
:error -> acc
end
_entry, acc ->
acc
end)
end
end