Coder Social home page Coder Social logo

serde-zod's Introduction

serde-zod

Generate zod definitions from your JSON-serializable types in Rust.

Why

This library was created whilst building a Tauri App where the architecture encourages heavy use of JSON serializable messaging.

Having those message structures described in Rust structs/enums was fantastic, but losing the type information on the frontend was a shame - so I built this attribute macro to solve that problem ๐Ÿ’ช๐Ÿ˜Ž

Install

It's not on crates.io yet, it will be soon.

In the meantime if you want to try it out, you can reference the git repo in your Cargo.toml

serde_zod = { git = "https://github.com/shakyShane/serde-zod.git#main" }

Features

  • structs -> z.object()
  • Optimized enum representation
    • defer to z.enum(["A", "B") when a Rust enum contains only unit variants (no sub-fields)
    • use z.discriminatedUnion("tag", ...) when attribute serde(tag = "kind") is used
    • fall back to z.union if fields are mixed
  • array subtype via Vec<T>
  • optional types Option<T>
  • HashMap/BTreeMap
  • Set/BTreeSet
  • serde rename_all
  • serde rename_field
  • document all available output types
rust zod
enum with only unit variants z.enum([...])
enum with "tagged" variants z.discriminatedUnion("tag", ...)
enum with mixed variants z.union([...])
String z.string()
usize|u8|u16|f32|f64 etc (numbers) z.number()
Option z.string().optional()
Struct/Enum fields z.object({ ... })

See the tests for more examples, or the Typescript output to see what it generates.

Basic Usage

Add the #[serde_zod::codegen] attribute above any existing Rust struct or enum where you already have #[derive(serde::Serialize)] or #[derive(serde::Deserialize)]

#[serde_zod::codegen]
#[derive(serde::Serialize)]
pub struct Person {
  age: u8,
}

With that, you can then create a binary application (alongside your lib, for example) to output the zod definitions

fn main() {
    let lines = vec![
        Person::print_imports(), // โฌ…๏ธ only needed once
        Person::codegen(),
    ];
    fs::write("./app/types.ts", lines.join("\n")).expect("hooray!");
}

output

import z from "zod"

export const Person =
  z.object({
    age: z.number()
  })

// โœ… usage
const person = Person.parse({age: 21})

Output Types

z.enum()

When you have a Rust enum with only unit variants - meaning all variants are 'bare' or 'without nested fields', then serde-zod will print a simple z.enum(["A", "B"])

input

#[serde_zod::codegen]
#[derive(Debug, Clone, serde::Serialize)]
pub enum UnitOnlyEnum {
    Stop,
    Toggle,
}

#[serde_zod::codegen]
#[derive(serde::Serialize)]
pub struct State {
    control: UnitOnlyEnum,
}

output

import z from "zod"

export const UnitOnlyEnum =
  z.enum([
    "Stop",
    "Toggle",
  ])

export const State =
  z.object({
    control: UnitOnlyEnum,
  })

// usage

// โŒ Invalid enum value. Expected 'Stop' | 'Toggle', received 'oops'
const msg = State.parse({control: "oops"})

// โœ… Both valid
const msg1 = State.parse({control: "Stop"})
const msg2 = State.parse({control: "Toggle"})

z.discriminatedUnion

When you use #[serde(tag="<tag>")] on a Rust enum, it can be represented by Typescript as a 'discriminated union'. So, when serde-zod notices tag=<value>, it will generate the optimized zod definitions. These offer the best-in-class type inference when used in Typescript

input

#[serde_zod::codegen]
#[derive(serde::Serialize)]
#[serde(tag = "kind")]
pub enum Control {
    Start { time: u32 },
    Stop,
    Toggle,
}

output

import z from "zod"

export const Control =
  z.discriminatedUnion("kind", [
    z.object({
      kind: z.literal("Start"),
      time: z.number(),
    }),
    z.object({
      kind: z.literal("Stop"),
    }),
    z.object({
      kind: z.literal("Toggle"),
    }),
  ])

// this usage show the type narrowing in action
const message = Control.parse({ kind: "Start", time: 10 });
if (message.kind === "Start") {
  console.log(message.time) // โœ… ๐Ÿ˜ Type-safe property access here on `time`
}

z.union()

This is the most flexible type, but also gives the least type information and produces the most unrelated errors (you get an errorr for each unmatched enum variant)

input

#[serde_zod::codegen]
#[derive(serde::Serialize)]
pub enum MixedEnum {
    One,
    Two(String),
    Three { temp: usize },
}

output

import z from "zod"

export const MixedEnum =
  z.union([
    z.literal("One"),
    z.object({
      Two: z.string(),
    }),
    z.object({
      Three: z.object({
        temp: z.number(),
      }),
    }),
  ])

// โœ… all of these are valid, but don't product great type information
MixedEnum.parse("One")
MixedEnum.parse({Two: "hello...."})
MixedEnum.parse({Three: { temp: 3 }})

serde-zod's People

Contributors

shakyshane avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.