Skip to content

Instantly share code, notes, and snippets.

@zoldar
Created April 24, 2023 08:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zoldar/52b7661a2214c981070aa7db9080ed95 to your computer and use it in GitHub Desktop.
Save zoldar/52b7661a2214c981070aa7db9080ed95 to your computer and use it in GitHub Desktop.

EIO.inspect

Section

defmodule EIO do
  def inspect(input, opts \\ []) do
    {when_fn, opts} = Keyword.pop(opts, :when)

    if when_fn && not is_function(when_fn, 1) do
      raise ArgumentError, ":when must be a function accepting a single argument"
    end

    when_result =
      try do
        if when_fn do
          when_fn.(input)
        else
          true
        end
      rescue
        _ -> true
      end

    if when_result do
      {highlight_subject, opts} = Keyword.pop(opts, :highlight)

      opts =
        opts
        |> Keyword.put(:inspect_fun, highlighter(highlight_subject))
        |> Keyword.put(:syntax_colors, highlight: :green_background)

      IO.inspect(input, opts)
    else
      input
    end
  end

  defp highlighter(nil) do
    &Inspect.inspect/2
  end

  defp highlighter(subject_fn) when is_function(subject_fn) do
    fn input, opts ->
      try do
        if subject_fn.(input) do
          highlight(input, opts)
        else
          Inspect.inspect(input, opts)
        end
      rescue
        _ ->
          Inspect.inspect(input, opts)
      end
    end
  end

  defp highlighter(subject) do
    fn input, opts ->
      cond do
        input == subject ->
          highlight(input, opts)

        struct_module?(subject) and is_struct(input, subject) ->
          highlight(input, opts)

        is_tuple(subject) and is_struct(input) ->
          with {module, fields} <- subject,
               true <- struct_module?(module),
               true <- is_map(fields) or is_list(fields),
               true <- module == input.__struct__,
               true <- Enum.all?(fields, fn {k, v} -> Map.fetch!(input, k) == v end) do
            highlight(input, opts)
          else
            _ -> Inspect.inspect(input, opts)
          end

        is_map(subject) and is_map(input) and
            Enum.all?(subject, fn {k, v} ->
              Map.has_key?(input, k) and
                  Map.fetch!(input, k) == v
            end) ->
          highlight(input, opts)

        true ->
          Inspect.inspect(input, opts)
      end
    end
  end

  defp highlight(subject, opts) do
    subject
    |> Inspect.inspect(opts)
    |> Inspect.Algebra.color(:highlight, opts)
  end

  defp struct_module?(name) do
    is_atom(name) and function_exported?(name, :__struct__, 1)
  end
end
EIO.inspect(%{a: :b, c: :b}, highlight: :b, when: &(&1.c == :b))

EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: DateTime)

EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: {DateTime, %{year: 2023}})

EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: {DateTime, year: 2023})

EIO.inspect(%{a: %{b: DateTime.utc_now()}},
  highlight: %{year: 2023},
  when: &(&1.a.b.year == 2023)
)

EIO.inspect(%{a: %{b: %{c: 1, d: 8}}}, highlight: %{d: 8})

EIO.inspect([:a, :b, :c], highlight: :c, when: &(:c in &1))

EIO.inspect([DateTime.utc_now(), :b, :c], highlight: &(&1.year == 2023))

EIO.inspect(Enum.map(1..100, fn _ -> Enum.random(1..100) end),
  limit: :infinity,
  highlight: &(rem(&1, 7) == 0)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment