# `Electric.Utils`
[🔗](https://github.com/electric-sql/electric/tree/%40core/sync-service%401.6.2/packages/sync-service/lib/electric/utils.ex#L1)

# `item_reader_fn`

```elixir
@type item_reader_fn(elem) :: (file :: :file.io_device() -&gt;
                           sortable_binary(elem) | :halt)
```

# `sortable_binary`

```elixir
@type sortable_binary(key) :: {key :: key, data :: binary()}
```

# `all_max_by`

Return a list of values from `enum` that are the maximal elements as calculated
by the given `fun`.

Base behaviour is similar to `Enum.max_by/4`, but this function returns a list
of all maximal values instead of just the first one.

## Examples

    iex> all_max_by([4, 1, 1, 3, -4], &abs/1)
    [4, -4]

    iex> all_max_by([4, 1, -1, 3, 4], &abs/1, &<=/2)
    [1, -1]

    iex> all_max_by([], &abs/1)
    ** (Enum.EmptyError) empty error

# `apply_fn_or_mfa`

Applies either an anonymous function or a MFA tuple, prepending the given arguments
in case of an MFA.

## Examples

    iex> apply_fn_or_mfa(&String.contains?(&1, "foo"), ["foobar"])
    true

    iex> apply_fn_or_mfa({String, :contains?, ["foo"]}, ["foobar"])
    true

# `deep_map`

```elixir
@spec deep_map(Enumerable.t(elem), (elem -&gt; result)) :: [result]
when elem: var, result: var
```

Apply a function to each element of an enumerable, recursively if the element is an enumerable itself.

## Examples

    iex> deep_map([1, [2, [3]], 4], &(&1 * 2))
    [2, [4, [6]], 8]

# `deobfuscate_password`

```elixir
@spec deobfuscate_password(Keyword.t()) :: Keyword.t()
```

Undo the obfuscation applied by `obfuscate_password/1`.

This function should be called just before passing connection options to one of
`Postgrex` functions. Never store deobfuscated password in any of our process
states.

# `encode_uuid`

Encode binary representation of a UUID into a string

## Examples

    iex> encode_uuid(<<1, 35, 69, 103, 137, 171, 76, 222, 143, 227, 251, 149, 223, 249, 31, 215>>)
    "01234567-89ab-4cde-8fe3-fb95dff91fd7"

# `escape_quotes`

# `extract_prefixed_keys_into_map`

```elixir
@spec extract_prefixed_keys_into_map(map(), String.t(), String.t()) :: map()
```

Extract keys from a map that start with a given prefix into a nested map.

## Examples

    iex> extract_prefixed_keys_into_map(%{"foo_bar" => "baz", "foo_moo" => "qux", "other" => "value"}, "foo")
    %{"foo" => %{"bar" => "baz", "moo" => "qux"}, "other" => "value"}

    iex> extract_prefixed_keys_into_map(%{"other" => "value"}, "foo")
    %{"other" => "value"}

# `flat_map_reduce_mark_last`

Flat map reduce that marks the last element of the enumerable.

This is equivalent to `Enum.flat_map_reduce/3`, but mapping function receives a boolean
indicating if the element is the last one.

## Examples

    iex> flat_map_reduce_mark_last(
    ...>   [1, 2, 3],
    ...>   0,
    ...>   fn
    ...>     x, false, acc -> {[x], acc + x}
    ...>     x, true, acc -> {[x * 2], acc + x}
    ...>   end
    ...> )
    {[1, 2, 6], 6}

# `inspect_relation`

```elixir
@spec inspect_relation({String.t(), String.t()}) :: String.t()
```

Output a 2-tuple relation (table) reference as pg-style `"schema"."table"`.

## Examples

    iex> inspect_relation({"schema", "table"})
    ~S|"schema"."table"|

# `list_reverse_map`

```elixir
@spec list_reverse_map(Enumerable.t(elem), (elem -&gt; result), [result]) :: [result]
when elem: var, result: var
```

Map each value of the enumerable using a mapper and reverse the resulting list.

Equivalent to `Enum.reverse/1` followed by `Enum.map/2`.

## Examples

    iex> list_reverse_map([1, 2, 3], &(&1 + 1))
    [4, 3, 2]

# `map_if_ok`

Map a value if it's in an OK tuple, but not otherwise.

## Examples

    iex> map_if_ok({:ok, "a"}, &String.upcase/1)
    {:ok, "A"}

    iex> map_if_ok({:error, :invalid}, &String.upcase/1)
    {:error, :invalid}

# `map_values`

```elixir
@spec map_values(map(), (term() -&gt; term())) :: map()
```

Apply a function to each value of a map.

# `map_while_ok`

```elixir
@spec map_while_ok(Enumerable.t(elem), (elem -&gt; {:ok, result} | {:error, term()})) ::
  {:ok, [result]} | {:error, term()}
when elem: var, result: var
```

Map each value of the enumerable using a mapper, unwrapping a result tuple returned by
the mapper and stopping on error.

## Examples

    iex> map_while_ok(["2015-01-23 23:50:07.0", "2015-01-23 23:50:08"], &NaiveDateTime.from_iso8601/1)
    {:ok, [~N[2015-01-23 23:50:07.0], ~N[2015-01-23 23:50:08]]}

    iex> map_while_ok(["2015-01-23 23:50:07A", "2015-01-23 23:50:08"], &NaiveDateTime.from_iso8601/1)
    {:error, :invalid_format}

# `merge_all`

```elixir
@spec merge_all([keyword()]) :: keyword()
```

# `obfuscate_password`

```elixir
@spec obfuscate_password(Keyword.t()) :: Keyword.t()
```

Given a keyword list of database connection options, obfuscate the password by wrapping it in
a zero-arity function.

This should be done as early as possible when parsing connection options from the OS env. The
aim of this obfuscation is to avoid accidentally leaking the password when inspecting connection
opts or logging them as part of a process state (which is done automatically by OTP when a
process that implements an OTP behaviour crashes).

# `parse_md_table`

```elixir
@spec parse_md_table(String.t(), [{:after, String.t()}]) :: [[String.t(), ...]]
```

Parse a markdown table from a string

Options:
- `after:` - taking a first table that comes right after a given substring.

## Example

    iex> """
    ...> Some text
    ...>
    ...> ## Known types
    ...>
    ...> | type                    | category | preferred? |
    ...> | ----------------------- | -------- | ---------- |
    ...> | bool                    | boolean  | t          |
    ...> | int2                    | numeric  |            |
    ...> """|> parse_md_table(after: "## Known types")
    [["bool", "boolean", "t"], ["int2", "numeric", ""]]

    iex> """
    ...> Some text
    ...> """|> parse_md_table([])
    []

# `parse_quoted_name`

Parses quoted names.
Lowercases unquoted names to match Postgres' case insensitivity.

## Examples
    iex> parse_quoted_name("foo")
    "foo"

    iex> parse_quoted_name(~S|"foo"|)
    "foo"

    iex> parse_quoted_name(~S|"fo""o"|)
    ~S|fo"o|

    iex> parse_quoted_name(~S|"FooBar"|)
    ~S|FooBar|

    iex> parse_quoted_name(~S|FooBar|)
    ~S|FooBar|

# `quote_name`

```elixir
@spec quote_name(String.t()) :: String.t()
```

Quote the given identifier for use in SQL queries.

## Examples
    iex> quote_name("foo")
    ~S|"foo"|

    iex> quote_name(~S|fo"o|)
    ~S|"fo""o"|

# `quote_string`

```elixir
@spec quote_string(String.t()) :: String.t()
```

Quote the given binary for use as a literal string in SQL queries.

# `reduce_while_ok`

```elixir
@spec reduce_while_ok(Enumerable.t(elem), acc, (elem, acc -&gt;
                                            {:ok, acc} | {:error, term()})) ::
  {:ok, acc} | {:error, term()}
when acc: var, elem: var
```

Reduce an enumerable while accumulating an accumulator, unwrapping a result tuple returned by
the reducer and stopping on error.

# `relation_to_sql`

Format a relation tuple to be correctly escaped for use in SQL queries.

## Examples

    iex> relation_to_sql({"public", "items"})
    ~S|public.items|

    iex> relation_to_sql({"public", "items"}, true)
    ~S|"public"."items"|

    iex> relation_to_sql({"public", "items-again"})
    ~S|public."items-again"|

    iex> relation_to_sql({"public", "99red_balloons"})
    ~S|public."99red_balloons"|

    iex> relation_to_sql({"public", "when"})
    ~S|public."when"|

    iex> relation_to_sql({"with spaces", ~S|and "quoted"!|})
    ~S|"with spaces"."and ""quoted""!"|

# `stream_add_side_effect`

Transform the stream to call a side-effect function for each element before continuing.

Acts like `Stream.each/2` but with an aggregate. `start_fun`, `last_fun`, `after_fun`
have the same semantics as in `Stream.transform/5`

# `stream_file_items`

```elixir
@spec stream_file_items(
  path :: String.t(),
  reader :: item_reader_fn(elem)
) :: Enumerable.t(sortable_binary(elem))
```

# `unzip_any`

Like `Enum.unzip/1`, but works for any tuple size instead of just 2.

Returns nil on empty list.

## Examples

    iex> unzip_any([{1, 2}, {3, 4}])
    {[1, 3], [2, 4]}

    iex> unzip_any([{1, 2, 3}, {4, 5, 6}])
    {[1, 4], [2, 5], [3, 6]}

    iex> unzip_any([{1, 2, 3, 4, 5, 6}, {7, 8, 9, 10, 11, 12}])
    {[1, 7], [2, 8], [3, 9], [4, 10], [5, 11], [6, 12]}

# `uuid4`

Generate a random UUID v4.

Code taken from Ecto: https://github.com/elixir-ecto/ecto/blob/v3.10.2/lib/ecto/uuid.ex#L174

## Examples

    iex> Regex.match?(~r/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, uuid4())
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
