Packages
ash
3.25.0
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Retired package: fast-follow breaking change
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/info/manifest.ex
# SPDX-FileCopyrightText: 2025 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule Ash.Info.Manifest do
@moduledoc """
Generates a language-agnostic API specification from Ash resources and actions.
Given a list of `{resource, action_name}` tuples or an OTP app, traverses the
type graph to find all reachable resources and types, producing structured IR
(Elixir structs) that can also be serialized to JSON.
## Operator canonical name vs. alias
`%Operator{}.name` is the user-facing symbol that appears in
`Ash.Query.filter` expressions (e.g. `:==`, `:<`, `:in`). The
`%Operator{}.aliases` list carries internal/legacy module-derived names
(e.g. `:eq`, `:less_than`) for backward-compatible rendering. New code
should prefer `name`; alias-substitution is for tools that historically
rendered the module-derived spelling.
"""
alias Ash.Info.Manifest.{Entrypoint, Resource}
@schema_version "1.0.0"
@type t :: %__MODULE__{
resources: [Resource.t()],
types: [Ash.Info.Manifest.Type.t()],
entrypoints: [Entrypoint.t()],
filter_capabilities: Ash.Info.Manifest.FilterCapabilities.t() | nil,
sort_capabilities: Ash.Info.Manifest.SortCapabilities.t() | nil,
custom: map()
}
@type resource_lookup :: %{atom() => Resource.t()}
@type action_lookup :: %{{atom(), atom()} => Ash.Info.Manifest.Action.t()}
@type type_lookup :: %{atom() => Ash.Info.Manifest.Type.t()}
defstruct resources: [],
types: [],
entrypoints: [],
filter_capabilities: nil,
sort_capabilities: nil,
custom: %{}
@doc """
The schema version of the manifest's JSON serialization format.
Bumped when the JSON output schema in `Ash.Info.Manifest.JsonSerializer`
changes in a way downstream tools need to detect.
"""
@spec schema_version() :: String.t()
def schema_version, do: @schema_version
@doc """
Generate an API specification for the given OTP app.
## Options
* `:otp_app` - The OTP app to scan for Ash domains and resources (required)
* `:action_entrypoints` - Optional list of `{resource_module, action_name}` tuples
used as entrypoints for deriving the spec. When omitted, all public actions
across all domains are included.
"""
@spec generate(keyword()) :: {:ok, t()} | {:error, term()}
def generate(opts) do
Ash.Info.Manifest.Generator.generate(opts)
end
@doc """
Builds a resource lookup map from the spec, keyed by resource module.
"""
@spec resource_lookup(t()) :: resource_lookup()
def resource_lookup(%__MODULE__{resources: resources}) do
Map.new(resources, fn r -> {r.module, r} end)
end
@doc """
Builds an action lookup map from the spec, keyed by `{resource_module, action_name}`.
"""
@spec action_lookup(t()) :: action_lookup()
def action_lookup(%__MODULE__{entrypoints: entrypoints}) do
Map.new(entrypoints, fn e -> {{e.resource, e.action.name}, e.action} end)
end
@doc """
Builds a type lookup map from the spec, keyed by named type module.
Each entry is the fully-resolved `%Ash.Info.Manifest.Type{}` for a named type
(Ash.Type.Enum implementations and Ash.Type.NewType subtypes) referenced
somewhere in the reachable type graph.
"""
@spec type_lookup(t()) :: type_lookup()
def type_lookup(%__MODULE__{types: types}) do
Map.new(types, fn type -> {type.module, type} end)
end
@doc """
Generates a spec and returns the resource lookup map directly.
"""
@spec generate_resource_lookup(keyword()) :: {:ok, resource_lookup()} | {:error, term()}
def generate_resource_lookup(opts) do
{:ok, spec} = generate(opts)
{:ok, resource_lookup(spec)}
end
@doc "Looks up a resource by module. Returns nil if not found."
@spec get_resource(resource_lookup(), atom()) :: Resource.t() | nil
def get_resource(resource_lookup, module) when is_map(resource_lookup) do
Map.get(resource_lookup, module)
end
@doc "Looks up a resource by module. Raises if not found."
@spec get_resource!(resource_lookup(), atom()) :: Resource.t()
def get_resource!(resource_lookup, module) when is_map(resource_lookup) do
case Map.get(resource_lookup, module) do
%Resource{} = r -> r
nil -> raise "Resource #{inspect(module)} not found in resource lookup"
end
end
@doc "Checks if a resource exists in the lookup."
@spec has_resource?(resource_lookup(), atom()) :: boolean()
def has_resource?(resource_lookup, module) when is_map(resource_lookup) do
Map.has_key?(resource_lookup, module)
end
@doc "Gets a field by resource module and field name."
@spec get_field(resource_lookup(), atom(), atom()) :: Ash.Info.Manifest.Field.t() | nil
def get_field(resource_lookup, resource_module, field_name) do
with %Resource{} = r <- Map.get(resource_lookup, resource_module) do
Resource.get_field(r, field_name)
end
end
@doc "Gets a relationship by resource module and relationship name."
@spec get_relationship(resource_lookup(), atom(), atom()) ::
Ash.Info.Manifest.Relationship.t() | nil
def get_relationship(resource_lookup, resource_module, rel_name) do
with %Resource{} = r <- Map.get(resource_lookup, resource_module) do
Resource.get_relationship(r, rel_name)
end
end
@doc """
Gets a field or relationship by name, checking fields first.
Returns `%Ash.Info.Manifest.Field{}`, `%Ash.Info.Manifest.Relationship{}`, or nil.
"""
@spec get_field_or_relationship(resource_lookup(), atom(), atom()) ::
Ash.Info.Manifest.Field.t() | Ash.Info.Manifest.Relationship.t() | nil
def get_field_or_relationship(resource_lookup, resource_module, name) do
case get_field(resource_lookup, resource_module, name) do
%Ash.Info.Manifest.Field{} = field -> field
nil -> get_relationship(resource_lookup, resource_module, name)
end
end
@doc "Gets an action by resource module and action name from an action lookup."
@spec get_action(action_lookup(), atom(), atom()) :: Ash.Info.Manifest.Action.t() | nil
def get_action(action_lookup, resource_module, action_name) do
Map.get(action_lookup, {resource_module, action_name})
end
@doc "Looks up a named type's full definition by module. Returns nil if not found."
@spec get_type(type_lookup(), atom()) :: Ash.Info.Manifest.Type.t() | nil
def get_type(type_lookup, module) when is_map(type_lookup) and is_atom(module) do
Map.get(type_lookup, module)
end
@doc """
Looks up a named type's full definition by module. Raises if not found.
A miss indicates a reachability bug: the type was referenced via `:type_ref`
somewhere in the type graph but was never registered during spec generation.
Reachability analysis (`Ash.Info.Manifest.Generator.Reachability`) should have
collected it as a standalone type.
"""
@spec get_type!(type_lookup(), atom()) :: Ash.Info.Manifest.Type.t()
def get_type!(type_lookup, module) when is_map(type_lookup) and is_atom(module) do
case Map.get(type_lookup, module) do
%Ash.Info.Manifest.Type{} = t ->
t
nil ->
raise """
Named type #{inspect(module)} not found in type lookup.
This indicates a reachability bug — the type was referenced via :type_ref
somewhere in the spec but was not collected as a standalone type during
generation. Check `Ash.Info.Manifest.Generator.Reachability.find_reachable/2`.
"""
end
end
@doc "Gets an identity by resource module and identity name."
@spec get_identity(resource_lookup(), atom(), atom()) :: %{keys: [atom()]} | nil
def get_identity(resource_lookup, resource_module, identity_name) do
with %Resource{} = r <- Map.get(resource_lookup, resource_module) do
Resource.get_identity(r, identity_name)
end
end
@doc "Gets the primary key field names by resource module."
@spec primary_key(resource_lookup(), atom()) :: [atom()]
def primary_key(resource_lookup, resource_module) do
case Map.get(resource_lookup, resource_module) do
%Resource{primary_key: pk} -> pk
nil -> []
end
end
@doc """
Builds an operator lookup map from the spec, keyed by operator name atom.
Returns an empty map when `filter_capabilities` is `nil`.
"""
@spec operator_lookup(t()) :: %{atom() => Ash.Info.Manifest.Operator.t()}
def operator_lookup(%__MODULE__{filter_capabilities: nil}), do: %{}
def operator_lookup(%__MODULE__{filter_capabilities: %{operators: operators}}) do
Map.new(operators, fn op -> {op.name, op} end)
end
@doc """
Builds a function lookup map from the spec, keyed by function name atom.
"""
@spec function_lookup(t()) :: %{atom() => Ash.Info.Manifest.Function.t()}
def function_lookup(%__MODULE__{filter_capabilities: nil}), do: %{}
def function_lookup(%__MODULE__{filter_capabilities: %{functions: functions}}) do
Map.new(functions, fn fun -> {fun.name, fun} end)
end
@doc """
Builds a custom-expression lookup map from the spec, keyed by expression name atom.
"""
@spec custom_expression_lookup(t()) :: %{atom() => Ash.Info.Manifest.CustomExpression.t()}
def custom_expression_lookup(%__MODULE__{filter_capabilities: nil}), do: %{}
def custom_expression_lookup(%__MODULE__{
filter_capabilities: %{custom_expressions: custom_expressions}
}) do
Map.new(custom_expressions, fn ce -> {ce.name, ce} end)
end
@doc """
Returns the resolved `%ApplicableOperator{}` list for `{resource_module, field_name}`.
Returns `[]` when the field exists but is not filterable. Raises if the
resource or field is unknown.
"""
@spec applicable_filter_operators(t(), {atom(), atom()}) ::
[Ash.Info.Manifest.ApplicableOperator.t()]
def applicable_filter_operators(%__MODULE__{} = manifest, {resource_module, field_name}) do
case lookup_field!(manifest, resource_module, field_name) do
%{filter_operators: nil} -> []
%{filter_operators: list} when is_list(list) -> list
end
end
@doc """
Returns the resolved `%ApplicableFunction{}` list for `{resource_module, field_name}`.
See `applicable_filter_operators/2` for behavior.
"""
@spec applicable_filter_functions(t(), {atom(), atom()}) ::
[Ash.Info.Manifest.ApplicableFunction.t()]
def applicable_filter_functions(%__MODULE__{} = manifest, {resource_module, field_name}) do
case lookup_field!(manifest, resource_module, field_name) do
%{filter_functions: nil} -> []
%{filter_functions: list} when is_list(list) -> list
end
end
@doc """
Returns the resolved `%ApplicableCustomExpression{}` list for `{resource_module, field_name}`.
See `applicable_filter_operators/2` for behavior.
"""
@spec applicable_filter_custom_expressions(t(), {atom(), atom()}) ::
[Ash.Info.Manifest.ApplicableCustomExpression.t()]
def applicable_filter_custom_expressions(
%__MODULE__{} = manifest,
{resource_module, field_name}
) do
case lookup_field!(manifest, resource_module, field_name) do
%{filter_custom_expressions: nil} -> []
%{filter_custom_expressions: list} when is_list(list) -> list
end
end
defp lookup_field!(manifest, resource_module, field_name) do
lookup = resource_lookup(manifest)
resource =
case Map.get(lookup, resource_module) do
%Resource{} = r -> r
nil -> raise "Resource #{inspect(resource_module)} not found in manifest"
end
case Resource.get_field(resource, field_name) do
%Ash.Info.Manifest.Field{} = field ->
field
nil ->
raise "Field #{inspect(field_name)} not found on resource #{inspect(resource_module)}"
end
end
end