{"id":18002856,"url":"https://github.com/am-kantox/estructura","last_synced_at":"2025-09-04T12:40:05.974Z","repository":{"id":40303909,"uuid":"450097775","full_name":"am-kantox/estructura","owner":"am-kantox","description":"Extensions for Elixir structures","archived":false,"fork":false,"pushed_at":"2025-06-10T05:47:39.000Z","size":292,"stargazers_count":28,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-10T06:27:52.995Z","etag":null,"topics":["elixir","elixir-library","elixir-structs"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/am-kantox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-01-20T12:57:01.000Z","updated_at":"2025-06-10T05:47:36.000Z","dependencies_parsed_at":"2024-03-01T14:27:10.657Z","dependency_job_id":"abd76370-a45f-4d37-97d4-3dfde90bf8c1","html_url":"https://github.com/am-kantox/estructura","commit_stats":{"total_commits":41,"total_committers":2,"mean_commits":20.5,"dds":"0.024390243902439046","last_synced_commit":"be1c71f89d81cc99e2dd8a1536b6f97e9f3d9a88"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/am-kantox/estructura","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Festructura","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Festructura/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Festructura/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Festructura/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/am-kantox","download_url":"https://codeload.github.com/am-kantox/estructura/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Festructura/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263499191,"owners_count":23476021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","elixir-library","elixir-structs"],"created_at":"2024-10-29T23:24:17.222Z","updated_at":"2025-09-04T12:40:05.938Z","avatar_url":"https://github.com/am-kantox.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Estructura    [![Kantox ❤ OSS](https://img.shields.io/badge/❤-kantox_oss-informational.svg)](https://kantox.com/)  [![Test](https://github.com/am-kantox/estructura/workflows/Test/badge.svg)](https://github.com/am-kantox/estructura/actions?query=workflow%3ATest)  [![Dialyzer](https://github.com/am-kantox/estructura/workflows/Dialyzer/badge.svg)](https://github.com/am-kantox/estructura/actions?query=workflow%3ADialyzer)\n\n**Extensions for _Elixir_ structures.**\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:estructura, \"~\u003e 0.1\"},\n    # optionally you might want to add `boundary` library \n    # it is used by `estructura` and many other projects\n    # more info: https://hexdocs.pm/boundary\n    {:boundary, \"~\u003e 0.9\", runtime: false}\n  ]\nend\n```\nI suggest adding [`boundary`](https://hexdocs.pm/boundary) as a dependency since that is used in this project.\n\n## Features\n\n### Nested Structures\n\n`Estructura.Nested` provides powerful nested structure support with validation, coercion, and generation capabilities:\n\n```elixir\ndefmodule User do\n  use Estructura.Nested\n\n  defstruct [\n    name: \"\",\n    address: %{\n      city: \"\",\n      street: %{name: \"\", house: 0}\n    }\n  ]\n\n  # Validation rules\n  def validate(:name, value), do: String.length(value) \u003e 0\n  def validate(\"address.street.house\", value), do: value \u003e 0\nend\n\n# Usage\niex\u003e user = %User{name: \"John\", address: %{city: \"London\", street: %{name: \"High St\", house: 42}}}\niex\u003e User.validate(user)\n{:ok, %User{...}}\n```\n\n### Type System\n\nEstructura provides a rich type system with built-in types and scaffolds for custom types:\n\n#### Built-in Types\n\n- `DateTime` - For handling datetime values\n- `Date` - For date values\n- `Time` - For time values\n- `URI` - For URI handling\n- `IP` - For IPv4 and IPv6 addresses\n- `String` - For string values\n\n```elixir\ndefmodule Event do\n  use Estructura.Nested\n\n  defstruct [\n    timestamp: nil,\n    url: nil\n  ]\n\n  def type(:timestamp), do: Estructura.Nested.Type.DateTime\n  def type(:url), do: Estructura.Nested.Type.URI\nend\n```\n\n#### Type Scaffolds\n\n##### Enum Types\n\nCreate types with predefined values:\n\n```elixir\ndefmodule Status do\n  use Estructura.Nested.Type.Enum,\n    elements: [:pending, :active, :completed]\nend\n\niex\u003e Status.validate(:pending)\n{:ok, :pending}\niex\u003e Status.validate(:invalid)\n{:error, \"Expected :invalid to be one of: [:pending, :active, :completed]\"}\n```\n\n##### Tag Sets\n\nManage lists of predefined tags:\n\n```elixir\ndefmodule Categories do\n  use Estructura.Nested.Type.Tags,\n    elements: [:tech, :art, :science]\nend\n\niex\u003e Categories.validate([:tech, :art])\n{:ok, [:tech, :art]}\niex\u003e Categories.validate([:invalid])\n{:error, \"All tags are expected to be one of [:tech, :art, :science]...\"}\n```\n\n### Coercion and Validation\n\nEstructura provides flexible coercion and validation:\n\n```elixir\ndefmodule Temperature do\n  use Estructura.Nested\n\n  defstruct value: 0, unit: :celsius\n\n  def coerce(:value, str) when is_binary(str) do\n    case Float.parse(str) do\n      {num, \"\"} -\u003e {:ok, num}\n      _ -\u003e {:error, \"Invalid number\"}\n    end\n  end\n\n  def validate(:value, v), do: v \u003e= -273.15  # Absolute zero\n  def validate(:unit, u), do: u in [:celsius, :fahrenheit, :kelvin]\nend\n```\n\n### Lazy Values\n\nUse `Estructura.Lazy` for deferred computation:\n\n```elixir\ndefmodule Cache do\n  use Estructura.Nested\n\n  defstruct value: Estructura.Lazy.new(\u0026expensive_computation/1)\n\n  def expensive_computation(_), do: :timer.sleep(1000) \u0026\u0026 :computed\nend\n```\n\n### Flattening and Transformation\n\nConvert nested structures to flat representations:\n\n```elixir\ndefmodule User do\n  use Estructura.Nested, flattenable: true\n\n  defstruct name: \"\", address: %{city: \"\", postal_code: \"\"}\nend\n\niex\u003e user = %User{name: \"John\", address: %{city: \"London\", postal_code: \"SW1\"}}\niex\u003e Estructura.Flattenable.flatten(user)\n%{\"name\" =\u003e \"John\", \"address_city\" =\u003e \"London\", \"address_postal_code\" =\u003e \"SW1\"}\n```\n\n## Property Testing\n\nEstructura supports property-based testing out of the box:\n\n```elixir\ndefmodule UserTest do\n  use ExUnit.Case\n  use ExUnitProperties\n\n  property \"valid users are validated\" do\n    check all %User{} = user \u003c- User.__generator__() do\n      assert {:ok, ^user} = User.validate(user)\n    end\n  end\nend\n```\n\n## Changelog\n* `1.10.0` — `TimeSeries` type, propagate already set values as payload to `StreamData.bind/2`\n* `1.8.0` — `validate/1`\n* `1.7.0` — better infrastructure for `Types`, `URI`, `IP`, `Scaffold`\n* `1.6.0` — `jsonify: true | module()` option in a call to `Estructura.Flattenable.flatten/2`\n* `1.5.0` — no `:formulae` dependency\n* `1.4.1` — allow functions of arity 1 in `content` as coercers in a call to `Estructura.Aston.coerce/2`\n* `1.4.0` — allow coercers in a call to `Estructura.Aston.coerce/2`\n* `1.3.0` — calculated fields for `Estructura` and `Estructura.Nested`\n* `1.2.12` — export type from `Estructura.Nested`\n* `1.2.11` — nullable coercers\n* `1.2.10` — coercers for floats, and date/time values\n* `1.2.8` — `Estructura.Tree` → `Estructura.Aston` + `Aston.access/2` to retrieve and access key by names\n* `1.2.5` — `use Estructura.Nested flattenable: boolean(), jason: boolean(), transformer: boolean()`\n* `1.2.3` — Several `coerce/1` and `validate/1` clauses, default coercers\n* `1.2.2` — `Estructura.Flattenable`\n* `1.2.1` — Generators for `:datetime` and `:date`\n* `1.2.0` — `Estructura.Nested` would attempt to split keys by a delimiter if instructed\n* `1.1.0` — `Estructura.Aston` to hold an AST structure, like XML\n* `1.0.0` — Elixir v1.16 and deps\n* `0.6.0` — `Estructura.Transform` to produce squeezed representations of nested structs\n* `0.5.5` — export declarations of both `Estructura` and `Estructura.Nested` to docs\n* `0.5.4` — `Estructura.Nested` allows `cast/1` to cast nested structs from maps\n* `0.5.3` — `Estructura.diff/3` now understands maps\n* `0.5.2` — `Estructura.diff/3`\n* `0.5.1` — [BUG] Fixed `Collectable` and `Enumerable` injected implementations\n* `0.5.0` — `Estructura.Nested` for nested structures with validation, coercion, and generation\n* `0.4.2` — [BUG] Fixed wrong spec for `put!/3`\n* `0.4.1` — `Estructura.LazyMap.keys/1`, `Estructura.LazyMap.fetch_all/1`\n* `0.4.0` — `Estructura.Lazy`, `Estructura.LazyMap`\n* `0.3.2` — `put!/3`\n* `0.3.0` — `coercion` and `validation` are now injected as behaviours\n* `0.2.0` — `coercion`, `validation`, `put/3`\n\n## [Documentation](https://hexdocs.pm/estructura)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Festructura","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fam-kantox%2Festructura","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Festructura/lists"}