DSL reference

v0.1.4

This page lists every option of the guardedstruct section and of the five entities you declare inside it: field, virtual_field, dynamic_field, sub_field and conditional_field. It describes GuardedStruct 0.1.4. For worked examples, start from Get started.

Section options

Pass these to guardedstruct, as in guardedstruct enforce: true, json: true do ... end.

OptionTypeDefaultEffect
enforcebooleanfalseRequires every field that has no default. A field can opt out with enforce: false. It does not reach inside a sub_field.
opaquebooleanfalseGenerates @opaque t() instead of @type t().
modulemodule namenoneGenerates the struct in a module nested under the current one. module: Struct inside MyApp.User generates MyApp.User.Struct, and a full name such as MyApp.User.Struct nests again as MyApp.User.MyApp.User.Struct. The outer module then has no builder.
errorbooleanfalseGenerates the exception module <Module>.Error.
authorized_fieldsbooleanfalseTurns every undeclared key of the input into an authorized_fields error.
jsonbooleanfalseDerives a JSON encoder: Jason.Encoder if Jason is available, otherwise JSON.Encoder on Elixir 1.18 and later, otherwise nothing. Generated sub-modules get it too.
main_validator{Module, :function}noneCalled with the attributes after the field validators ran. A main_validator/1 defined in the module runs only when this option is not set.
validate_derivemodule or list of modulesnoneModules whose validate/3 serves the validate ops that no built-in op or derive extension handles. They are tried before the application config of the same name, which still serves the ops they leave unhandled. A sub_field uses them too.
sanitize_derivemodule or list of modulesnoneModules whose sanitize/2 serves the sanitize ops that no built-in op or derive extension handles. They are tried before the application config of the same name, which still serves the ops they leave unhandled. A sub_field uses them too.
auto_wirebooleanfalseOnly in an Ash resource: adds GuardedStruct.AshResource.Change to the resource. No effect elsewhere.

Entities

EntitySignatureUse
fieldfield name, type, optsOne value. With a regex as the name it describes a map with no fixed keys.
virtual_fieldvirtual_field name, type, optsA value that is checked and then left out of the struct.
dynamic_fielddynamic_field name, optsA map kept exactly as sent. The type defaults to map(), the default value to %{} and the derive string to validate(map).
sub_fieldsub_field name, type, opts do ... endA struct declared inline, as its own module.
conditional_fieldconditional_field name, type, opts do ... endOne name with several possible shapes.

A sub_field and a conditional_field hold field, sub_field and conditional_field entries. A sub_field also takes virtual_field and dynamic_field entries, and they behave as they do at the top level. The name of a sub_field and a conditional_field is an atom. The name of a field is an atom or a regex.

Options by entity

Optionfieldvirtual_fielddynamic_fieldsub_fieldconditional_field
enforce, defaultYesYesYesYesYes
derives, deriveYesYesYesYesYes
validatorYesYesYesYesYes
auto, from, on, domainYesYesYesYesYes
hintYesYesYesYesYes
struct, structsYesNoNoYesYes
priorityYesNoNoYesYes
error, authorized_fields, main_validatorNoNoNoYesNo

Option reference

OptionValueEffect
nameatom, or regex for a fieldThe key. Declaring the same atom twice raises an ArgumentError.
typetypespecThe type of the key in the generated @type t(). A key that is not enforced gets | nil added.
enforcebooleanRequires the key. A missing key is a required_fields error. On a sub_field it also requires the fields inside that have no default.
defaultvalueThe value in the struct when the key is absent. Maps and tuples are allowed.
derivesstringThe sanitize and validate ops. See Derive string syntax.
derivestringThe old name of derives. It still works, and the compiler warns that it is deprecated.
validator{Module, :function}Called as Module.function(field, value). See Write a field validator and a main validator.
auto{Module, :function} or {Module, :function, arg}Fills the field by calling the function. A list as arg is spread as arguments.
frompath stringCopies the value at the path into the field when that value is not nil.
onpath stringWhen the field has a value, requires a value at the path, or a dependent_keys error follows.
domainrule stringLimits the field by values elsewhere in the input. See the next section.
structmoduleBuilds the value with Module.builder/1.
structsmodule or trueWith a module, builds a list of values with Module.builder/1. With true on a sub_field or a conditional_field, the value is a list.
hintstringCopied as __hint__ into the errors of a child of a conditional_field.
prioritybooleanOn a conditional_field, keeps only the first error. A field and a sub_field accept it with no effect.
errorbooleanOn a sub_field, generates an Error exception module inside it.
authorized_fieldsbooleanOn a sub_field, rejects undeclared keys inside it.
main_validator{Module, :function}On a sub_field. Called with the attributes of that nested map after its field validators ran. A main_validator/1 of the parent module does not run for a sub_field.

Path and rule strings

A from or on path is a list of keys joined with ::. A path that starts with root reads from the whole input. Any other path starts at the map of the struct being built.

A domain string is a list of rules joined with ::. Each rule is a prefix, a dotted path from the top of the input, =, and an operand.

  • ! requires the path to exist. ? allows it to be missing.
  • The operand is String[...], Atom[...], Integer[...], Map[...], Tuple[...], Equal[...], Either[...] or Custom[Module, function]. Fill and check fields from other fields shows each one.

The derives decorator

In a module that uses use GuardedStruct, you can write a derive string as a module attribute above the entity it belongs to. The attribute applies to the next entity only, like @doc. @derive_rules is an alias. A derives: option on the entity wins over the attribute. An Ash resource does not read the attribute.

elixir
guardedstruct do
  @derives "sanitize(trim) validate(string, not_empty, max_len=200)"
  field :title, String.t(), enforce: true

  @derives "validate(integer, min_len=0)"
  field :views, integer(), default: 0
end

Formatter

GuardedStruct exports locals_without_parens for its macros. Add it to your .formatter.exs so mix format leaves field :name, String.t() without parentheses.

elixir
[
  import_deps: [:guarded_struct]
]