defmodule FileStream do @moduledoc """ A module to stream file-like resources. """ defstruct [:uri, :config, :storage, meta: %{}] ################################ # Types ################################ @type t :: %__MODULE__{ uri: URI.t(), config: any(), storage: FileStream.Storage.t(), meta: map() } ################################ # Public API ################################ @doc """ Returns a `FileStream` for the given URI. """ @spec stream!(binary | URI.t(), keyword()) :: FileStream.t() def stream!(uri, opts \\ []) def stream!(uri, opts) when is_binary(uri) do uri |> URI.parse() |> stream!(opts) end def stream!(%URI{} = uri, opts) do storage = FileStream.Storage.from_uri!(uri) config = storage.init(opts) %FileStream{uri: uri, storage: storage, config: config} end @doc """ Puts the given `value` under `key` of the stream meta. """ @spec put_meta(FileStream.t(), atom(), any()) :: FileStream.t() def put_meta(stream, key, value) do %{stream | meta: Map.put(stream.meta, key, value)} end @doc """ Gets the value for a specific `key` within the stream meta. """ @spec get_meta(FileStream.t(), atom()) :: any() def get_meta(stream, key) do Map.get(stream.meta, key) end @doc """ Resets the stream meta to be empty. """ @spec reset_meta(FileStream.t()) :: FileStream.t() def reset_meta(stream) do %{stream | meta: %{}} end ################################ # Implementations ################################ defimpl Collectable do def into(stream) do stream.storage.into(stream) end end defimpl Enumerable do def reduce(stream, acc, fun) do stream.storage.reduce(stream, acc, fun) end @doc false def member?(_, _) do {:error, __MODULE__} end @doc false def count(_) do {:error, __MODULE__} end @doc false def slice(_) do {:error, __MODULE__} end end defimpl Inspect do def inspect(%{uri: uri}, _) do "#FileStream<\"#{uri}\">" end end end