# FlakeId: Decentralized, k-ordered ID generation service # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: LGPL-3.0-only defmodule FlakeId do @moduledoc """ Decentralized, k-ordered ID generation service. """ import Kernel, except: [to_string: 1] @doc """ Converts a binary Flake to a String ## Examples iex> FlakeId.to_string(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>) "9n2ciuz1wdesFnrGJU" """ def to_string(<<_::integer-size(64), _::integer-size(48), _::integer-size(16)>> = binary_flake) do <> = binary_flake Base62.encode(integer) end def to_string(string), do: string @doc """ Converts a String to a binary Flake ## Examples iex> FlakeId.from_string("9n2ciuz1wdesFnrGJU") <<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>> """ @spec from_string(binary()) :: nil | <<_::128>> def from_string(string) def from_string(<<_::integer-size(128)>> = flake), do: flake def from_string(string) when is_binary(string) and byte_size(string) < 18, do: nil def from_string(string), do: string |> Base62.decode!() |> from_integer @doc """ Converts a binary Flake to an integer ## Examples iex> FlakeId.to_integer(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>) 28939165907792829150732718047232 """ @spec to_integer(<<_::128>>) :: non_neg_integer def to_integer(binary_flake) def to_integer(<>), do: integer @doc """ Converts an integer to a binary Flake ## Examples iex> FlakeId.from_integer(28939165907792829150732718047232) <<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>> """ @spec from_integer(integer) :: <<_::128>> def from_integer(integer) do <<_time::integer-size(64), _node::integer-size(48), _seq::integer-size(16)>> = <> end @doc """ Generates a string with Flake """ @spec get :: String.t() def get, do: FlakeId.Worker.get() |> to_string() @doc """ Checks that ID is a valid FlakeId ## Examples iex> FlakeId.flake_id?("9n2ciuz1wdesFnrGJU") true iex> FlakeId.flake_id?("#cofe") false iex> FlakeId.flake_id?("pleroma.social") false """ @spec flake_id?(String.t()) :: boolean def flake_id?(id), do: flake_id?(String.to_charlist(id), true) defp flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: flake_id?(cs, true) defp flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: flake_id?(cs, true) defp flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: flake_id?(cs, true) defp flake_id?([], true), do: true defp flake_id?(_, _), do: false end