lib/exexif/data/thumbnail.ex

defmodule Exexif.Data.Thumbnail do
  @moduledoc """
  Internal representation of Thumbnail tag in the EXIF.
  """
  @type t :: %Exexif.Data.Thumbnail{
          thumbnail_offset: non_neg_integer(),
          thumbnail_size: non_neg_integer()
        }

  @fields [
    :thumbnail_offset,
    :thumbnail_size
  ]

  @spec fields :: [:thumbnail_offset | :thumbnail_size]
  @doc false
  def fields, do: @fields

  defstruct @fields

  @spec to_image(binary(), t()) :: :ok | {:error, :bad_thumbnail_data | :file.posix()}
  @doc "Converts the thumbnail to image and writes it to the file"
  def to_image(_, %Exexif.Data.Thumbnail{thumbnail_offset: offset, thumbnail_size: size})
      when is_nil(offset) or is_nil(size),
      do: {:error, :bad_thumbnail_data}

  def to_image(file, %Exexif.Data.Thumbnail{thumbnail_offset: offset, thumbnail_size: size})
      when is_binary(file) do
    [name, dot, ext] = String.split(file, ~r/(?=.{3,4}\z)/)

    with {:ok, src} <-
           File.open(file, [:read], fn f ->
             IO.binread(f, offset)
             IO.binread(f, size)
           end),
         {:ok, _dst} <- File.open("#{name}-thumb#{dot}#{ext}", [:write], &IO.binwrite(&1, src)),
         do: :ok
  end

  defimpl String.Chars, for: Exexif.Data.Thumbnail do
    @spec to_string(data :: Exexif.Data.Thumbnail.t()) :: <<_::64, _::_*8>>
    def to_string(data), do: "Image Thumbnail of size #{data.thumbnail_size}"
  end
end