wingyplus

wingyplus

ZoiDefstruct - an extension for Zoi to generate Elixir struct

I just did a dirty hack after seeing Zoi on x.com a few hours ago.

Quick Introduction

The zoi_defstruct is a library to help you generate a struct from the Zoi object schema (over Zoi.object). Let’s see a small example below:

defmodule K8S.Deployment do
  use ZoiDefstruct

  @schema Zoi.object(%{
            apiVersion: Zoi.string(),
            metadata:
              Zoi.object(%{
                name: Zoi.string()
              }),
            spec:
              Zoi.object(%{
                replicas: Zoi.integer() |> Zoi.optional()
              })
          })

  defstructure(@schema)
end

When you call a defstructure The macro generates a struct with enforced keys and typespec for you automatically. So in the IEx, you can see:

A struct:

iex(4)> K8S.Deployment.__struct__()
%K8S.Deployment{spec: nil, metadata: nil, apiVersion: nil}

A typespec declaration:

iex(1)> t K8S.Deployment
@type t() :: %{
        spec: %{optional(:replicas) => integer()},
        metadata: %{name: binary()},
        apiVersion: binary()
      } 

ArgumentError if it has no required keys:

iex(5)> %K8S.Deployment{}
** (ArgumentError) the following keys must also be given when building struct K8S.Depl
oyment: [:spec, :metadata, :apiVersion]
    (examples 0.1.0) expanding struct: K8S.Deployment.__struct__/1
    iex:5: (file)

Since this is a quick hack, it has numerous edge cases that need to be addressed. Please feel free to provide feedback or suggestions. Opening an issue or pull request is very welcome.

I need to give special thanks to Zoi for making this awesome library!

https://github.com/wingyplus/zoi_defstruct

First Post!

byu

byu

Great. will check it out.

How does it differ from zoi/lib/zoi/struct.ex at main · phcurado/zoi · GitHub ?

Most Liked

charlieaten

charlieaten

Great work @wingyplus, this is some inspiring stuff.

One concern I had though with this approach is that having a defstruct-like DSL that looks like Kernel.defstruct but behaves differently can be confusing.

Your work inspired me to experiment with Sigil, a proof-of-concept DSL for defining nested structs and validating external data using Zoi:

defmodule Post do
  use Sigil.Schema

  schema do
    field :title, Zoi.string()

    field :image, Image do
      field :url, Zoi.string()
      field :blurhash, Zoi.string()

      field :metadata, Metadata do
        field :size, Zoi.integer() |> Zoi.optional()
        field :format, Zoi.string() |> Zoi.optional()
      end
    end
  end
end

I’m still iterating and may try more compact forms for types like:

field :age, :integer, coerce: true

This has been a great way to learn macros and DSL design, and aims to complement(and obviously heavily inspired by) Ecto. Whereas Ecto handles data mapping and persistence at the external data/application(and database) layer, validation libraries like zoi and the likes and thus Sigil aim to provide a layer that works great for things like wrapping external api’s into client libraries and doing validation and egornomic casting into elixir constructs.

Thanks again—I hope others find it interesting.

phcurado

phcurado

awesome @wingyplus

You can leverage some of the helpers on the Zoi Struct module as @byu mentioned. I personally decided to not add any macros into the library (unless strictly necessary like infering type specs) but I added these helpers for anyone who wishes to extend it.
Thanks for using Zoi, awesome to see this

nulltree

nulltree

I’m so happy libraries like Zoi seem to catch on and inspire extension (infant adoption steps, but still).

They complement my way of thinking really well!

Where Next?

Trending in Announcing Top

webofbits
Squid Mesh is an open source workflow automation runtime for Elixir applications. It is aimed at Phoenix and OTP apps that want to defin...
New
GenericJam
Short version up top for anyone who just wants the ask: BEAM has had hot code loading since the jump. Sloppy Joe takes advantage of that...
New
tubedude
Finance does cash-flow analysis — XIRR/IRR, NPV, MIRR, time-value-of-money, depreciation — matching Excel and Google Sheets closely, and ...
New
Potions
Hi everyone! I’m Alekx, some of you may know me from ElixirCasts. I always wished there were a simple, affordable way to get Phoenix app...
New
kip
I’ve published localize_mcp, a Model Context Protocol server for the Localize library. This is the last piece of the family before 1.0 r...
New
mhs
Hi all, I want to share Googly, a code generator that turns a Google API’s Discovery document into a ready-to-use Elixir client. It’s i...
New
nseaSeb
Pure-Elixir toolkit for Factur-X / ZUGFeRD hybrid electronic invoices — a PDF/A-3 file with a machine-readable CII XML payload embedded i...
New

Other Trending Topics Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2976 91332 914
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
zachdaniel
Introducing AshStorage! Attachment and file management that slots directly into your resources :smiling_face_with_sunglasses: I had hope...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jola
I’ve been bumping into some rough edges recently with Elixir dependencies that use app config and start their own supervision trees and I...
New
quatermain
Hello, I’m sharing my plugin here in forum after some time so it has time to mature and proof yourself. I use Claude Code daily on a pr...
New

We're in Beta

About us Mission Statement