defmodule Noizu.ElixirCore.Guards do @moduledoc """ Provides guards for caller context and reference checks. ## Calling Context Guards The following guards are used to validate the caller context: - `is_caller_context/1`: Checks if the value is a struct of type `Noizu.ElixirCore.CallingContext`. - `caller_context_with_permissions/1`: Checks if the value is a caller context struct with a `permissions` map. The following guards are used to validate specific caller types: - `is_system_caller/1`: Checks if the value is a system caller with the `system` permission. - `is_admin_caller/1`: Checks if the value is an admin caller with the `admin` permission. - `is_internal_caller/1`: Checks if the value is an internal caller with the `internal` permission. - `is_restricted_caller/1`: Checks if the value is a restricted caller without any permissions or with the `restricted` permission set to true. ## Caller Permission Macros The following macros are used to check caller permissions: - `caller_permission?(term, permission)`: Checks if the given `term` has the specified `permission`. - `caller_permission_value?(term, permission, value)`: Checks if the given `term` has the specified `permission` with the specified `value`. ## Ref Guards The following guards are used for reference checks: - `is_ref/1`: Checks if the value is a reference tuple of the form `{:ref, Module, identifier}`. - `is_sref/1`: Checks if the value is a reference string of the form `"ref.module.identifier"`. - `entity_ref/1`: Checks if the value is a reference tuple, reference string, or a struct with a `vsn` field. # Code Review - The code is well-documented and follows the Elixir naming conventions. - The guards are defined clearly and provide useful abstractions for caller context and reference checks. """ # ----------------------- # Calling Context Guards # ----------------------- defguard is_caller_context(value) when value != nil and is_struct(value, Noizu.ElixirCore.CallingContext) defguard caller_context_with_permissions(value) when is_caller_context(value) and is_map(value.auth) and is_map_key(value.auth, :permissions) and is_map(value.auth.permissions) defguard is_system_caller(value) when caller_context_with_permissions(value) and is_map_key(value.auth.permissions, :system) and value.auth.permissions.system == true defguard is_admin_caller(value) when caller_context_with_permissions(value) and is_map_key(value.auth.permissions, :admin) and value.auth.permissions.admin == true defguard is_internal_caller(value) when caller_context_with_permissions(value) and is_map_key(value.auth.permissions, :internal) and value.auth.permissions.internal == true defguard is_restricted_caller(value) when not caller_context_with_permissions(value) or (is_map_key(value.auth.permissions, :restricted) and value.auth.permissions.restricted == true) defmacro caller_permission?(term, permission) do case __CALLER__.context do nil -> quote generated: true do case unquote(permission) do permission when is_atom(permission) or is_tuple(permission) -> case unquote(term) do %{auth: %{permissions: p}} -> p[permission] _ -> false end _ -> raise ArgumentError end end :match -> raise ArgumentError, "invalid expression in match, #{:has_permission?} is not allowed in patterns " <> "such as function clauses, case clauses or on the left side of the = operator" :guard -> quote do is_map(unquote(term)) and (is_atom(unquote(permission)) or is_tuple(unquote(permission)) or :fail) and :erlang.is_map_key(:auth, unquote(term)) and :erlang.is_map(unquote(term).auth) and :erlang.is_map_key(:permissions, unquote(term).auth) and :erlang.is_map(unquote(term).auth.permissions) and :erlang.is_map_key(unquote(permission), unquote(term).auth.permissions) and :erlang.map_get(unquote(permission), unquote(term).auth.permissions) == true end end end defmacro caller_permission_value?(term, permission, value) do case __CALLER__.context do nil -> quote generated: true do case unquote(permission) do permission when is_atom(permission) or is_tuple(permission) -> case unquote(term) do %{auth: %{permissions: p}} -> p[permission] === unquote(value) _ -> false end _ -> raise ArgumentError end end :match -> raise ArgumentError, "invalid expression in match, #{:has_permission?} is not allowed in patterns " <> "such as function clauses, case clauses or on the left side of the = operator" :guard -> quote do is_map(unquote(term)) and (is_atom(unquote(permission)) or is_tuple(unquote(permission)) or :fail) and :erlang.is_map_key(:auth, unquote(term)) and :erlang.is_map(unquote(term).auth) and :erlang.is_map_key(:permissions, unquote(term).auth) and :erlang.is_map(unquote(term).auth.permissions) and :erlang.is_map_key(unquote(permission), unquote(term).auth.permissions) and :erlang.map_get(unquote(permission), unquote(term).auth.permissions) === unquote(value) end end end defguard permission?(context, permission) when caller_permission?(context, permission) defguard permission?(context, permission, value) when caller_permission_value?(context, permission, value) defguard has_call_reason?(value) when is_map(value) and is_map_key(value, :reason) and value.reason != :none and value.reason != nil # ----------------------- # Ref Guard # ----------------------- defguard is_ref(value) when is_tuple(value) and tuple_size(value) == 3 and elem(value, 0) == :ref defguard is_sref(value) when is_bitstring(value) and binary_part(value, 0, 4) == "ref." defguard entity_ref(value) when is_ref(value) or is_sref(value) or (is_struct(value) and is_map_key(value, :vsn) and value.vsn != nil) end