Skip to content

Instantly share code, notes, and snippets.

@schickling
Last active September 29, 2023 10:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schickling/7f8ef3a53088f801c55f70b5bc6080ab to your computer and use it in GitHub Desktop.
Save schickling/7f8ef3a53088f801c55f70b5bc6080ab to your computer and use it in GitHub Desktop.
import { describe, expect, it } from 'vitest'
import { Equal } from '../index.js'
import * as Schema from './index.js'
import { JsonWrapper, jsonWrapper } from './SchemaJsonWrapper.js'
describe('wrapper', () => {
it('wrapper', () => {
const wrapped = jsonWrapper(Schema.number)
const decoded = Schema.parseSync(wrapped)(10)
expect(decoded).toEqual(new JsonWrapper(10))
})
it('wrapper array', () => {
const wrapped = Schema.array(jsonWrapper(Schema.number))
const decoded = Schema.parseSync(wrapped)([10, 20])
expect(decoded).toEqual([new JsonWrapper(10), new JsonWrapper(20)])
})
it('wrapper schema-class', () => {
type ProfilePicture = { url: string; description: string }
const makeProfilePicture = () => ({ url: 'https://example.com', description: 'example' })
class User extends Schema.Class<User>()({
id: Schema.string,
profilePicture: Schema.jsonUnsafe<ProfilePicture>(),
}) {}
const parse = Schema.parseSync(User)
const userA = parse({ id: 'u1', profilePicture: makeProfilePicture() })
const userB = parse({ id: 'u1', profilePicture: makeProfilePicture() })
expect(Equal.equals(userA, userB)).toBe(true)
})
it('wrapper schema-class array', () => {
type ProfilePicture = { url: string; description: string }
const makeProfilePicture = () => ({ url: 'https://example.com', description: 'example' })
class User extends Schema.Class<User>()({
id: Schema.string,
profilePictures: Schema.data(Schema.array(Schema.jsonUnsafe<ProfilePicture>())),
}) {}
const parse = Schema.parseSync(User)
const userA = parse({ id: 'u1', profilePictures: [makeProfilePicture()] })
// console.dir(userA, { depth: null, colors: true })
const userB = parse({ id: 'u1', profilePictures: [makeProfilePicture()] })
expect(Equal.equals(userA, userB)).toBe(true)
})
})
/* eslint-disable prefer-arrow/prefer-arrow-functions */
import * as Equal from '@effect/data/Equal'
import * as Hash from '@effect/data/Hash'
import type { Arbitrary } from '@effect/schema/Arbitrary'
import { ArbitraryHookId } from '@effect/schema/Arbitrary'
import * as AST from '@effect/schema/AST'
import * as P from '@effect/schema/Parser'
import * as PR from '@effect/schema/ParseResult'
import type { Pretty } from '@effect/schema/Pretty'
import { PrettyHookId } from '@effect/schema/Pretty'
import * as Schema from '@effect/schema/Schema'
// TODO get rid of this in favor of a better upstream solution
export class JsonWrapper<A> {
constructor(readonly value: A) {}
[Hash.symbol]() {
return Hash.string(JSON.stringify(this.value))
}
[Equal.symbol](that: Equal.Equal): boolean {
return that instanceof JsonWrapper && JSON.stringify(this.value) === JSON.stringify(that.value)
}
}
const arb =
<A>(item: Arbitrary<A>): Arbitrary<JsonWrapper<A>> =>
(fc) =>
item(fc).map((a) => new JsonWrapper(a))
const pretty =
<A>(item: Pretty<A>): Pretty<JsonWrapper<A>> =>
(c) =>
`Wrapper(${item(c.value)})`
const wrapperFromSelf = <I, A>(item: Schema.Schema<I, A>): Schema.Schema<JsonWrapper<I>, JsonWrapper<A>> => {
const schema = Schema.declare(
[item],
Schema.struct({ value: item }),
<A>(_isDecoding: boolean, item: Schema.Schema<A>) => {
const parse = P.parse(item)
return (u, options) =>
typeof u === 'object' && u !== null && u instanceof JsonWrapper
? PR.map(parse(u.value, options), (a) => new JsonWrapper(a))
: PR.failure(PR.type(schema.ast, u))
},
{
[AST.IdentifierAnnotationId]: 'Wrapper',
[PrettyHookId]: pretty,
[ArbitraryHookId]: arb,
},
)
return schema
}
export const jsonWrapper = <I, A>(item: Schema.Schema<I, A>): Schema.Schema<I, JsonWrapper<A>> =>
Schema.transform(
item,
Schema.to(wrapperFromSelf(item)),
(a) => new JsonWrapper(a),
(w) => w.value,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment