defmodule Plymio.Codi.Pattern.Struct do @moduledoc ~S""" The *struct* patterns create a range of transform functions for a module's struct. See `Plymio.Codi` for an overview and documentation terms. ## Set and Unset Fields These patterns use *the unset value* (see `Plymio.Fontais.Guard.the_unset_value/0`) to decide whether a field has a useful value. Fields can be unset by default by using *the unset value* in the `Kernel.defstruct/1` e.g. defstruct [x: Plymio.Fontais.Guard.the_unset_value()] For example, a function built using the `struct_fetch` pattern will return `{error, error}` if the target field's value is unset. Similary, for `struct_get`, `{:ok, default}` will be returned if the field's value is unset. ## Errors The code generated by most *struct* patterns checks the first argument is an instance of the target module's *struct* and returns `{:error, error}` if not. ## Test Environment The doctests use a helper (`codi_helper_struct_compile_module/1`) to compile the generated function(s) in a dynamically created module, returning `{:ok, {forms, test_mod}}`. The `forms` are the generated code and another helper (`Harnais.Helper.harnais_helper_format_forms!/2`) is used to "textify" the code using the Elixir code formatter. The `test_mod` is the dynamically created module and is used to call the generated function(s). The default `Kernel.defstruct/1` for the doctests is shown here. Note the `:z` field is unset. defstruct [x: 42, y: nil, z: Plymio.Fontais.Guard.the_unset_value()] The first example for each pattern just shows the generated code with subsequent examples performing the actual test(s). ## Common Codi Pattern Opts (cpo) Keys As well as the top level common options, these keys are valid in any *struct* pattern's *cpo*: | Key | Aliases | | :--- | :--- | | `:fun_name` | *:name, :function_name* | | `:fun_field` | *:field, ::function_field, :key, :fun_key, :function_key* | | `:fun_args` | *:args, :function_args* | | `:fun_arity` | *:arity, :function_arity* | | `:fun_doc` | *:doc, :function_doc* | | `:typesepc_spec_args` | *:spec_args* | | `:typespec_spec_result` |*:spec_result, :result, :fun_result, :function_result* | | `:since` | | All *struct* patterns support the generation of `@doc`, `@spec` and `@since` module attribute forms. All *struct* patterns generate a `@doc` by default. (It can be disabled in the usual way by specifying `doc: false` in the *cpo*.) > In the doctests below the `@doc` forms are mostly disabled (i.e. `doc: nil`) for clarity. ## Pattern: *struct_get* This pattern is a convenience to generate both *struct_get1* and *struct_get2* patterns. The *cpo* must be configured for a *struct_get2* and will be "reduced" to suite a *struct_get1*. ## Pattern: *struct_get1* The *struct_get1* pattern creates a function to get the value of a struct's field and if the value is unset return a fixed default. ## Examples This example generate a `get/1` function for the `:z` field. Note the `:field` in the *cpo* is a `Keyword` with the fixed default for the `:z` field (`"z get default"`) iex> {:ok, {forms, _test_mod}} = [ ...> struct_get1: [args: :t, name: :get_z, field: [z: "z get default"], doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(get_z(t))", "", "def(get_z(%__MODULE__{z: field_value})) do", " field_value", " |> case do", " x when Plymio.Fontais.Guard.is_value_set(x) ->", " {:ok, x}", "", " _ ->", " {:ok, \"z get default\"}", " end", "end", "", "def(get_z(state)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] iez> {:ok, {_forms, test_mod}} = [ ...> struct_get1: [args: :t, name: :get_z, field: [z: "z get default"], doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # z is unset by default so the get/1 default will be returned. ...> struct(test_mod) |> test_mod.get_z {:ok, "z get default"} ## Pattern: *struct_get2* The *struct_get2* pattern creates a function to get the value of a struct's field and, if the value is unset, return the second argument. ## Examples This example generate a `get/2` function for the `:z` field. iex> {:ok, {forms, _test_mod}} = [ ...> struct_get2: [args: [:t, :the_default_for_z], name: :get_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(get_z(t, the_default_for_z))", "", "def(get_z(%__MODULE__{z: field_value}, default)) do", " field_value", " |> case do", " x when Plymio.Fontais.Guard.is_value_set(x) ->", " {:ok, x}", "", " _ ->", " {:ok, default}", " end", "end", "", "def(get_z(state, _default)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] iez> {:ok, {_forms, test_mod}} = [ ...> struct_get2: [args: [:t, :the_default_for_z], name: :get_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # z is unset by default so the get/2 2nd argument will be returned. ...> struct(test_mod) |> test_mod.get_z("an explicit default") {:ok, "an explicit default"} ## Pattern: *struct_fetch* The *struct_fetch* pattern creates a function to fetch the value of a struct's field. ## Examples This example generate a `fetch/1` function for the `:x` field. iex> {:ok, {forms, _test_mod}} = [ ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(fetch_x(t))", "", "def(fetch_x(%__MODULE__{x: field_value} = state)) do", " field_value", " |> case do", " x when Plymio.Fontais.Guard.is_value_set(x) ->", " {:ok, x}", "", " _ ->", " Plymio.Codi.Error.new_error_result(m: \"struct field \#{:x} unset\", v: state)", " end", "end", "", "def(fetch_x(state)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] iex> {:ok, {_forms, test_mod}} = [ ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> {:ok, 42} = struct(test_mod) |> test_mod.fetch_x ...> # setting x to the unset value causes the fetch to fail ...> {:error, error} = struct(test_mod, x: Plymio.Fontais.Guard.the_unset_value) |> test_mod.fetch_x ...> true = error |> Exception.message |> String.starts_with?("struct field x unset") ...> # the argument must be an instance of the module's struct ...> {:error, error} = :not_a_struct |> test_mod.fetch_x ...> error |> Exception.message "struct invalid, got: :not_a_struct" ## Pattern: *struct_put* The *struct_put* pattern creates a function to put a value for a struct's field. ## Examples This example generates a `put/2` function for the `:x` field. iex> {:ok, {forms, _test_mod}} = [ ...> struct_put: [args: [:t, :value], name: :put_x, field: :x, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(put_x(t, value))", "", "def(put_x(%__MODULE__{x: _} = state, value)) do", " {:ok, state |> struct!(x: value)}", "end", "", "def(put_x(state, _value)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] iex> {:ok, {_forms, test_mod}} = [ ...> struct_put: [args: [:t, :value], name: :put_x, field: :x, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # set the :x field's value to 123 ...> {:ok, %test_mod{} = t1} = struct(test_mod) |> test_mod.put_x(123) ...> # use `fetch_x/1` to check ...> {:ok, 123} = t1 |> test_mod.fetch_x ...> # the argument must be an instance of the module's struct ...> {:error, error} = :not_a_struct |> test_mod.put_x(123) ...> error |> Exception.message "struct invalid, got: :not_a_struct" ## Pattern: *struct_maybe_put* The *struct_maybe_put* pattern create a function to put a value for a struct's field *only* if the field's current value is unset. ## Examples This code shows a `maybe_put/2` function for the `:z` field: iex> {:ok, {forms, _test_mod}} = [ ...> struct_maybe_put: [args: [:t, :value], name: :maybe_put_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(maybe_put_z(t, value))", "", "def(", " maybe_put_z(%__MODULE__{z: field_value} = state, _value)", " when Plymio.Fontais.Guard.is_value_set(field_value)", ") do", " {:ok, state}", "end", "", "def(", " maybe_put_z(%__MODULE__{z: field_value} = state, value)", " when Plymio.Fontais.Guard.is_value_unset(field_value)", ") do", " value", " |> Plymio.Fontais.Guard.is_value_unset()", " |> case do", " true ->", " {:ok, state}", "", " _ ->", " {:ok, state |> struct!(z: value)}", " end", "end", "", "def(maybe_put_z(state, _value)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] Here `maybe_put/2` and `fetch/1` functions are generated for two fields: the `:x` field has `42` for its default, whereas the `:z` field is unset. iex> {:ok, {_forms, test_mod}} = [ ...> struct_maybe_put: [args: [:t, :value], name: :maybe_put_x, field: :x, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> struct_maybe_put: [args: [:t, :value], name: :maybe_put_z, field: :z, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # by default the `:z` field is unset so `fetch_z/1` will fail ...> t1 = struct(test_mod) ...> {:error, _error} = t1 |> test_mod.fetch_z ...> # maybe_put/2 will update the field since it is unset ...> {:ok, %test_mod{} = t1} = t1 |> test_mod.maybe_put_z(123) ...> # use `fetch_z/1` to check ...> {:ok, 123} = t1 |> test_mod.fetch_z ...> # field `:x` has a default of `42` so maybe_put/2 will not update the field ...> {:ok, %test_mod{} = t1} = t1 |> test_mod.maybe_put_x("will be ignored") ...> # the `:x` field will still be `42` ...> t1 |> test_mod.fetch_x {:ok, 42} ## Pattern: *struct_has?* The *struct_has?* pattern creates a function that returns `true` if the field's value is set, otherwise `false`. > Note: if the first argument is not a valid struct, `false` is returned.. ## Examples This code shows `has?/1` function for the `:z` field: iex> {:ok, {forms, _test_mod}} = [ ...> struct_has?: [args: :t, name: :has_z?, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(has_z?(t))", "", "def(has_z?(%__MODULE__{z: field_value}) when Plymio.Fontais.Guard.is_value_set(field_value)) do", " true", "end", "", "def(has_z?(_state)) do", " false", "end"] Here `has?/1` functions are generated for two fields: the `:x` field has `42` for its default, whereas the `:z` field is unset. iex> {:ok, {_forms, test_mod}} = [ ...> struct_has?: [args: :t, name: :has_x?, field: :x, doc: nil], ...> struct_has?: [args: :t, name: :has_z?, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> t1 = struct(test_mod) ...> false = t1 |> test_mod.has_z? ...> t1 |> test_mod.has_x? true ## Pattern: *struct_update* The *struct_update* pattern creates a function to call the modules's `update/2` function. The module's `update/2` function is a standard `Plymio` module state function that works like a validated put. Apart from showing the generated code, it is not documented or tested further here. ## Examples This example generates an `update/2` function for the `:x` field. iex> {:ok, {forms, _codi}} = [ ...> struct_update: [args: [:t, :value], name: :update_x, field: :x, doc: nil], ...> ] |> CODI.produce_codi ...> forms |> harnais_helper_format_forms! ["def(update_x(t, value))", "", "def(update_x(%__MODULE__{x: _} = state, value)) do", " {:ok, state |> update(x: value)}", "end", "", "def(update_x(state, _value)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] ## Pattern: *struct_set* The *struct_set* pattern is a simple but versatile pattern for creating a function that sets one or more fields in the *struct* to specific values, defaulting to *the unset value*. ## Examples This example generates an `set/2` function to set the `:x` field to value `123`. Note the `:field` in the *cpo* is a `Keyword`. iex> {:ok, {forms, _codi}} = [ ...> struct_set: [args: :t, name: :set_x, field: [x: 123], doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(set_x(t))", "", "def(set_x(%__MODULE__{x: _} = state)) do", " {:ok, state |> struct!(x: 123)}", "end", "", "def(set_x(state)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] This example create a `set/1` function that sets the `:x` and `:z` fields. iex> {:ok, {_forms, test_mod}} = [ ...> struct_set: [args: :t, name: :set_xz, doc: nil, ...> field: [x: 123, z: "z is no longer unset"]], ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_y, field: :y, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # set the :x and :z fields ...> {:ok, %test_mod{} = t1} = struct(test_mod) |> test_mod.set_xz ...> # use fetch to check ...> {:ok, 123} = t1 |> test_mod.fetch_x ...> {:ok, nil} = t1 |> test_mod.fetch_y ...> t1 |> test_mod.fetch_z {:ok, "z is no longer unset"} This example create a `set/1` function that sets the `:x` and `:z` fields to specific values, but unsets the `:y` field. Note the `:field` in the *cpo* is not a `Keyword`: The value for `:y` is not given and defaults to be the unset value. iex> {:ok, {_forms, test_mod}} = [ ...> struct_set: [args: :t, name: :set_xyz, doc: nil, ...> field: [{:x, 123}, :y, {:z, "z is no longer unset"}]], ...> struct_fetch: [args: :t, name: :fetch_x, field: :x, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_y, field: :y, doc: nil], ...> struct_fetch: [args: :t, name: :fetch_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # set all 3 fields ...> {:ok, %test_mod{} = t1} = struct(test_mod) |> test_mod.set_xyz ...> {:ok, 123} = t1 |> test_mod.fetch_x ...> {:ok, "z is no longer unset"} = t1 |> test_mod.fetch_z ...> # :y is now unset ...> {:error, error} = t1 |> test_mod.fetch_y ...> error |> Exception.message |> String.starts_with?("struct field y unset") true ## Pattern: *struct_export* The *struct_export* pattern creates a function that exports one or more fields in the *struct* to an *opts* (`Keyword`). The export (*opts*) is sparse: only keys that are set are included. Default values can be provided; if the value of the key in the *struct* is unset, the default is used. ## Examples This example generates an `export/1` function for all three fields in the test *struct*. Note, since no default export values were given in the `:field` in the *cpo*, the defaults are the unset value and the field will only appear in the export if the *struct* value is set. iex> {:ok, {forms, _codi}} = [ ...> struct_export: [args: :t, name: :export_all, field: [:x, :y, :z], doc: nil], ...> ] |> codi_helper_struct_compile_module ...> forms |> harnais_helper_format_forms! ["def(export_all(t))", "", "def(export_all(%__MODULE__{x: field_value, y: field_value1, z: field_value2})) do", " tuples =", " [", " x: :plymio_fontais_t3h1e4_u9n8s7e2t7_v1a8l3u8e,", " y: :plymio_fontais_t3h1e4_u9n8s7e2t7_v1a8l3u8e,", " z: :plymio_fontais_t3h1e4_u9n8s7e2t7_v1a8l3u8e", " ] ++ [x: field_value, y: field_value1, z: field_value2]", "", " export =", " tuples", " |> Keyword.keys()", " |> Stream.uniq()", " |> Stream.map(fn k ->", " tuples", " |> Keyword.get_values(k)", " |> Enum.filter(fn v -> v |> Plymio.Fontais.Guard.is_value_set() end)", " |> case do", " [] ->", " {k, @plymio_fontais_the_unset_value}", "", " values ->", " {k, values |> List.last()}", " end", " end)", " |> Stream.filter(fn {_k, v} -> v |> Plymio.Fontais.Guard.is_value_set() end)", " |> Keyword.new()", "", " {:ok, export}", "end", "", "def(export_all(state)) do", " Plymio.Codi.Error.new_error_result(m: \"struct invalid\", v: state)", "end"] This example creates an `export/1` function that exports the `:x` and `:z` fields. The `:z` field is unset by default so will not appear in the export unless set explicitly. iex> {:ok, {_forms, test_mod}} = [ ...> struct_export: [args: :t, name: :export_xz, doc: nil, ...> field: [:x, :z]], ...> struct_put: [args: [:t, :value], name: :put_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # the :z field is by default unset and will not be in the export ...> {:ok, [x: 42]} = struct(test_mod) |> test_mod.export_xz ...> # set z and export ...> {:ok, %test_mod{} = t1} = struct(test_mod) |> test_mod.put_z("z is now set") ...> t1 |> test_mod.export_xz {:ok, [x: 42, z: "z is now set"]} Another example but providing default values for each key in the export by supplying a `Keyword` for `:field` in the *cpo*:. iex> {:ok, {_forms, test_mod}} = [ ...> struct_export: [args: :t, name: :export_xz, doc: nil, ...> field: [x: :x_default, z: :z_default]], ...> struct_put: [args: [:t, :value], name: :put_z, field: :z, doc: nil], ...> ] |> codi_helper_struct_compile_module ...> # the :z field has a default export value ...> struct(test_mod) |> test_mod.export_xz {:ok, [x: 42, z: :z_default]} """ alias Plymio.Codi, as: CODI alias Plymio.Codi.Utility, as: CODIUTIL alias Plymio.Codi.Utility.Depend, as: DEPEND use Plymio.Fontais.Attribute use Plymio.Codi.Attribute import Plymio.Codi.Error, only: [ new_error_result: 1 ] import Plymio.Fontais.Option, only: [ opts_take_canonical_keys: 2, opts_create_aliases_dict: 1 ] import Plymio.Codi.Utility, only: [ cpo_resolve_fun_name: 1, cpo_resolve_guard_fun_args: 2, cpo_resolve_guard_field_match: 1, cpo_resolve_guard_field_match: 2, cpo_resolve_typespec_spec_args: 1 ] import Plymio.Codi.CPO @pattern_struct_get_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_fun_default, @plymio_codi_key_alias_forms_edit ] @pattern_struct_get_dict_alias @pattern_struct_get_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_get_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_get_dict_alias) end @pattern_struct_fetch_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_forms_edit ] @pattern_struct_fetch_dict_alias @pattern_struct_fetch_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_fetch_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_fetch_dict_alias) end @pattern_struct_put_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_forms_edit ] @pattern_struct_put_dict_alias @pattern_struct_put_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_put_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_put_dict_alias) end @pattern_struct_maybe_put_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_forms_edit ] @pattern_struct_maybe_put_dict_alias @pattern_struct_maybe_put_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_maybe_put_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_maybe_put_dict_alias) end @pattern_struct_set_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_fun_default, @plymio_codi_key_alias_forms_edit ] @pattern_struct_set_dict_alias @pattern_struct_set_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_set_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_set_dict_alias) end @pattern_struct_has_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_fun_default, @plymio_codi_key_alias_forms_edit ] @pattern_struct_has_dict_alias @pattern_struct_has_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_has_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_has_dict_alias) end @pattern_struct_update_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_forms_edit ] @pattern_struct_update_dict_alias @pattern_struct_update_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_update_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_update_dict_alias) end @pattern_struct_export_kvs_alias [ @plymio_codi_key_alias_pattern, @plymio_codi_key_alias_status, @plymio_codi_key_alias_form, @plymio_codi_key_alias_since, # limited aliases {@plymio_codi_key_typespec_spec_args, [:spec_args]}, @plymio_codi_key_alias_typespec_spec_result, @plymio_codi_key_alias_fun_name, @plymio_codi_key_alias_fun_doc, @plymio_codi_key_alias_fun_args, @plymio_codi_key_alias_fun_arity, @plymio_codi_key_alias_fun_key, @plymio_codi_key_alias_forms_edit ] @pattern_struct_export_dict_alias @pattern_struct_export_kvs_alias |> opts_create_aliases_dict @doc false def cpo_pattern_struct_export_normalise(opts, dict \\ nil) do opts |> opts_take_canonical_keys(dict || @pattern_struct_export_dict_alias) end @doc false def express_pattern(codi, pattern, opts) # expectation if the cpo is initially configured for get2 def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_get do with {:ok, cpo} <- cpo |> cpo_pattern_struct_get_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(2), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct, :default]), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(2), {:ok, get2_fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # typespec if any {:ok, cpo} <- cpo |> cpo_maybe_transform(&cpo_has_typespec_spec_opts?/1, [ {:cpo_maybe_put_typespec_spec_result, :result}, {:cpo_maybe_put_typespec_spec_args, [[:struct, :any]]} ]), {:ok, cpo_get1} <- cpo |> cpo_put_pattern(@plymio_codi_pattern_struct_get1), {:ok, cpo_get1} <- cpo_get1 |> cpo_put_fun_args(get2_fun_args |> Enum.take(1)), {:ok, cpo_get1} <- cpo_get1 |> cpo_put_fun_arity(1), {:ok, cpo_get1} <- cpo_get1 |> cpo_maybe_transform( &cpo_has_typespec_spec_opts?/1, fn cpo -> with {:ok, spec_args} <- cpo_get1 |> cpo_resolve_typespec_spec_args, {:ok, cpo} <- cpo |> cpo_put_typespec_spec_args(spec_args |> Enum.take(1)), true <- true do {:ok, cpo} else {:error, %{__exception__: true}} = result -> result end end ), {:ok, cpo_get2} <- cpo |> cpo_put_pattern(@plymio_codi_pattern_struct_get2), {:ok, cpo_get2} <- cpo_get2 |> cpo_put_fun_args(get2_fun_args), true <- true do {:ok, {[cpo_get1, cpo_get2], state}} else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_get1 do with {:ok, cpo} <- cpo |> cpo_pattern_struct_get_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(1), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args(:struct), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_get" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do fun_default = field_tuples |> hd |> elem(1) pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match)) do unquote(field_value_var) |> case do x when Plymio.Fontais.Guard.is_value_set(x) -> {:ok, x} _ -> {:ok, unquote(fun_default)} end end def unquote(fun_name)(state) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :result}, {:cpo_maybe_put_typespec_spec_args, :struct} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_get2 do with {:ok, cpo} <- cpo |> cpo_pattern_struct_get_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(2), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct, :default]), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(2), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_get" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match), default) do unquote(field_value_var) |> case do x when Plymio.Fontais.Guard.is_value_set(x) -> {:ok, x} _ -> {:ok, default} end end def unquote(fun_name)(state, _default) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :result}, {:cpo_maybe_put_typespec_spec_args, [[:struct, :any]]} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_fetch do with {:ok, cpo} <- cpo |> cpo_pattern_struct_fetch_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(1), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args(:struct), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_fetch" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match) = state) do unquote(field_value_var) |> case do x when Plymio.Fontais.Guard.is_value_set(x) -> {:ok, x} _ -> Plymio.Codi.Error.new_error_result( m: "struct field #{unquote(field_name)} unset", v: state ) end end def unquote(fun_name)(state) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :result}, {:cpo_maybe_put_typespec_spec_args, :struct} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_put do with {:ok, cpo} <- cpo |> cpo_pattern_struct_put_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(2), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct, :value]), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(2), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_build_value(&CODIUTIL.field_build_anon_var/1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_put" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match) = state, value) do {:ok, state |> struct!([{unquote(field_name), value}])} end def unquote(fun_name)(state, _value) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :struct_result}, {:cpo_maybe_put_typespec_spec_args, [[:struct, :any]]} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_maybe_put do with {:ok, cpo} <- cpo |> cpo_pattern_struct_maybe_put_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(2), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct, :value]), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(2), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name( "struct_#{to_string(field_name)}_maybe_put" |> String.to_atom() ), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match) = state, _value) when Plymio.Fontais.Guard.is_value_set(unquote(field_value_var)) do {:ok, state} end def unquote(fun_name)(unquote(field_match) = state, value) when Plymio.Fontais.Guard.is_value_unset(unquote(field_value_var)) do value |> Plymio.Fontais.Guard.is_value_unset() |> case do true -> {:ok, state} _ -> {:ok, state |> struct!([{unquote(field_name), value}])} end end def unquote(fun_name)(state, _value) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :struct_result}, {:cpo_maybe_put_typespec_spec_args, [[:struct, :any]]} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_has? do with {:ok, cpo} <- cpo |> cpo_pattern_struct_has_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(1), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args(:struct), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_has?" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match)) when Plymio.Fontais.Guard.is_value_set(unquote(field_value_var)) do true end def unquote(fun_name)(_state) do false end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_put_typespec_spec_result, :boolean}, {:cpo_maybe_put_typespec_spec_args, :struct} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_update do with {:ok, cpo} <- cpo |> cpo_pattern_struct_update_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(2), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct, :value]), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(2), {:ok, ctrl} <- ctrl |> ctrl_put_fun_key_length(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_build_value(&CODIUTIL.field_build_anon_var/1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_update" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match) = state, value) do {:ok, state |> update([{unquote(field_name), value}])} end def unquote(fun_name)(state, _value) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :struct_result}, {:cpo_maybe_put_typespec_spec_args, [[:struct, :keyword]]} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_set do with {:ok, cpo} <- cpo |> cpo_pattern_struct_set_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(1), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args([:struct]), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(1), {:ok, ctrl} <- ctrl |> ctrl_put_fun_build_value(&CODIUTIL.field_build_anon_var/1), {:ok, {{field_name, field_value_var}, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_#{to_string(field_name)}_set" |> String.to_atom()), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl), # save resolved for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, cpo} <- cpo |> cpo_put_fun_args(fun_args), true <- true do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match) = state) do {:ok, state |> struct!(unquote(field_tuples))} end def unquote(fun_name)(state) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_maybe_put_typespec_spec_result, :struct_result}, {:cpo_maybe_put_typespec_spec_args, :struct} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(%CODI{} = state, pattern, cpo) when pattern == @plymio_codi_pattern_struct_export do with {:ok, cpo} <- cpo |> cpo_pattern_struct_export_normalise, {:ok, cpo} <- cpo |> cpo_maybe_put_fun_arity(1), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_args(:struct), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_doc(pattern), {:ok, cpo} <- cpo |> cpo_maybe_put_fun_name("struct_export" |> String.to_atom()), {:ok, ctrl} <- [] |> ctrl_put_fun_arity_value(1), {:ok, {_, field_vars, field_tuples, field_match}} <- cpo |> cpo_resolve_guard_field_match(ctrl), # saves resolved fields for e.g. doc pattern {:ok, cpo} <- cpo |> cpo_put_fun_key(field_tuples), {:ok, fun_name} <- cpo |> cpo_resolve_fun_name, {:ok, fun_args} <- cpo |> cpo_resolve_guard_fun_args(ctrl) do pattern_form = quote do def unquote(fun_name)(unquote_splicing(fun_args)) def unquote(fun_name)(unquote(field_match)) do # must maintain order tuples = unquote(field_tuples) ++ unquote(field_vars) export = tuples |> Keyword.keys() |> Stream.uniq() |> Stream.map(fn k -> tuples |> Keyword.get_values(k) # dop unset values |> Enum.filter(fn v -> v |> Plymio.Fontais.Guard.is_value_set() end) |> case do # no set values => mark to drop in next stage [] -> {k, @plymio_fontais_the_unset_value} # want last value values -> {k, values |> List.last()} end end) # make the export sparse i.e. no unset values |> Stream.filter(fn {_k, v} -> v |> Plymio.Fontais.Guard.is_value_set() end) # take last tuple for each key |> Keyword.new() {:ok, export} end def unquote(fun_name)(state) do Plymio.Codi.Error.new_error_result(m: "struct invalid", v: state) end end :ok = pattern_form |> Macro.validate() ## pattern_form |> Code.eval_quoted([], __ENV__) depend_args = [ {&cpo_has_fun_doc?/1, &DEPEND.cpo_transform_doc_depend/1}, {&cpo_has_since?/1, &DEPEND.cpo_transform_since_depend/1}, {&cpo_has_typespec_spec_opts?/1, [ &DEPEND.cpo_transform_typespec_spec_depend/1, {:cpo_put_typespec_spec_result, :opts_result}, {:cpo_maybe_put_typespec_spec_args, :struct} ]} ] with {:ok, cpo} <- cpo |> cpo_done_with_edited_form(pattern_form), {:ok, {depend_cpos, %CODI{}}} <- state |> DEPEND.create_depend_cpos(cpo, depend_args) do cpos = depend_cpos ++ [cpo] {:ok, {cpos, state}} else {:error, %{__exception__: true}} = result -> result end else {:error, %{__exception__: true}} = result -> result end end def express_pattern(_codi, pattern, opts) do new_error_result(m: "proxy pattern #{inspect(pattern)} invalid", v: opts) end end