defprotocol SData.Page do @moduledoc""" Protocol to be implemented by objects that are used as data pages in a pagestore and that may reference other data pages by their hash. """ @fallback_to_any true @doc""" Get hashes of all pages referenced by this page. """ def refs(page) end defimpl SData.Page, for: Any do def refs(_page), do: [] end defprotocol SData.PageStore do @moduledoc""" Protocol to be implemented for page stores to allow their manipulation. This protocol may also be implemented by store proxies that track operations and implement different synchronization or caching mechanisms. """ @doc""" Put a page. Argument is the content of the page, returns the hash that the store has associated to it. Returns {hash, store} """ def put(store, page) @doc""" Get a page referenced by its hash. Returns page """ def get(store, hash) @doc""" Copy to the store a page and all its references from the other store. In the case of pages on the network in a distributed store, this may be lazy. Returns store """ def copy(store, other_store, hash) @doc""" Free a page referenced by its hash, marking it as no longer needed. Returns store """ def free(store, hash) end defmodule SData.LocalStore do defstruct [:pages] def new() do %SData.LocalStore{ pages: %{} } end end defimpl SData.PageStore, for: SData.LocalStore do def put(store, page) do hash = SData.term_hash page store = %{ store | pages: Map.put(store.pages, hash, page) } { hash, store } end def get(store, hash) do store.pages[hash] end def copy(store, other_store, hash) do page = SData.PageStore.get(other_store, hash) refs = SData.Page.refs(page) store = Enum.reduce(refs, store, fn x, acc -> copy(acc, other_store, x) end) %{ store | pages: Map.put(store.pages, hash, page) } end def free(store, hash) do %{ store | pages: Map.delete(store.pages, hash) } end end