macula_mri_nif (macula v1.4.23)

View Source

MRI (Macula Resource Identifier) operations for Macula mesh.

This module provides high-performance MRI parsing, validation, and hierarchy queries. It uses Rust NIFs when available, falling back to pure Erlang implementations otherwise.

NIF vs Erlang

The Rust NIFs provide significant performance improvements: - Parsing: ~3-5x faster (single-pass parsing) - Validation: ~4-6x faster (avoids binary_to_list conversion) - Path joining: ~5-10x faster (single allocation) - Hierarchy queries: ~10-20x faster (efficient prefix matching)

Trie Index for Million-Scale Deployments

For large-scale deployments (millions of MRIs), use the persistent index:

  %% Build index once (O(n))
  {ok, Index} = macula_mri_nif:build_path_index(AllMRIs),
 
  %% Fast O(d) queries instead of O(n)
  {ok, Children} = macula_mri_nif:index_find_children(Index, Realm, Path),
  {ok, Descendants} = macula_mri_nif:index_find_descendants(Index, Realm, Path),
 
  %% Dynamic updates for device churn
  ok = macula_mri_nif:index_insert(Index, Realm, Path, MRI),
  ok = macula_mri_nif:index_remove(Index, Realm, Path).

The pure Erlang fallbacks ensure the module works even when NIFs cannot be loaded (e.g., different architecture, missing Rust toolchain).

MRI Format

MRIs follow the format: mri:type:realm or mri:type:realm/path

Examples: mri:realm:io.macula — A realm mri:app:io.macula/counter — An app in the io.macula realm mri:service:io.macula/counter/api — A service endpoint

Summary

Functions

Build a trie index from a list of MRIs.

Find children of a parent MRI from a list of MRIs.

Find descendants of a parent MRI from a list of MRIs.

Format MRI components into an MRI string.

Find direct children using a trie index.

Find all descendants using a trie index.

Insert a single MRI into an existing index.

Remove a single MRI from an existing index.

Get the number of MRIs in an index.

Check if a type is a builtin MRI type.

Check if the NIF is loaded.

Join path segments into a single path binary.

Parse an MRI string into its components.

Validate realm format.

Validate segment characters.

Functions

build_path_index(MRIs)

-spec build_path_index(MRIs :: [{binary(), [binary()], binary()}]) -> {ok, reference() | map()}.

Build a trie index from a list of MRIs.

Creates a persistent trie index that enables O(d) queries where d is the path depth. For million-scale deployments, this is orders of magnitude faster than O(n) list scanning.

When NIFs are loaded, returns an opaque reference to the Rust trie. When using Erlang fallback, returns a map-based index (slower but functional).

Example:

  AllMRIs = [
      {<<"io.macula">>, [<<"apps">>], <<"mri:app:io.macula/apps">>},
      {<<"io.macula">>, [<<"apps">>, <<"counter">>], <<"mri:app:io.macula/apps/counter">>}
  ],
  {ok, Index} = macula_mri_nif:build_path_index(AllMRIs).

find_children(ParentRealm, ParentPath, AllMRIs)

-spec find_children(ParentRealm :: binary(),
                    ParentPath :: [binary()],
                    AllMRIs :: [{binary(), [binary()], binary()}]) ->
                       [binary()].

Find children of a parent MRI from a list of MRIs.

Children are MRIs that have the same realm, path starts with parent's path, and have exactly one more path segment.

The AllMRIs parameter is a list of {Realm, PathSegments, FullMRI} tuples.

Example:

  AllMRIs = [
      {<<"io.macula">>, [<<"apps">>], <<"mri:app:io.macula/apps">>},
      {<<"io.macula">>, [<<"apps">>, <<"counter">>], <<"mri:app:io.macula/apps/counter">>}
  ],
  [<<"mri:app:io.macula/apps/counter">>] =
      macula_mri_nif:find_children(<<"io.macula">>, [<<"apps">>], AllMRIs).

find_descendants(ParentRealm, ParentPath, AllMRIs)

-spec find_descendants(ParentRealm :: binary(),
                       ParentPath :: [binary()],
                       AllMRIs :: [{binary(), [binary()], binary()}]) ->
                          [binary()].

Find descendants of a parent MRI from a list of MRIs.

Descendants are MRIs that have the same realm, path starts with parent's path, and have at least one more path segment.

The AllMRIs parameter is a list of {Realm, PathSegments, FullMRI} tuples.

format_mri(Type, Realm, Path)

