Foundation.Utils (foundation v0.1.0)
Pure utility functions for the Foundation layer.
Contains helper functions that are used across the Foundation layer. All functions are pure and have no side effects.
Summary
Functions
Convert atom keys to string keys recursively.
Check if a term is blank (nil, empty string, empty list, etc.).
Merge two maps recursively.
Calculate the deep size of a term.
Format byte size into human-readable string.
Format duration in nanoseconds to human readable string.
Generate a correlation ID string in UUID v4 format.
Generate a unique ID using monotonic time and randomness.
Get a nested value from a map with a default.
Measure execution time of a function in microseconds.
Measure memory consumption before and after a function execution.
Get monotonic timestamp in milliseconds.
Check if a term is present (not blank).
Returns process statistics for the current process.
Put a nested value in a map.
Retry a function with exponential backoff.
Safely convert a term to string representation.
Sanitize a string for safe logging/display.
Convert atom keys to string keys recursively.
Returns system statistics.
Truncate data if it's too large for storage.
Truncate data if it's too large for storage with custom size limit.
Check if a value is a valid positive integer.
Get current wall clock timestamp.
Functions
Convert atom keys to string keys recursively.
Examples
iex> data = %{key: %{nested: "value"}}
iex> Foundation.Utils.atomize_keys(data)
%{key: %{nested: "value"}}
Check if a term is blank (nil, empty string, empty list, etc.).
Examples
iex> Foundation.Utils.blank?(nil)
true
iex> Foundation.Utils.blank?("")
true
iex> Foundation.Utils.blank?("hello")
false
Merge two maps recursively.
Examples
iex> map1 = %{a: %{b: 1}, c: 2}
iex> map2 = %{a: %{d: 3}, e: 4}
iex> Foundation.Utils.deep_merge(map1, map2)
%{a: %{b: 1, d: 3}, c: 2, e: 4}
@spec deep_size(term()) :: non_neg_integer()
Calculate the deep size of a term.
Examples
iex> size = Foundation.Utils.deep_size(%{a: 1, b: [1, 2, 3]})
iex> is_integer(size) and size > 0
true
@spec format_bytes(non_neg_integer()) :: String.t()
Format byte size into human-readable string.
Examples
iex> Foundation.Utils.format_bytes(1024)
"1.0 KB"
iex> Foundation.Utils.format_bytes(1536)
"1.5 KB"
iex> Foundation.Utils.format_bytes(1048576)
"1.0 MB"
@spec format_duration(non_neg_integer()) :: String.t()
Format duration in nanoseconds to human readable string.
Examples
iex> Foundation.Utils.format_duration(1_500_000_000)
"1.5s"
iex> Foundation.Utils.format_duration(2_500_000)
"2.5ms"
@spec generate_correlation_id() :: String.t()
Generate a correlation ID string in UUID v4 format.
Examples
iex> correlation_id = Foundation.Utils.generate_correlation_id()
iex> String.length(correlation_id)
36
iex> String.match?(correlation_id, ~r/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
true
@spec generate_id() :: pos_integer()
Generate a unique ID using monotonic time and randomness.
Examples
iex> id1 = Foundation.Utils.generate_id()
iex> id2 = Foundation.Utils.generate_id()
iex> id1 != id2
true
Get a nested value from a map with a default.
Examples
iex> data = %{a: %{b: %{c: 42}}}
iex> Foundation.Utils.get_nested(data, [:a, :b, :c], 0)
42
iex> Foundation.Utils.get_nested(data, [:x, :y], "default")
"default"
@spec measure((-> result)) :: {result, non_neg_integer()} when result: any()
Measure execution time of a function in microseconds.
Examples
iex> {result, duration} = Foundation.Utils.measure(fn -> :timer.sleep(10); :ok end)
iex> result
:ok
iex> duration > 10_000 # At least 10ms in microseconds
true
@spec measure_memory((-> result)) :: {result, {non_neg_integer(), non_neg_integer(), integer()}} when result: any()
Measure memory consumption before and after a function execution.
Examples
iex> {result, {before, after, diff}} = Foundation.Utils.measure_memory(fn -> "test" end)
iex> result
"test"
iex> is_integer(before) and is_integer(after) and is_integer(diff)
true
@spec monotonic_timestamp() :: integer()
Get monotonic timestamp in milliseconds.
Examples
iex> timestamp = Foundation.Utils.monotonic_timestamp()
iex> is_integer(timestamp)
true
Check if a term is present (not blank).
Examples
iex> Foundation.Utils.present?("hello")
true
iex> Foundation.Utils.present?(nil)
false
@spec process_stats() :: %{ garbage_collection: any(), memory: any(), message_queue_len: any(), reductions: any(), status: any() }
Returns process statistics for the current process.
Examples
iex> stats = Foundation.Utils.process_stats()
iex> Map.has_key?(stats, :memory)
true
iex> Map.has_key?(stats, :message_queue_len)
true
Put a nested value in a map.
Examples
iex> data = %{}
iex> Foundation.Utils.put_nested(data, [:a, :b, :c], 42)
%{a: %{b: %{c: 42}}}
Retry a function with exponential backoff.
Examples
iex> result = Foundation.Utils.retry(fn -> :ok end, max_attempts: 3)
{:ok, :ok}
iex> result = Foundation.Utils.retry(fn -> {:error, :failed} end, max_attempts: 2)
{:error, :max_attempts_exceeded}
Safely convert a term to string representation.
Examples
iex> Foundation.Utils.safe_inspect(%{key: "value"})
"%{key: "value"}"
iex> Foundation.Utils.safe_inspect(:atom)
":atom"
Sanitize a string for safe logging/display.
Examples
iex> Foundation.Utils.sanitize_string("hello\nworld\ttab")
"hello world tab"
Convert atom keys to string keys recursively.
Examples
iex> data = %{key: %{nested: "value"}}
iex> Foundation.Utils.stringify_keys(data)
%{"key" => %{"nested" => "value"}}
@spec system_stats() :: %{ atom_count: any(), memory: [ {:atom | :atom_used | :binary | :code | :ets | :processes | :processes_used | :system | :total, non_neg_integer()}, ... ], process_count: non_neg_integer(), scheduler_count: pos_integer(), scheduler_online: pos_integer() }
Returns system statistics.
Examples
iex> stats = Foundation.Utils.system_stats()
iex> Map.has_key?(stats, :process_count)
true
iex> Map.has_key?(stats, :memory)
true
Truncate data if it's too large for storage.
Examples
iex> small_data = [1, 2, 3]
iex> Foundation.Utils.truncate_if_large(small_data)
[1, 2, 3]
iex> large_data = String.duplicate("x", 100_000)
iex> result = Foundation.Utils.truncate_if_large(large_data)
iex> is_map(result) and Map.has_key?(result, :truncated)
true
@spec truncate_if_large(term(), pos_integer()) :: term()
Truncate data if it's too large for storage with custom size limit.
Examples
iex> small_data = [1, 2, 3]
iex> Foundation.Utils.truncate_if_large(small_data, 1000)
[1, 2, 3]
iex> large_data = String.duplicate("x", 2000)
iex> result = Foundation.Utils.truncate_if_large(large_data, 1000)
iex> is_map(result) and Map.has_key?(result, :truncated)
true
Check if a value is a valid positive integer.
Examples
iex> Foundation.Utils.valid_positive_integer?(42)
true
iex> Foundation.Utils.valid_positive_integer?(0)
false
iex> Foundation.Utils.valid_positive_integer?(-1)
false
iex> Foundation.Utils.valid_positive_integer?("42")
false
@spec wall_timestamp() :: integer()
Get current wall clock timestamp.
Examples
iex> timestamp = Foundation.Utils.wall_timestamp()
iex> is_integer(timestamp)
true