Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6ca1360
init
shahryarjb May 8, 2026
d76eb30
vip
shahryarjb May 8, 2026
95e4ff8
vip
shahryarjb May 8, 2026
5e460da
vip
shahryarjb May 8, 2026
85b81ba
vip
shahryarjb May 10, 2026
b1a5a79
delete the bench
shahryarjb May 11, 2026
87985a2
add tests
shahryarjb May 11, 2026
86165ec
Update conditionals_test.exs
shahryarjb May 11, 2026
288d39f
vip
shahryarjb May 11, 2026
23c1f8c
vip
shahryarjb May 11, 2026
fd5bfa8
vip
shahryarjb May 11, 2026
65e9946
Update cross_field_test.exs
shahryarjb May 11, 2026
11029a4
vip
shahryarjb May 11, 2026
36c04e2
vip
shahryarjb May 11, 2026
80d4d75
vip
shahryarjb May 11, 2026
25d0d27
Update decorated_test.exs
shahryarjb May 11, 2026
7a84e39
vip
shahryarjb May 11, 2026
005e84f
vip
shahryarjb May 12, 2026
0787149
Update forms_test.exs
shahryarjb May 12, 2026
d552ec6
Update records_test.exs
shahryarjb May 12, 2026
bcda52b
Update OPTIONS-0.1.0.md
shahryarjb May 12, 2026
6e30459
vip
shahryarjb May 12, 2026
e830d7c
vip
shahryarjb May 12, 2026
9d1f9cb
vip
shahryarjb May 12, 2026
36810cb
vip
shahryarjb May 12, 2026
1b9e451
vip
shahryarjb May 13, 2026
8db6f4b
vip
shahryarjb May 13, 2026
437d139
vip
shahryarjb May 13, 2026
d0a695a
Update OPTIONS-0.1.0.md
shahryarjb May 13, 2026
c6a94af
Update mix.exs
shahryarjb May 13, 2026
60855c1
vip
shahryarjb May 13, 2026
03e61d3
vip
shahryarjb May 13, 2026
c5a26fa
vip
shahryarjb May 13, 2026
880281a
vip
shahryarjb May 13, 2026
8446847
vip
shahryarjb May 13, 2026
a453fcf
vip
shahryarjb May 13, 2026
df73986
vip
shahryarjb May 14, 2026
dc24475
vip
shahryarjb May 14, 2026
8fd6b5e
vip
shahryarjb May 14, 2026
700681d
vip
shahryarjb May 14, 2026
e88e31b
vip
shahryarjb May 14, 2026
9fda5bf
vip
shahryarjb May 14, 2026
67ec617
vip
shahryarjb May 14, 2026
000837d
vip
shahryarjb May 14, 2026
5cea41e
vip
shahryarjb May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
# Used by "mix format"
spark_locals_without_parens = [
atomic: 1,
authorized_fields: 1,
auto: 1,
auto_wire: 1,
conditional_field: 2,
conditional_field: 3,
default: 1,
derive: 1,
derives: 1,
domain: 1,
dynamic_field: 1,
dynamic_field: 2,
enforce: 1,
error: 1,
field: 2,
field: 3,
from: 1,
hint: 1,
json: 1,
main_validator: 1,
module: 1,
on: 1,
opaque: 1,
priority: 1,
sanitize_derive: 1,
sanitizer: 2,
sanitizer: 3,
struct: 1,
structs: 1,
sub_field: 2,
sub_field: 3,
type: 1,
validate_derive: 1,
validator: 1,
validator: 2,
validator: 3,
virtual_field: 2,
virtual_field: 3
]

[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
import_deps: [:spark, :ash],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
plugins: [Spark.Formatter],
locals_without_parens: spark_locals_without_parens,
export: [
locals_without_parens: spark_locals_without_parens
]
]
126 changes: 125 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,142 @@
# Changelog for GuardedStruct 0.1.0

