Packages
estructura
0.5.1
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.1
1.6.0
1.5.0
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
Extensions for Elixir structures.
Current section
Files
Jump to
Current section
Files
lib/estructura/lazy_map.ex
defmodule Estructura.LazyMap do
@moduledoc """
The implementation of lazy map implementing lazy `Access` for its keys.
`Estructura.LazyMap` is backed by the “raw” object and a key-value pairs
where values might be instances of `Estructura.Lazy`. If this is a case,
they will be accessed through `Lazy` implementation.
Values might be also raw values, which makes `LazyMap` a drop-in replacement
of standard _Elixir_ maps, assuming they are accessed through `Access`
only (e. g. `map[:key]` and not `map.key`.)
"""
@type t :: %{
__struct__: __MODULE__,
__lazy_data__: term(),
data: map()
}
defstruct data: %{}, __lazy_data__: nil
alias Estructura.Lazy
@behaviour Access
@impl Access
def fetch(lazy, key)
def fetch(%__MODULE__{data: %{} = data} = this, key) when is_map_key(data, key) do
case Map.get(data, key) do
%Lazy{} = value ->
case Lazy.apply(value, this, key) do
%Lazy{value: {:ok, value}} -> {:ok, value}
_ -> :error
end
value ->
{:ok, value}
end
end
def fetch(%__MODULE__{}, _), do: :error
@impl Access
def pop(lazy, key)
def pop(%__MODULE__{data: %{} = data} = this, key) when is_map_key(data, key) do
case Map.get(data, key) do
%Lazy{} = value ->
case Lazy.apply(value, this, key) do
%Lazy{value: {:ok, value}} ->
{value, %__MODULE__{this | data: Map.delete(data, key)}}
_ ->
{nil, this}
end
value ->
{value, %__MODULE__{this | data: Map.delete(data, key)}}
end
end
def pop(%__MODULE__{data: %{}} = this, _), do: {nil, this}
@impl Access
def get_and_update(lazy, key, fun \\ &{&1, &1})
def get_and_update(%__MODULE__{data: %{} = data} = this, key, fun) do
case Map.get(data, key) do
%Lazy{} = value ->
case Lazy.apply(value, this, key) do
%Lazy{value: {:ok, value}} = result ->
case fun.(value) do
:pop ->
pop(this, key)
{current_value, new_value} ->
{current_value,
%__MODULE__{this | data: Map.put(data, key, Lazy.put(result, new_value))}}
end
_ ->
{nil, data}
end
_ ->
{value, data} = Map.get_and_update(data, key, fun)
{value, %__MODULE__{this | data: data}}
end
end
@spec new(keyword() | map()) :: t()
@doc """
Creates new instance of `LazyMap` with a second parameter being a backed up object,
which would be used for lazy retrieving data for values, when value is an instance
of `Estructura.Lazy`.
## Examples
iex> lm = Estructura.LazyMap.new(
...> [int: Estructura.Lazy.new(&Estructura.LazyInst.parse_int/1)], "42")
...> get_in lm, [:int]
42
"""
def new(initial \\ %{}, lazy_data \\ nil)
def new(kw, lazy_data) when is_list(kw), do: kw |> Map.new() |> new(lazy_data)
def new(%{} = map, lazy_data), do: %__MODULE__{data: map, __lazy_data__: lazy_data}
@spec keys(t()) :: [Map.key()]
@doc "Returns all the keys of the underlying map"
@doc since: "0.4.1"
def keys(%__MODULE__{data: data}), do: Map.keys(data)
@spec fetch_all(t()) :: t()
@doc "Eagerly instantiates the data"
@doc since: "0.4.1"
def fetch_all(%__MODULE__{} = lazy) do
lazy
|> keys()
|> Enum.reduce({%{}, lazy}, fn key, {result, lazy} ->
{value, lazy} = get_and_update(lazy, key, &{&1, &1})
{Map.put(result, key, value), lazy}
end)
end
defimpl Inspect do
@moduledoc false
import Inspect.Algebra
alias Estructura.LazyMap
def inspect(%LazyMap{data: %{} = data}, opts) do
{_, data} = Map.pop(data, :__lazy_data__)
if Keyword.get(opts.custom_options, :lazy_marker, false),
do: concat(["%‹", to_doc(data, opts), "›"]),
else: to_doc(data, opts)
end
end
end