Skip to content

Instantly share code, notes, and snippets.

@wmaurer
Created October 31, 2023 20:17
Show Gist options
  • Save wmaurer/f8b13913b3bfa7d04a80170689a50e69 to your computer and use it in GitHub Desktop.
Save wmaurer/f8b13913b3bfa7d04a80170689a50e69 to your computer and use it in GitHub Desktop.
Decoder
import type { ParseOptions } from "@effect/schema/AST"
import * as Parser from "@effect/schema/Parser"
import * as PR from "@effect/schema/ParseResult"
import * as S from "@effect/schema/Schema"
import { pipe } from "effect"
const TypeId: unique symbol = Symbol.for("@typed/decoder/decoder") as TypeId
export type TypeId = typeof TypeId
interface Decoder<I, O> {
[TypeId]: TypeId
decode(i: I, options?: ParseOptions): PR.ParseResult<O>
}
interface SchemaDecoder<From, To = From> extends S.Schema<From, To>, Decoder<unknown, To> {}
function fromSchema<From, To>(schema: S.Schema<From, To>): SchemaDecoder<From, To> {
return {
...schema,
[TypeId]: TypeId,
decode(i: unknown, options?: ParseOptions): PR.ParseResult<To> {
return Parser.validate(schema)(i, options)
}
}
}
function map<A, B>(f: (a: A) => B): <I>(decoder: Decoder<I, A> | S.Schema<I, A>) => Decoder<I, B> {
return (decoder) => {
const _decoder: Decoder<any, any> = (decoder as any)[TypeId] === TypeId
? (decoder as Decoder<any, any>)
: fromSchema(decoder as S.Schema<any, any>)
return ({
[TypeId]: TypeId,
decode: (i: any, options?: ParseOptions) => PR.map(_decoder.decode(i, options), f)
})
}
}
function parse<A, B>(f: (a: A) => PR.ParseResult<B>): <I>(decoder: Decoder<I, A> | S.Schema<I, A>) => Decoder<I, B> {
return (decoder) => {
const _decoder: Decoder<any, any> = (decoder as any)[TypeId] === TypeId
? (decoder as Decoder<any, any>)
: fromSchema(decoder as S.Schema<any, any>)
return ({
[TypeId]: TypeId,
decode: (i: any, options?: ParseOptions) => PR.flatMap(_decoder.decode(i, options), f)
})
}
}
// TODO: compose
const WordsSplit = S.string.pipe(
parse((a) =>
a.indexOf(" ") !== -1
? PR.success(a.split(" ") as [string, string, ...Array<string>])
: PR.failure(PR.type(S.string.ast, a, "must contain at least one word"))
)
)
const SecondWord = pipe(WordsSplit, map((a) => a[1]))
const SecondWordSplitByO = pipe(
SecondWord,
parse((a) =>
a.indexOf("o") !== -1
? PR.success(a.split("o") as [string, string, ...Array<string>])
: PR.failure(PR.type(S.string.ast, a, "must contain at least one 'o'"))
)
)
const SecondWordSplitByOTailLength = pipe(SecondWordSplitByO, map((a) => a[1].length))
SecondWordSplitByOTailLength.decode("hello world") // ?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment