defmodule Serum.File do
@moduledoc """
Defines a struct representing a file to be read or written.
## Fields
* `src`: Source path
* `dest`: Destination path
* `in_data`: Data read from a file
* `out_data`: Data to be written to a file
"""
import Serum.IOProxy, only: [put_msg: 2]
alias Serum.Result
defstruct [:src, :dest, :in_data, :out_data]
@type t :: %__MODULE__{
src: binary() | nil,
dest: binary() | nil,
in_data: IO.chardata() | String.Chars.t() | nil,
out_data: IO.chardata() | String.Chars.t() | nil
}
@doc """
Reads data from a file described by the given `Serum.File` struct.
An error will be returned if `src` is `nil`.
"""
@spec read(t()) :: Result.t(t())
def read(%__MODULE__{src: nil}) do
msg = "a Serum.File struct with 'src = nil' cannot be used with Serum.File.read/1"
{:error, msg}
end
def read(%__MODULE__{src: src} = file) do
case File.read(src) do
{:ok, data} when is_binary(data) ->
print_read(src)
{:ok, %__MODULE__{file | in_data: data}}
{:error, reason} ->
{:error, {reason, src, 0}}
end
end
@doc """
Writes data to a file described by the given `Serum.File` struct.
An error will be returned if `dest` is `nil`.
"""
@spec write(t()) :: Result.t(t())
def write(%__MODULE__{dest: nil}) do
msg = "a Serum.File struct with 'dest = nil' cannot be used with Serum.File.write/1"
{:error, msg}
end
def write(%__MODULE__{dest: dest, out_data: data} = file) do
case File.open(dest, [:write, :utf8], &IO.write(&1, data)) do
{:ok, _} ->
print_write(dest)
{:ok, file}
{:error, reason} ->
{:error, {reason, dest, 0}}
end
rescue
_e in ErlangError ->
# {:error, {ErlangError.message(e), dest, 0}}
{:ok, file}
end
@spec print_read(binary()) :: :ok
defp print_read(src), do: put_msg(:read, src)
@spec print_write(binary()) :: :ok
defp print_write(dest), do: put_msg(:gen, dest)
defimpl String.Chars, for: Serum.File do
@moduledoc false
def to_string(%Serum.File{src: src}), do: src
end
end