/** * @since 1.0.0 */ import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" import * as Data from "effect/Data" import * as Effect from "effect/Effect" import { dual } from "effect/Function" import type { ParseError } from "effect/ParseResult" import * as ParseResult from "effect/ParseResult" import * as Predicate from "effect/Predicate" import * as Schema from "effect/Schema" import { Packr, Unpackr } from "msgpackr" import * as Msgpackr from "msgpackr" import * as ChannelSchema from "./ChannelSchema.js" /** * @since 1.0.0 * @category errors */ export const ErrorTypeId: unique symbol = Symbol.for("@effect/platform/MsgPack/MsgPackError") /** * @since 1.0.0 * @category errors */ export type ErrorTypeId = typeof ErrorTypeId /** * @since 1.0.0 * @category errors */ export class MsgPackError extends Data.TaggedError("MsgPackError")<{ readonly reason: "Pack" | "Unpack" readonly cause: unknown }> { /** * @since 1.0.0 */ readonly [ErrorTypeId]: ErrorTypeId = ErrorTypeId /** * @since 1.0.0 */ get message() { return this.reason } } /** * @since 1.0.0 * @category constructors */ export const pack = (): Channel.Channel< Chunk.Chunk, Chunk.Chunk, IE | MsgPackError, IE, Done, Done > => Channel.suspend(() => { const packr = new Packr() const loop: Channel.Channel, Chunk.Chunk, IE | MsgPackError, IE, Done, Done> = Channel .readWithCause({ onInput: (input) => Channel.zipRight( Channel.flatMap( Effect.try({ try: () => Chunk.of(packr.pack(Chunk.toReadonlyArray(input))), catch: (cause) => new MsgPackError({ reason: "Pack", cause }) }), Channel.write ), loop ), onFailure: (cause) => Channel.failCause(cause), onDone: Channel.succeed }) return loop }) /** * @since 1.0.0 * @category constructors */ export const packSchema = ( schema: Schema.Schema ) => (): Channel.Channel< Chunk.Chunk, Chunk.Chunk, IE | MsgPackError | ParseError, IE, Done, Done, R > => Channel.pipeTo(ChannelSchema.encode(schema)(), pack()) /** * @since 1.0.0 * @category constructors */ export const unpack = (): Channel.Channel< Chunk.Chunk, Chunk.Chunk, IE | MsgPackError, IE, Done, Done > => Channel.flatMap( Channel.sync(() => new Unpackr()), (packr) => { let incomplete: Uint8Array | undefined = undefined const unpack = (value: Chunk.Chunk) => Effect.try({ try: () => Chunk.flatMap(value, (buf) => { if (incomplete !== undefined) { const chunk = new Uint8Array(incomplete.length + buf.length) chunk.set(incomplete) chunk.set(buf, incomplete.length) buf = chunk incomplete = undefined } try { return Chunk.unsafeFromArray(packr.unpackMultiple(buf).flat()) } catch (error_) { const error: any = error_ if (error.incomplete) { incomplete = buf.subarray(error.lastPosition) return Chunk.unsafeFromArray(error.values ?? []) } throw error } }), catch: (cause) => new MsgPackError({ reason: "Unpack", cause }) }) const loop: Channel.Channel, Chunk.Chunk, IE | MsgPackError, IE, Done, Done> = Channel.readWithCause({ onInput: (input: Chunk.Chunk) => Channel.zipRight( Channel.flatMap(unpack(input), Channel.write), loop ), onFailure: (cause) => Channel.failCause(cause), onDone: Channel.succeed }) return loop } ) /** * @since 1.0.0 * @category constructors */ export const unpackSchema = ( schema: Schema.Schema ) => (): Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | IE, IE, Done, Done, R > => Channel.pipeTo(unpack(), ChannelSchema.decodeUnknown(schema)()) /** * @since 1.0.0 * @category combinators */ export const duplex = ( self: Channel.Channel, Chunk.Chunk, OE, IE | MsgPackError, OutDone, InDone, R> ): Channel.Channel, Chunk.Chunk, MsgPackError | OE, IE, OutDone, InDone, R> => Channel.pipeTo( Channel.pipeTo(pack(), self), unpack() ) /** * @since 1.0.0 * @category combinators */ export const duplexSchema: { /** * @since 1.0.0 * @category combinators */ ( options: { readonly inputSchema: Schema.Schema readonly outputSchema: Schema.Schema } ): ( self: Channel.Channel< Chunk.Chunk, Chunk.Chunk, OutErr, MsgPackError | ParseError | InErr, OutDone, InDone, R > ) => Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | OutErr, InErr, OutDone, InDone, IR | OR | R > /** * @since 1.0.0 * @category combinators */ ( self: Channel.Channel< Chunk.Chunk, Chunk.Chunk, OutErr, MsgPackError | ParseError | InErr, OutDone, InDone, R >, options: { readonly inputSchema: Schema.Schema readonly outputSchema: Schema.Schema } ): Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | OutErr, InErr, OutDone, InDone, R | IR | OR > } = dual< /** * @since 1.0.0 * @category combinators */ ( options: { readonly inputSchema: Schema.Schema readonly outputSchema: Schema.Schema } ) => ( self: Channel.Channel< Chunk.Chunk, Chunk.Chunk, OutErr, MsgPackError | ParseError | InErr, OutDone, InDone, R > ) => Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | OutErr, InErr, OutDone, InDone, R | IR | OR >, /** * @since 1.0.0 * @category combinators */ ( self: Channel.Channel< Chunk.Chunk, Chunk.Chunk, OutErr, MsgPackError | ParseError | InErr, OutDone, InDone, R >, options: { readonly inputSchema: Schema.Schema readonly outputSchema: Schema.Schema } ) => Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | OutErr, InErr, OutDone, InDone, R | IR | OR > >(2, ( self: Channel.Channel< Chunk.Chunk, Chunk.Chunk, OutErr, MsgPackError | ParseError | InErr, OutDone, InDone, R >, options: { readonly inputSchema: Schema.Schema readonly outputSchema: Schema.Schema } ): Channel.Channel< Chunk.Chunk, Chunk.Chunk, MsgPackError | ParseError | OutErr, InErr, OutDone, InDone, R | IR | OR > => ChannelSchema.duplexUnknown(duplex(self), options)) /** * @since 1.0.0 * @category schemas */ export interface schema extends Schema.transformOrFail, S> {} /** * @since 1.0.0 * @category schemas */ export const schema = (schema: S): schema => Schema.transformOrFail( Schema.Uint8ArrayFromSelf, schema, { decode(fromA, _, ast) { return ParseResult.try({ try: () => Msgpackr.decode(fromA) as Schema.Schema.Encoded, catch: (cause) => new ParseResult.Type( ast, fromA, Predicate.hasProperty(cause, "message") ? String(cause.message) : String(cause) ) }) }, encode(toI, _, ast) { return ParseResult.try({ try: () => Msgpackr.encode(toI), catch: (cause) => new ParseResult.Type( ast, toI, Predicate.hasProperty(cause, "message") ? String(cause.message) : String(cause) ) }) } } )