> We are delighted to introduce v0.1.0 — a from-scratch rewrite of the macro core on top of [Spark](https://hex.pm/packages/spark). Every existing 0.0.x public API is preserved. Bump the dep, run `mix deps.get`, and existing tests stay green.
>
> See [`OPTIONS-0.1.0.md`](./OPTIONS-0.1.0.md) for every new option with examples.

**Tracking PR**: [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Features:

- Rewrite the 2,910-LOC `defmacro` core on `Spark.Dsl.Extension` with one `:guardedstruct` section, five entities (`field`, `sub_field`, `conditional_field`, `virtual_field`, `dynamic_field`), six transformers, three verifiers [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `Pattern-keyed maps` — `field` whose name is a regex declares a free-form map shape (closes [#11](https://github.com/mishka-group/guarded_struct/issues/11)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `virtual_field` — validated through the full pipeline but excluded from `defstruct` (closes [#5](https://github.com/mishka-group/guarded_struct/issues/5)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `dynamic_field` — free-form map with passthrough; atom-attack-safe (string keys stay strings) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add Erlang `Record` support via `validate(record)` and `validate(record=tag)` (closes [#6](https://github.com/mishka-group/guarded_struct/issues/6)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.Validate` standalone API — `Validate.run/2`, `Validate.field/3,4`, `Validate.partial/2` (closes [#2](https://github.com/mishka-group/guarded_struct/issues/2)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add Spark-native custom derive DSL — `use GuardedStruct.Derive.Extension` + `derives do validator/2, sanitizer/2 end` for declarative custom ops [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add per-module `derive_extensions:` opt with `:config` sentinel for in-position merge with global registry [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add compile-time shadow warning when a custom op-name collides with a built-in registered in `Derive.Registry` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `Splode` error wrapping — `GuardedStruct.Errors.from_tuple/1`, `traverse_errors/2`, `to_class/1`, JSON-serializable shape (opt-in) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.AshResource` extension — same DSL inside `Ash.Resource`; generates `__guarded_change__/1`, `__guarded_information__/0`, `__guarded_fields__/0` under the prefixed namespace [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.AshResource.Change` — ready-made `Ash.Resource.Change` module bridging `__guarded_change__/1` into the changeset pipeline [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `auto_wire: true` section option — Spark transformer injects the change into the resource's `changes` section via `Ash.Resource.Builder.add_change/3`; no manual wiring needed [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `batch_change/3` on the Ash change — `Ash.bulk_create/3` and `Ash.bulk_update/3` (with `strategy: :stream`) work end-to-end [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add auto-map cascade for the Ash extension — every nested `sub_field` returns a plain map at every depth (matches Ash's `:map` attribute type) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `atomic: true` section option — compile-time `VerifyAtomic` verifier rejects (with `Spark.Error.DslError`) any derive op that can't translate to atomic SQL (DNS validators, MFA callbacks, custom Extension ops, `main_validator/1`, cross-field options) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.AtomicClassifier` — one pattern-match clause per atomic-safe op; contributors extend by adding a single clause [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add typo-aware diagnostic in `VerifyAtomic` — distinguishes "built-in but not atomic-safe" from "unknown op (typo or custom Extension)" with a different actionable message [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `json: true` section option — auto-derives `Jason.Encoder` (if `:jason` in deps) with fallback to built-in `JSON.Encoder` on Elixir 1.18+ [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.Info` — full introspection API: `describe/1`, `field_kind/2`, `enforce?/1,2`, `virtual?/2`, `dynamic?/2`, `sub_module/2`, `conditional_children/2`, collection helpers, section-option shorthands [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `GuardedStruct.Diff` — `diff/2`, `apply/2`, `equal?/2` for audit-log-friendly struct diffing [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `MyStruct.example/0` — REPL helper returning a struct populated with defaults / type placeholders [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add telemetry events — `[:guarded_struct, :builder, :start | :stop | :exception]` on every top-level `builder/1` call [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `@derives` decorator attribute — alternative to inline `derives:` for keeping fields short [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add editor autocomplete inside `guardedstruct do … end` via Spark's ElixirSense plugin (closes [#1](https://github.com/mishka-group/guarded_struct/issues/1)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add igniter installer — `mix igniter.install guarded_struct` [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Refactors:

- Move every static-string parse to compile time — derive op-strings, `from:`/`on:` paths, `domain:` patterns are now parsed once during compilation; the runtime reads pre-built op-maps from `__fields__/0` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Pre-evaluate `enum=Map[…]` / `enum=Tuple[…]` / `equal=Map::…` operands at compile time — zero `Code.eval_string` on the runtime hot path [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Replace plain-macro `validator/2` and `sanitizer/2` with proper Spark entities under `derives do ... end` block — Spark.Formatter handles paren-stripping consistently with the rest of the DSL [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Replace `IO.warn/2` with `Spark.Warning.warn/3` — shadow warnings point at the user's source line via the entity's `__spark_metadata__.anno` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Rename `__guarded_validate__/1` → `__guarded_change__/1` on the Ash extension — name reflects that the function transforms (sanitize, auto-fill) as well as validates [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Rename `derive:` option to `derives:` (plural) — aligns with the `@derives` decorator; legacy `derive:` still works but emits a compile-time deprecation warning via `Spark.Warning.warn_deprecated/4` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Rename `jason: true` section option to `json: true` — option now derives whichever JSON encoder is available (Jason or built-in) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Extract test fixtures (Ash resources + custom-derive modules) to top-level modules in `test/support/` so Spark.Formatter applies paren-removal and section-ordering rules uniformly [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Bugs:

- Fix nested `conditional_field` — works to arbitrary depth via `recursive_as: :conditional_fields` (closes [#7](https://github.com/mishka-group/guarded_struct/issues/7), [#8](https://github.com/mishka-group/guarded_struct/issues/8), [#25](https://github.com/mishka-group/guarded_struct/issues/25)) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Restore i18n via `GuardedStruct.Messages.translated_message/1,2` for orchestration-layer errors (`authorized_fields`, `required_fields`, `:on` / `:domain` core keys, list-builder errors) — all 14 message callbacks reachable again [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Fix `__information__/0` to populate `conditional_keys` with actual conditional-field names (was always `[]`) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Fix `MyStruct.Error.message/1` to match master's format and use `translated_message(:message_exception)` for i18n [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Unblock the legacy `Parser` `raise` sites that prevented nested `conditional_field` from compiling [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Surface malformed `derives:` strings as `Spark.Error.DslError` with file:line — previously swallowed by a `rescue _ -> nil` and silently produced no validation [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Fix re-entrancy in the auto-map cascade — process-dict flag is saved+restored across nested `validate/3` calls so a validator callback can recursively validate without clobbering outer state [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Fix `Logger.configure(level: :warning)` global side-effect in `test_helper.exs` — replaced with `@moduletag capture_log: true` on Ash test modules [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Tests:

- Add 743+ tests (up from 146 in 0.0.4), including 6 property-based tests via `stream_data` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add real Ash 3.x integration suite — ETS data layer, end-to-end `Ash.create/1`, `Ash.update/1`, `Ash.bulk_create/3`, `Ash.bulk_update/3` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/atomic_verifier_test.exs` — 30 tests covering the atomic-safety classifier and verifier in isolation [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/ash_integration_test.exs` atomic describe blocks — 29 tests covering happy path + compile-time rejection for every blocker category [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/info_test.exs` — 38 tests covering every introspection helper including `describe/1` consolidated dump [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/derive_extension_shadow_warning_test.exs` — 9 tests for compile-time shadow detection [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/derive_extensions_per_module_test.exs` — 19 tests for per-module opt resolution including the `:config` sentinel [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/jason_encoder_test.exs` — Jason + built-in JSON encoder coverage with nested sub_field [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `test/telemetry_test.exs` — start/stop/exception event coverage, including nested-build inheritance [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Docs:

- Add full LiveBook walkthrough at [`guidance/guarded-struct.livemd`](./guidance/guarded-struct.livemd) with runnable end-to-end examples [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add [`OPTIONS-0.1.0.md`](./OPTIONS-0.1.0.md) — every new option in v0.1.0 with worked examples [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add auto-generated DSL cheat sheets at `documentation/dsls/` via `mix spark.cheat_sheets` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `mix lint` and `mix cheat` aliases — wrap `spark.formatter` + `format` and `spark.cheat_sheets` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add "Atom-attack safety" section to the `GuardedStruct` module @moduledoc covering the dynamic_field / pattern-keyed map threat model [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Internals dropped:

- Remove `builder/4` `@doc false` form (with `(actions, key, type, error)` arity) — replaced by an internal runtime helper [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Remove `register_struct/4`, `__field__/6`, `__type__/2`, `delete_temporary_revaluation/1`, `create_builder/1`, `create_error_module/0` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Remove the 12 `gs_*` accumulator module attributes (`gs_fields`, `gs_types`, `gs_enforce_keys`, etc.) — replaced by Spark DSL state [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Remove `parser/3` (the conditional variant of `Parser.parser`), `elements_unification/2`, `find_node_tags/1`, `add_parent_tags/3`, `conds_list/2`, `find_conds_children_recursive/2` [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Remove `Derive.pre_derives_check/3`, `get_derives_from_success_conditional_data/1`, `error_handler/2`, `halt_errors/1`, the alternate-shape `derive/1` clauses [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Remove `Messages.unsupported_conditional_field/0` and `Messages.parser_field_value/0` callbacks (dead code after the nested-conditional fix) [#13](https://github.com/mishka-group/guarded_struct/pull/13)

### Dependencies:

- Add `{:spark, "~> 2.7"}` (runtime — DSL framework) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `{:splode, "~> 0.3"}` (runtime — error class hierarchy for opt-in wrapper) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `{:telemetry, "~> 1.0"}` (runtime — builder events) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `{:sourceror, "~> 1.7", only: [:dev, :test]}` (required by Spark.Formatter) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `{:igniter, "~> 0.8", only: [:dev, :test]}` (installer mix task) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- Add `{:ash, "~> 3.0", only: [:dev, :test]}` (real Ash integration suite — not a runtime dep) [#13](https://github.com/mishka-group/guarded_struct/pull/13)
- All optional deps unchanged (`html_sanitize_ex`, `email_checker`, `ex_url`, `ex_phone_number`, `sweet_xml`) [#13](https://github.com/mishka-group/guarded_struct/pull/13)

---

# Changelog for GuardedStruct 0.0.4

### Bugs:

- Fix deprecated code from Elixir 1.18

### Features:

- Support overridable messages for the `GuardedStruct` module with support for multiple languages

---

# Changelog for GuardedStruct 0.0.3

### Bugs:

- Fix deprecated code from Elixir 1.18.0-rc.0

---

# Changelog for GuardedStruct 0.0.2

- Fix: Support charlists sigil warning and keep backward compatibility for charlist regex
### Bugs:

- Support charlists sigil warning and keep backward compatibility for charlist regex

---

# Changelog for GuardedStruct 0.0.1

> We are delighted to introduce our first standalone release of GuardedStruct — extracted from the Mishka developer tools library.
>
> **For more information please see**: https://mishka.tools

### Features:

- Detach from the Mishka developer tools library

### Refactors:

- Remove optional libraries (must be enabled by the user)
- Improvements in some tests
Loading
Loading