defmodule BSON.Binary do @moduledoc """ Represents BSON binary type """ @type t :: %__MODULE__{ binary: binary, subtype: :generic | :function | :binary_old | :uuid_old | :uuid | :md5 | 0x80..0xFF } defstruct binary: nil, subtype: :generic defimpl String.Chars, for: BSON.Binary do def to_string(%BSON.Binary{binary: value, subtype: subtype}) when subtype in [:uuid, :uuid_old] do p1 = binary_part(value, 0, 4) p2 = binary_part(value, 4, 2) p3 = binary_part(value, 6, 2) p4 = binary_part(value, 8, 2) p5 = binary_part(value, 10, 6) "#{Base.encode16(p1, case: :lower)}-#{Base.encode16(p2, case: :lower)}-#{Base.encode16(p3, case: :lower)}-#{Base.encode16(p4, case: :lower)}-#{Base.encode16(p5, case: :lower)}" end def to_string(%BSON.Binary{binary: value}) do Base.encode16(value, case: :lower) end end defimpl Inspect do def inspect(%BSON.Binary{subtype: :generic} = binary, _opts) do "#BSON.Binary<#{to_string(binary)}>" end def inspect(%BSON.Binary{subtype: :uuid} = binary, _opts) do "#BSON.UUID<#{to_string(binary)}>" end def inspect(%BSON.Binary{subtype: :uuid_old} = binary, _opts) do "#BSON.LUUID<#{to_string(binary)}>" end def inspect(%BSON.Binary{subtype: subtype} = binary, _opts) do "#BSON.Binary<#{to_string(binary)}, #{subtype}>" end end end defmodule BSON.ObjectId do @moduledoc """ Represents BSON ObjectId type """ defstruct [:value] @type t :: %__MODULE__{value: <<_::96>>} @doc """ Creates a new ObjectId from the consisting parts """ def new(machine_id, proc_id, secs, counter) do value = <> %BSON.ObjectId{value: value} end @doc """ Converts string representation of ObjectId to a BSON.ObjectId struct """ def decode!(<>) do <> catch :throw, :error -> raise ArgumentError else value -> %BSON.ObjectId{value: value} end @doc """ Converts string representation of ObjectId to a BSON.ObjectId struct ## Example ```elixir {:ok, id} <- BSON.ObjectId.decode(bson_id) """ def decode(id) do try do {:ok, decode!(id)} rescue _ -> :error end end @doc """ Converts BSON.ObjectId struct to a string representation """ def encode!(%BSON.ObjectId{value: value}), do: do_encode(value) defp do_encode(<>) do <> catch :throw, :error -> raise ArgumentError else value -> value end @doc """ Converts BSON.ObjectId struct to a string representation ## Example ```elixir {:ok, bson_id} <- BSON.ObjectId.encode(id) """ def encode(object_id) do try do {:ok, encode!(object_id)} rescue _ -> :error end end @compile {:inline, :d, 1} @compile {:inline, :e, 1} @chars Enum.concat(?0..?9, ?a..?f) for {char, int} <- Enum.with_index(@chars) do defp d(unquote(char)), do: unquote(int) defp e(unquote(int)), do: unquote(char) end for {char, int} <- Enum.with_index(?A..?F) do defp d(unquote(char)), do: unquote(int) end defp d(_), do: throw(:error) defp e(_), do: throw(:error) defimpl Inspect do def inspect(objectid, _opts) do encoded = BSON.ObjectId.encode!(objectid) "#BSON.ObjectId<#{encoded}>" end end end defmodule BSON.Regex do @moduledoc """ Represents BSON Regex type """ @type t :: %__MODULE__{pattern: binary, options: binary} defstruct pattern: "", options: "" defimpl Inspect do def inspect(%BSON.Regex{pattern: pattern, options: nil}, _opts) do "#BSON.Regex<#{inspect(pattern)}>" end def inspect(%BSON.Regex{pattern: pattern, options: options}, _opts) do "#BSON.Regex<#{inspect(pattern)}, #{inspect(options)}>" end end end defmodule BSON.JavaScript do @moduledoc """ Represents BSON JavaScript (with and without scope) types """ @type t :: %__MODULE__{code: binary, scope: %{binary => BSON.t()}} defstruct [:code, :scope] defimpl Inspect do def inspect(%BSON.JavaScript{code: code, scope: nil}, _opts) do "#BSON.JavaScript<#{inspect(code)}>" end def inspect(%BSON.JavaScript{code: code, scope: scope}, _opts) do "#BSON.JavaScript<#{inspect(code)}, #{inspect(scope)}>" end end end defmodule BSON.Timestamp do @moduledoc """ Represents BSON Timestamp type """ @type t :: %__MODULE__{value: integer, ordinal: integer} defstruct [:value, :ordinal] def is_after(this, that) do this.value > that.value or (this.value == that.value and this.ordinal > that.ordinal) end def is_before(this, that) do this.value < that.value or (this.value == that.value and this.ordinal < that.ordinal) end defimpl Inspect do def inspect(%BSON.Timestamp{value: value, ordinal: ordinal}, _opts) do "#BSON.Timestamp<#{value}:#{ordinal}>" end end end defmodule BSON.LongNumber do @moduledoc """ Represents BSON long type """ @type t :: %__MODULE__{value: integer} defstruct value: 0 defimpl Inspect do def inspect(%BSON.LongNumber{value: value}, _opts) do "#BSON.LongNumber<#{value}>" end end end