-spec format_mri(Type :: binary(), Realm :: binary(), Path :: [binary()]) ->
                    {ok, binary()} | {error, invalid_type | invalid_realm}.

Format MRI components into an MRI string.

Example:

  {ok, <<"mri:app:io.macula/counter">>} =
      macula_mri_nif:format_mri(<<"app">>, <<"io.macula">>, [<<"counter">>]).

index_find_children(Index, Realm, Path)

-spec index_find_children(Index :: reference() | map(), Realm :: binary(), Path :: [binary()]) ->
                             {ok, [binary()]} | {error, invalid_realm}.

Find direct children using a trie index.

O(d) complexity where d is the path depth, vs O(n) for list scanning.

Example:

  {ok, Index} = macula_mri_nif:build_path_index(AllMRIs),
  {ok, [<<"mri:app:io.macula/apps/counter">>]} =
      macula_mri_nif:index_find_children(Index, <<"io.macula">>, [<<"apps">>]).

index_find_descendants(Index, Realm, Path)

-spec index_find_descendants(Index :: reference() | map(), Realm :: binary(), Path :: [binary()]) ->
                                {ok, [binary()]} | {error, invalid_realm}.

Find all descendants using a trie index.

O(d + m) complexity where d is path depth and m is number of descendants, vs O(n) for list scanning where n is total MRIs.

index_insert(Index, Realm, Path, MRI)

-spec index_insert(Index :: reference() | map(), Realm :: binary(), Path :: [binary()], MRI :: binary()) ->
                      ok | {error, invalid_realm}.

Insert a single MRI into an existing index.

Use this for dynamic updates when devices connect.

Example:

  ok = macula_mri_nif:index_insert(Index, <<"io.macula">>, [<<"devices">>, <<"sensor1">>],
                                    <<"mri:device:io.macula/devices/sensor1">>).

index_remove(Index, Realm, Path)

-spec index_remove(Index :: reference() | map(), Realm :: binary(), Path :: [binary()]) ->
                      ok | {error, not_found | invalid_realm}.

Remove a single MRI from an existing index.

Use this for dynamic updates when devices disconnect.

index_size(Index)

-spec index_size(Index :: reference() | map()) -> {ok, non_neg_integer()}.

Get the number of MRIs in an index.

is_builtin_type(Type)

-spec is_builtin_type(Type :: binary()) -> boolean().

Check if a type is a builtin MRI type.

Builtin types: realm, org, user, app, service, artifact, license, cert, key, topic, proc, content, device, cluster, location, zone, network, model, dataset, config, class, taxonomy.

is_nif_loaded()

-spec is_nif_loaded() -> boolean().

Check if the NIF is loaded.

join_path_segments(Segments)

-spec join_path_segments(Segments :: [binary()]) -> binary().

Join path segments into a single path binary.

Example:

  <<"foo/bar/baz">> = macula_mri_nif:join_path_segments([<<"foo">>, <<"bar">>, <<"baz">>]).

nif_build_path_index(MRIs)

nif_find_children(ParentRealm, ParentPath, AllMRIs)

nif_find_descendants(ParentRealm, ParentPath, AllMRIs)

nif_format_mri(Type, Realm, Path)

nif_index_find_children(Index, Realm, Path)

nif_index_find_descendants(Index, Realm, Path)

nif_index_insert(Index, Realm, Path, MRI)

nif_index_remove(Index, Realm, Path)

nif_index_size(Index)

nif_is_builtin_type(Type)

nif_join_path_segments(Segments)

nif_parse_mri(MRI)

nif_validate_realm_format(Realm)

nif_validate_segment_chars(Segment)

parse_mri(MRI)

-spec parse_mri(MRI :: binary()) ->
                   {ok, #{type := binary(), realm := binary(), path := [binary()]}} |
                   {error, invalid_format | invalid_realm | invalid_segment}.

Parse an MRI string into its components.

Returns a map with type, realm, and path keys.

Example:

  {ok, #{type := <<"app">>, realm := <<"io.macula">>, path := [<<"counter">>]}} =
      macula_mri_nif:parse_mri(<<"mri:app:io.macula/counter">>).

validate_realm_format(Realm)

-spec validate_realm_format(Realm :: binary()) -> boolean().

Validate realm format.

Valid realm: reverse domain notation (e.g., "io.macula.example") Must contain at least one dot and only lowercase letters, digits, and dots.

validate_segment_chars(Segment)

-spec validate_segment_chars(Segment :: binary()) -> boolean().

Validate segment characters.

Valid segment: a-z, 0-9, hyphen (-), underscore (_)