{"id":15063674,"url":"https://github.com/utopos/key_validator","last_synced_at":"2026-01-25T16:33:07.149Z","repository":{"id":243065003,"uuid":"447757068","full_name":"utopos/key_validator","owner":"utopos","description":"Macro for validating fields (map, keywords) against a struct.","archived":false,"fork":false,"pushed_at":"2024-08-20T09:23:25.000Z","size":19,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-21T19:31:33.249Z","etag":null,"topics":["elixir","macro","macros","struct"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/utopos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-01-13T21:25:59.000Z","updated_at":"2024-08-20T09:23:28.000Z","dependencies_parsed_at":"2024-08-21T03:16:17.997Z","dependency_job_id":null,"html_url":"https://github.com/utopos/key_validator","commit_stats":null,"previous_names":["utopos/field_validator","utopos/key_validator"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/utopos/key_validator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utopos%2Fkey_validator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utopos%2Fkey_validator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utopos%2Fkey_validator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utopos%2Fkey_validator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/utopos","download_url":"https://codeload.github.com/utopos/key_validator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utopos%2Fkey_validator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28755557,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T16:32:25.380Z","status":"ssl_error","status_checked_at":"2026-01-25T16:32:09.189Z","response_time":113,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","macro","macros","struct"],"created_at":"2024-09-25T00:05:54.831Z","updated_at":"2026-01-25T16:33:07.131Z","avatar_url":"https://github.com/utopos.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Key Validator\n\nCompile-time validation to assure all the map/keyword keys exist in the target struct. Use case: maps that will be merged with structs.\n\nExposes the `KeyValidator.for_struct/2` macro.\n\n# Installation\n\nThe package can be installed by adding live_stream_async to your list of dependencies in mix.exs:\n\n```elixir\ndef deps do\n  [\n    {:key_validator, \"~\u003e 0.1.0\", runtime: false}\n  ]\nend\n```\n\n## Testing\n\nLibrary ready for testing:\n\n```bash\nmix test\n```\n\n## Use cases\n\nThe macro targets the situations where working with map/keyword literals that will be later cast onto the known structs.\n\nElixir and Ecto has built-in functions that perform the key validity check, but only at runtime:\n\n- `Kernel.struct!/2`\n- `Ecto.Query.API.merge/2`\n\nIn certain situations, the conformity between map/keyword keys can be checked already at the compile-time. One example is when we have present map/keyword **literals** in our code that we know ahead that will be used for casting onto structs. Let's take a look at the following example:\n\n```elixir\ndefmodule User do\ndefstruct name: \"john\"\nend\n\n# Following line is a runtime only check:\n\nKernel.struct!(User, %{name: \"Jakub\"})\n#=\u003e %User{name: \"Jakub\"}\n\n# Runtime error on key typo:\n\nKernel.struct!(User, %{nam__e: \"Jakub\"})\n#=\u003e ** (KeyError) key :nam__e not found\n```\n\nThe expression `Kernel.struct!(User, %{name: \"Jakub\"})` uses a map literal (`%{name: \"Jakub\"}`). Since the User struct module together with the map literal is defined at the compile time, we can leverage the power of compile-time macros to validate those. This is where `KeyValidator.for_struct/2` comes to help:\n\n```elixir\ndefmodule User do\ndefstruct name: \"john\"\nend\n\nimport KeyValidator\n\n# Succesfull validation. Returns the map:\n\nuser_map = for_struct(User, %{name: \"Jakub\"})\n#=\u003e %{name: \"Jakub\"}\n\nKernel.struct!(User, user_map)\n#=\u003e %User{name: \"Jakub\"}\n\n# Compile time error on \"nam__e:\" key typo\n\nuser_map2 = for_struct(User, %{nam__e: \"Jakub\"})\n#=\u003e** (KeyError) Key :name_e not found in User\n```\n\nAs we can see `for_struct/2` macro allows some category of errors to be caught at very early stage in the development workflow. No need to wait the code to crash at runtime if there's a opportunity to check the key conformity before that. This is not a silver bullet though: the macro cannot accept dynamic variables, because their content cannot be evaluated during compilation.\n\n## Extended example\n\nUseful to work with Ecto.Query.select_merge/3 when working with `virtual_fields`\n\n```elixir\ndefmodule Post do\nuse Ecto.Schema\nschema \"posts\" do\n  field :author_firstname, :string\n  field :author_lastname, :string\n  field :author, :string, virtual_field: true\nend\nend\n\ndefmodule Posts do\nimport KeyValidator\n\ndef list_posts do\n  Post\n  |\u003e select_merge([p], for_struct(Post, %{author: p.author_firstname \u003c\u003e \" \" \u003c\u003e p.author_lastname}))\n  |\u003e Repo.all()\nend\nend\n```\n\nThe following code will raise a Key Error with message: \"Key :author_non_existent_key not found in Post\"\n\n```elixir\ndefmodule Posts do\nimport KeyValidator\n\ndef list_posts_error do\nPost\n|\u003e select_merge([p], for_struct(Post, %{author_non_existent_key: \"some value\"}))\n|\u003e Repo.all()\n  end\nend\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futopos%2Fkey_validator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Futopos%2Fkey_validator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futopos%2Fkey_validator/lists"}