Packages

Matryoshka provides composable storage functionality through stores and storage combinators.

Current section

Files

Jump to
matryoshka lib matryoshka.ex
Raw

lib/matryoshka.ex

defmodule Matryoshka do
# Implementation logic
alias Matryoshka.Impl.CachingStore
alias Matryoshka.Impl.FilesystemStore
alias Matryoshka.Impl.LoggingStore
alias Matryoshka.Impl.LogStore
alias Matryoshka.Impl.MapStore
alias Matryoshka.Impl.MappingStore
alias Matryoshka.Impl.PassThrough
alias Matryoshka.Impl.PersistentStore
alias Matryoshka.Impl.SftpStore
alias Matryoshka.Impl.SwitchingStore
# Client to interact with store
alias Matryoshka.Client
# _________________________ Client _________________________
# --------------------- Starting Server --------------------
@doc """
Starts the storage server from a provided store.
## Examples
{:ok, pid} = map_store() |> start_link()
"""
defdelegate start_link(store, options \\ []), to: Client
# -------------------- Storage Requests --------------------
@doc """
Gets a value from the storage server from the provided `ref`.
## Examples
iex> {:ok, pid} = map_store() |> start_link()
iex> put(pid, "item", :item)
iex> get(pid, "item")
:item
iex> get(pid, "not_existing")
nil
"""
defdelegate get(pid, ref), to: Client
@doc """
Fetches a value from the storage server from the provided `ref`.
## Examples
iex> {:ok, pid} = map_store() |> start_link()
iex> put(pid, "item", :item)
iex> fetch(pid, "item")
{:ok, :item}
iex> fetch(pid, "not_existing")
{:error, {:no_ref, "not_existing"}}
"""
defdelegate fetch(pid, ref), to: Client
@doc """
Puts a value into the storage server at the provided `ref`.
## Examples
iex> {:ok, pid} = map_store() |> start_link()
iex> put(pid, "item", :item)
iex> fetch(pid, "item")
{:ok, :item}
"""
defdelegate put(pid, ref, value), to: Client
@doc """
Fetches a value from the storage server from the provided `ref`.
## Examples
iex> {:ok, pid} = map_store() |> start_link()
iex> put(pid, "item", :item)
iex> fetch(pid, "item")
{:ok, :item}
iex> delete(pid, "item")
iex> fetch(pid, "item")
{:error, {:no_ref, "item"}}
"""
defdelegate delete(pid, ref), to: Client
# _________________________ Stores _________________________
@doc """
Creates a `CachingStore` backed by a `MapStore` as the fast cache.
This is a convenience function that automatically wraps the given `main_store`
with a new in-memory `MapStore` for fast access.
## Parameters
* `main_store` - The primary storage module or instance that acts as the
source of truth. Must implement the `Matryoshka.Storage` protocol.
## Returns
A `CachingStore` struct wrapping the `main_store` and a new `MapStore` as
the fast cache.
"""
defdelegate caching_store(main_store), to: CachingStore
@doc """
Creates a `CachingStore` from a `main_store` (source of truth) and a
`cache_store` (cache).
The `CachingStore` first attempts to read from `cache_store` for faster
access, falling back to `main_store` if necessary. On successful reads
from `main_store`, the value is populated into the `cache_store` to speed
up future access.
## Parameters
* `main_store` - The primary store holding the authoritative data.
Must implement the `Matryoshka.Storage` protocol.
* `cache_store` - A secondary, faster-access store used for caching
lookups. Must also implement the `Matryoshka.Storage`
protocol.
## Returns
A `CachingStore` struct that wraps both the `main_store` and the
`cache_store`.
## Raises
Raises an error if either `main_store` or `cache_store` does not implement
the required Storage protocol.
"""
defdelegate caching_store(main_store, cache_store), to: CachingStore
@doc """
Creates a new `%FilesystemStore{}` rooted at the given directory.
All reference paths used by this store will be resolved relative to this
root directory. The path should be a valid absolute or relative path on
the filesystem.
## Examples
store = filesystem_store("/tmp/storage")
"""
defdelegate filesystem_store(root_dir), to: FilesystemStore
@doc """
Wraps a store with logging functionality.
The `LoggingStore` logs all `get`, `put`, `patch`, and `delete` operations
performed on the inner store. This is useful for debugging or auditing storage behavior.
## Parameters
* `storage` - The underlying storage to wrap. Must implement the
`Matryoshka.Storage` protocol.
## Returns
A `%LoggingStore{}` struct that wraps the given `storage`.
## Raises
Raises an error if `storage` does not implement the required
`Matryoshka.Storage` protocol.
"""
defdelegate logging_store(store), to: LoggingStore
@doc """
Opens a log file, either loading its offset index or creating a new one if
the file doesn't exist.
The function attempts to open the log file at the given `log_filepath`.
If the file exists, it loads the key-value offsets and opens the file for
both reading and writing. If the file doesn't exist, it creates a new index
and opens the file for both reading and writing.
Returns a `%__MODULE__{}` struct containing the following:
- `reader`: The file descriptor for reading from the log file.
- `writer`: The file descriptor for writing to the log file.
- `index`: A map representing the offset index of the log file.
If the log file doesn't exist, the index is initialized as an empty map.
"""
defdelegate log_store(log_filepath), to: LogStore
@doc """
Creates a new `MapStore` backed by an empty `Map`.
Returns a `%MapStore{}` with an empty map.
"""
defdelegate map_store(), to: MapStore
@doc """
Creates a new `MapStore` from an existing map.
## Parameters
* `map` - An existing `%{}` map to use as the backing store.
Returns a `%MapStore{}` wrapping the given map.
"""
defdelegate map_store(map), to: MapStore
@doc """
Wraps an inner store with a mapping layer.
The `mapping_store/2` function allows you to map values when reading from
or writing to the `inner` store. It accepts an `inner` store and an optional
list of options.
## Parameters
* `inner` - The underlying store module or instance to wrap. Must implement
the `Matryoshka.Storage` protocol.
* `opts` - (keyword list) Optional settings:
* `:map_to_store` - (function) A function `(value -> stored_value)` that
maps values *before* storing them. Defaults to `Function.identity/1`.
* `:map_retrieved` - (function) A function `(stored_value -> value)` that
maps values when retrieved (get/fetch) from the store. Defaults to
`Function.identity/1`.
* `:map_ref` - (function). A function that maps references
`(ref -> mapped_ref)` before using them to locate values. Defaults to
`Function.identity/1`.
If the mapping function aren't provided, values and references are passed
through unchanged.
"""
defdelegate mapping_store(store, opts), to: MappingStore
@doc """
Wraps a storage with a `PassThrough` combinator.
The `PassThrough` combinator simply delegates all storage operations to the
inner storage.
## Parameters
* `storage` — A struct implementing the `Matryoshka.Storage`
protocol.
## Returns
A `%PassThrough{}` wrapping the given storage.
"""
defdelegate pass_through(store), to: PassThrough
@doc """
Creates a persistent and cached log store from the provided file path.
This function initializes a log store by opening the specified log file
(`log_filepath`). If the log file exists, it reads the file and loads any
existing offsets for fast access. If the log file does not exist, it creates
a new file for storing log data.
This store also applies a caching mechanism to optimize subsequent read and
write operations.
### Parameters:
- `log_filepath`: The file path to the log file, which will be used for
persisting data on disk.
### Returns:
- A cached log store, which includes functionality for efficient reading,
writing, and indexing of log data.
"""
defdelegate persistent_store(log_filepath), to: PersistentStore
@doc """
Creates a store backed by an SSH FTP (SFTP) client.
## Parameters
* `host` - The hostname of the SFTP server.
* `port` - The port of the SFTP server.
* `username` - The username to log in to the SFTP server with.
* `password` - The password to log in to the SFTP server with.
Returns a `%SftpStore` struct containing the following:
- `pid`: The PID for communicating with the SFTP server.
- `connection`: An opaque data type representing the connection between the
SFTP client and the SFTP server.
"""
defdelegate sftp_store(host, port, username, password), to: SftpStore
@doc """
Creates a `SwitchingStore` that routes `fetch`, `get`, `put`, and `delete`
requests to different sub-stores based on the first segment of the path.
Each sub-store is mapped by a string key. The first segment of the given
reference is used to select the appropriate sub-store to handle the
operation.
## Parameters
* `path_store_map` — A map where keys are `String.t()` representing the
first path segment, and values are storages implementing the
`Matryoshka.Storage` protocol.
"""
defdelegate switching_store(path_store_map), to: SwitchingStore
end