Skip to content

Commit

Permalink
update readme with test example
Browse files Browse the repository at this point in the history
  • Loading branch information
lostbean committed Nov 29, 2024
1 parent 086183c commit 1c20bfb
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 48 deletions.
139 changes: 136 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,153 @@
# blueprint

Blueprint is a Gleam library that simplifies JSON encoding and decoding while automatically generating JSON schemas for your data types.

[![Package Version](https://img.shields.io/hexpm/v/blueprint)](https://hex.pm/packages/blueprint)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/blueprint/)

```sh
gleam add blueprint1
gleam add blueprint
```

## Usage

Blueprint provides utilities for encoding and decoding JSON data, with special support for union types.

### Encoding Union Types

Here's an example of encoding a union type to JSON:

```gleam
import blueprint
import gleam/json
import gleam/io
import gleeunit/should
pub type Shape {
Circle(Float)
Rectangle(Float, Float)
}
fn encode_shape(shape: Shape) -> json.Json {
blueprint.union_type_encoder(shape, fn(shape_case) {
case shape_case {
Circle(radius) -> #(
"circle",
json.object([#("radius", json.float(radius))]),
)
Rectangle(width, height) -> #(
"rectangle",
json.object([
#("width", json.float(width)),
#("height", json.float(height)),
]),
)
}
})
}
fn shape_decoder() -> blueprint.Decoder(Shape) {
blueprint.union_type_decoder([
#(
"circle",
blueprint.decode1(Circle, blueprint.field("radius", blueprint.float())),
),
#(
"rectangle",
blueprint.decode2(
Rectangle,
blueprint.field("width", blueprint.float()),
blueprint.field("height", blueprint.float()),
),
),
])
}
fn simple_test() {
let decoder = shape_decoder()
// Test encoding a Circle
let circle = Circle(5.0)
encode_shape(circle)
|> json.to_string
|> blueprint.decode(using: decoder)
|> should.equal(Ok(circle))
// Test encoding a Rectangle
let rectangle = Rectangle(10.0, 20.0)
encode_shape(rectangle)
|> json.to_string
|> blueprint.decode(using: decoder)
|> should.equal(Ok(rectangle))
// Print JSON schema
decoder
|> blueprint.generate_json_schema()
|> json.to_string
|> io.println
}
```

pub fn main() {
// TODO: An example of the project in use
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"oneOf": [
{
"required": ["type", "data"],
"additionalProperties": false,
"type": "object",
"properties": {
"type": {
"enum": ["circle"]
},
"data": {
"required": ["radius"],
"additionalProperties": false,
"type": "object",
"properties": {
"radius": {
"type": "number"
}
}
}
}
},
{
"required": ["type", "data"],
"additionalProperties": false,
"type": "object",
"properties": {
"type": {
"enum": ["rectangle"]
},
"data": {
"required": ["width", "height"],
"additionalProperties": false,
"type": "object",
"properties": {
"width": {
"type": "number"
},
"height": {
"type": "number"
}
}
}
}
}
]
}
```

This will encode your union types into a standardized JSON format with `type` and `data` fields, making it easy to decode on the receiving end.

## Features

- 🎯 Type-safe JSON encoding and decoding
- 🔄 Support for union types with standardized encoding
- 📋 Automatic JSON schema generation
- ✨ Clean and intuitive API

Further documentation can be found at <https://hexdocs.pm/blueprint>.

## Development
Expand Down
88 changes: 43 additions & 45 deletions test/blueprint_test.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import blueprint
import gleam/io
import gleam/json
import gleam/option.{type Option, None, Some}
import gleeunit
Expand Down Expand Up @@ -283,6 +282,41 @@ type Shape {
Rectangle(Float, Float)
}

fn encode_shape(shape: Shape) -> json.Json {
blueprint.union_type_encoder(shape, fn(shape_case) {
case shape_case {
Circle(radius) -> #(
"circle",
json.object([#("radius", json.float(radius))]),
)
Rectangle(width, height) -> #(
"rectangle",
json.object([
#("width", json.float(width)),
#("height", json.float(height)),
]),
)
}
})
}

fn shape_decoder() -> blueprint.Decoder(Shape) {
blueprint.union_type_decoder([
#(
"circle",
blueprint.decode1(Circle, blueprint.field("radius", blueprint.float())),
),
#(
"rectangle",
blueprint.decode2(
Rectangle,
blueprint.field("width", blueprint.float()),
blueprint.field("height", blueprint.float()),
),
),
])
}

pub fn constructor_type_decoder_test() {
let shape_decoder =
blueprint.union_type_decoder([
Expand Down Expand Up @@ -315,10 +349,6 @@ pub fn constructor_type_decoder_test() {
let expected_schema_str =
"{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"oneOf\":[{\"required\":[\"type\",\"data\"],\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"type\":{\"enum\":[\"circle\"]},\"data\":{\"required\":[\"radius\"],\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"radius\":{\"type\":\"number\"}}}}},{\"required\":[\"type\",\"data\"],\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"type\":{\"enum\":[\"rectangle\"]},\"data\":{\"required\":[\"width\",\"height\"],\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"number\"},\"height\":{\"type\":\"number\"}}}}}]}"

schema
|> json.to_string
|> io.println

schema
|> json.to_string
|> should.equal(expected_schema_str)
Expand All @@ -330,33 +360,17 @@ pub fn union_type_encoder_test() {
// Test encoding a Rectangle
let rectangle = Rectangle(10.0, 20.0)

let json_encoder = fn(input) {
blueprint.union_type_encoder(input, fn(shape) {
case shape {
Circle(radius) -> #(
"circle",
json.object([#("radius", json.float(radius))]),
)
Rectangle(width, height) -> #(
"rectangle",
json.object([
#("width", json.float(width)),
#("height", json.float(height)),
]),
)
}
})
}

json_encoder(circle)
let decoder = shape_decoder()

encode_shape(circle)
|> should.equal(
json.object([
#("type", json.string("circle")),
#("data", json.object([#("radius", json.float(5.0))])),
]),
)

json_encoder(rectangle)
encode_shape(rectangle)
|> should.equal(
json.object([
#("type", json.string("rectangle")),
Expand All @@ -371,29 +385,13 @@ pub fn union_type_encoder_test() {
)

//test decoding
let shape_decoder =
blueprint.union_type_decoder([
#(
"circle",
blueprint.decode1(Circle, blueprint.field("radius", blueprint.float())),
),
#(
"rectangle",
blueprint.decode2(
Rectangle,
blueprint.field("width", blueprint.float()),
blueprint.field("height", blueprint.float()),
),
),
])

json_encoder(circle)
encode_shape(circle)
|> json.to_string
|> blueprint.decode(using: shape_decoder)
|> blueprint.decode(using: decoder)
|> should.equal(Ok(circle))

json_encoder(rectangle)
encode_shape(rectangle)
|> json.to_string
|> blueprint.decode(using: shape_decoder)
|> blueprint.decode(using: decoder)
|> should.equal(Ok(rectangle))
}

0 comments on commit 1c20bfb

Please sign in to comment.