AshIntrospection.FieldFormatter (AshIntrospection v0.3.0)
View SourceHandles field name formatting for input parameters, output fields, and code generation.
Supports built-in formatters (:camel_case, :pascal_case, :snake_case) and custom formatter functions specified as {module, function} or {module, function, extra_args}.
Summary
Functions
Formats a field name using the configured formatter.
Formats a field name using the configured formatter.
Formats a map of fields, converting all keys using the specified formatter.
Recursively formats every key in a nested structure for client consumption.
Parses input field names from client format to internal format.
Parses a map of input fields, converting all keys from client format to internal format.
Recursively parses input values, handling nested structures.
Resolves a field name to the atom that already names that field.
Functions
Formats a field name using the configured formatter.
Examples
iex> AshIntrospection.FieldFormatter.format_field(:user_name, :camel_case)
"userName"
iex> AshIntrospection.FieldFormatter.format_field(:user_name, :snake_case)
"user_name"
iex> AshIntrospection.FieldFormatter.format_field(:user_name, :pascal_case)
"UserName"
Formats a field name using the configured formatter.
Examples
iex> AshIntrospection.FieldFormatter.format_field_name(:user_name, :camel_case)
"userName"
iex> AshIntrospection.FieldFormatter.format_field_name(:user_name, :snake_case)
"user_name"
iex> AshIntrospection.FieldFormatter.format_field_name("user_name", :pascal_case)
"UserName"
Formats a map of fields, converting all keys using the specified formatter.
Examples
iex> AshIntrospection.FieldFormatter.format_fields(%{user_name: "John", user_email: "john@example.com"}, :camel_case)
%{"userName" => "John", "userEmail" => "john@example.com"}
Recursively formats every key in a nested structure for client consumption.
Walks maps and lists, converting each key with formatter. Structs and
primitives are returned untouched, and a key that is neither an atom nor a
binary is left as it is.
Used for any payload handed to the client without passing through a type-driven formatter — the RPC response envelope and error payloads both rely on it, so their field names agree.
Examples
iex> AshIntrospection.FieldFormatter.format_output_field_names(%{short_message: "x"}, :camel_case)
%{"shortMessage" => "x"}
iex> AshIntrospection.FieldFormatter.format_output_field_names([%{user_name: "a"}], :camel_case)
[%{"userName" => "a"}]
Parses input field names from client format to internal format.
This is used for converting incoming client field names to the internal Elixir atom keys that Ash expects.
Examples
iex> AshIntrospection.FieldFormatter.parse_input_field("userName", :camel_case)
:user_name
Parses a map of input fields, converting all keys from client format to internal format.
Recursively processes nested maps and arrays to ensure all field names are properly formatted. This is essential for union types and embedded resources that contain nested field structures.
Examples
iex> AshIntrospection.FieldFormatter.parse_input_fields(%{"userName" => "John", "userEmail" => "john@example.com"}, :camel_case)
%{user_name: "John", user_email: "john@example.com"}
iex> AshIntrospection.FieldFormatter.parse_input_fields(%{"attachments" => [%{"mimeType" => "pdf", "attachmentType" => "file"}]}, :camel_case)
%{attachments: [%{mime_type: "pdf", attachment_type: "file"}]}
Recursively parses input values, handling nested structures.
This function ensures that all nested maps and arrays containing maps have their field names properly formatted according to the formatter.
Only handles JSON-decoded data (maps, lists, primitives) - no structs.
Resolves a field name to the atom that already names that field.
Atoms pass through unchanged. A string is parsed into internal form by the formatter and resolved against the existing atom table; when no such atom exists the parsed string is returned unchanged.
Every field a resource or type declares gets its atom at compile time, so a valid name always resolves. An unresolved name is one no field has, and callers compare the result against the declared field atoms — a string never matches one, so it fails as an unknown field.
This deliberately never calls String.to_atom/1. Field names come from
clients and the atom table is never garbage collected, so minting one atom per
name lets an unauthenticated caller exhaust it and take the node down.
Examples
iex> AshIntrospection.FieldFormatter.resolve_field_name("userName", :camel_case)
:user_name
iex> AshIntrospection.FieldFormatter.resolve_field_name(:user_name, :snake_case)
:user_name