drytypes

package module
v0.0.0-...-2277b33 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 9 Imported by: 0

README

go-ruby-dry-types/dry-types

dry-types — go-ruby-dry-types

Docs License Go Coverage

A pure-Go (no cgo) MRI-faithful reimplementation of Ruby's dry-types gem — a composable type system with coercion, constraints, and combinators. Every type is a value you build from a constructor (Strict, Coercible, Params, JSON, Nominal) and refine with combinator methods (Optional, Default, Constrained, Enum, Or, Constructor, Array.of, Hash.schema). Applying a type to an input coerces and validates it, returning the coerced value or an error whose message is byte-identical to the dry-types gem's — without any Ruby runtime.

It is the type-system backend for go-embedded-ruby and feeds the dry-struct / dry-validation ports; it is a standalone, reusable module with no dependency on the Ruby runtime.

Features

Faithful port of dry-types' coercion + validation, differentially validated against the dry-types gem on Ruby ≥ 4.0:

  • Nominal / strict typesStrict::{Integer,String,Float,Bool,Symbol,Array, Hash,Date,Time,DateTime,Nil} (pure type? check, raise ConstraintError), and unconstrained Nominal::* pass-through types.
  • Coercible typesCoercible::{Integer,Float,String,Symbol} using the target's Ruby coercion rules (Kernel#Integer with 0x/0o/0b prefixes and underscore separators, Kernel#Float, #to_s, #to_sym); raise CoercionError.
  • Params types — form-param coercion: Params::{Integer,Float,Bool,Nil, Symbol,Date,Time,DateTime}, with "1"1, "true"true, ""nil, and the gem's exact TRUE_VALUES / FALSE_VALUES sets.
  • JSON typesJSON::{Date,Time,DateTime,Symbol,Nil}.
  • Combinators.optional (nil-allowed), .default(x) and callable defaults, .constrained(gt:, gteq:, lt:, lteq:, format:, size:, min_size:, max_size:, included_in:, excluded_from:, filled:, eql:, …), .enum(...), sum types A | B, .meta, .constructor(fn), Array.of(T), and Hash.schema with required / optional keys and .strict.
  • Exact error parityCoercionError, ConstraintError, SchemaError, MissingKeyError, UnknownKeysError messages match the gem verbatim.

Usage

import drytypes "github.com/go-ruby-dry-types/dry-types"

age := drytypes.CoercibleInteger()
v, err := age.Call("30")            // v == int64(30)

adult := drytypes.StrictInteger().Constrained(
    drytypes.Constraint{Name: "gteq", Arg: 18})
_, err = adult.Call(17)             // "17 violates constraints (gteq?(18, 17) failed)"

status := drytypes.StrictString().Enum("draft", "published")
_, err = status.Call("x")           // included_in?(...) constraint error

user := drytypes.NewSchema(
    drytypes.SchemaKey{Key: "name", Type: drytypes.StrictString()},
    drytypes.SchemaKey{Key: "age", Type: drytypes.CoercibleInteger()},
)
out, err := user.Call(map[string]any{"name": "Jane", "age": "30"})

Every type also answers drytypes.Valid(t, input) bool and drytypes.Try(t, input) Result (a Success / Failure carrying the coerced value or the error).

Value model

Ruby values are the same small, fixed set of Go types the go-ruby-* ecosystem uses, so a host (go-embedded-ruby / rbgo) maps its object graph to and from this package with no glue: nil, bool, int64 / *big.Int, float64, string, Symbol, []any, *Map (ordered hash), Date, and Time. Host callables (default blocks, constructor functions) are Go closures.

Tests & coverage

The deterministic, ruby-free suite holds 100% statement coverage on its own. A differential oracle additionally compares every coercion, constraint, and combinator outcome — success values and error messages — against the real dry-types gem (gated on RUBY_VERSION >= "4.0"); it skips itself where ruby or the gem is absent (the qemu cross-arch lanes and the Windows lane), so the gate stays green everywhere. Validated on all six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) across Linux, macOS, and Windows.

go test -race -cover ./...

License

BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-dry-types/dry-types authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package drytypes is a pure-Go (CGO-free) MRI-faithful reimplementation of the Ruby dry-types gem: a composable type system with coercion, constraints, and combinators. Every type is a Type — a value you build from a constructor (Strict, Coercible, Params, JSON, Nominal, …) and refine with combinator methods (Optional, Default, Constrained, Enum, Or, Constructor, …). Applying a type to an input coerces and validates it, returning the coerced value or an error whose message is byte-identical to the dry-types gem's.

Ruby value model

Ruby values are represented by the same small, fixed set of Go types the go-ruby-* ecosystem uses, so a host (go-embedded-ruby / rbgo) maps its object graph to and from this package with no glue:

Ruby            Go
----            --
nil             nil
true / false    bool
Integer         int64, *big.Int (int/int32 accepted on input)
Float           float64
String          string
Symbol          Symbol
Array           []any
Hash            *Map (ordered), map[string]any / map[Symbol]any (input)
Date            Date
Time / DateTime Time

A host callable (a default block or a constructor function) is a Go closure of type [func(any) any] or Callable.

Index

Constants

This section is empty.

Variables

View Source
var Undefined = undefined{}

Undefined is dry-types' sentinel for "no value given" — it triggers a Type.Default's fallback. Pass it as the input to Call to request the default. (In the gem this is Dry::Types::Undefined.)

Functions

func ArrayOf

func ArrayOf(elem Type) *baseType

ArrayOf returns a member-typed array type (dry-types' `Array.of(T)`): it first requires an Array, then coerces every element through elem. The first element failure surfaces that element's error (matching the gem).

func Valid

func Valid(t Type, input any) bool

Valid reports whether t accepts input (dry-types' `type.valid?`).

Types

type Callable

type Callable = func(any) any

Callable is a host closure used as a constructor function or a default block.

type CoercionError

type CoercionError struct{ Message string }

CoercionError is raised when a coercion fails (e.g. Integer("abc")). Its message is byte-identical to Dry::Types::CoercionError#message.

func (*CoercionError) Error

func (e *CoercionError) Error() string

type Constraint

type Constraint struct {
	Name string
	Arg  any
}

Constraint is one dry-logic predicate applied by [baseType.Constrained]. The map key is the predicate name (gt, gteq, lt, lteq, format, size, min_size, max_size, included_in, filled, …) and the value is its argument.

type ConstraintError

type ConstraintError struct {
	Message string
	// Input is the offending value.
	Input any
	// Rule is the failed predicate rendered as dry-logic renders it
	// (e.g. `gt?(18, 10) failed`).
	Rule string
}

ConstraintError is raised when a value violates a constraint (including the implicit type? constraint of a strict type). Its message matches Dry::Types::ConstraintError#message: `<input> violates constraints (<rule>)`.

func (*ConstraintError) Error

func (e *ConstraintError) Error() string

type Date

type Date struct {
	Year  int
	Month int
	Day   int
}

Date is a Ruby Date (a calendar day with no time-of-day). It is the coercion target of the *::Date types. Stored as year/month/day plus the parsed underlying time for formatting.

func (Date) String

func (d Date) String() string

String renders the Date the way Ruby's Date#to_s / inspect renders it (ISO-8601 `YYYY-MM-DD`).

type HashSchema

type HashSchema struct {
	// contains filtered or unexported fields
}

HashSchema is a struct-hash type (dry-types' `Hash.schema({...})`). It embeds [*baseType], so it *is* a Type: it composes with every combinator and with ArrayOf. Build one with NewSchema and refine with HashSchema.Strict.

func NewSchema

func NewSchema(keys ...SchemaKey) *HashSchema

NewSchema builds a Hash.schema type from its members.

func (*HashSchema) AsType

func (s *HashSchema) AsType() Type

AsType exposes the schema as a plain Type (identity — a *HashSchema already is a Type; kept for readability at call sites).

func (HashSchema) Call

func (b HashSchema) Call(input any) (any, error)

Call runs the pipeline.

func (HashSchema) Constrained

func (b HashSchema) Constrained(cs ...Constraint) Type

Constrained returns a type that applies each predicate after the base coercion/validation (dry-types' `.constrained(...)`). Predicates run in the order given; the first failure reports the gem's `<pred>?(<arg>, <val>) failed` (or the arity-1 `<pred>?(<val>) failed`) constraint message.

func (HashSchema) Constructor

func (b HashSchema) Constructor(fn Callable) Type

Constructor returns a type that first runs fn over the input, then applies the base type to fn's result (dry-types' `.constructor(fn)`).

func (HashSchema) Default

func (b HashSchema) Default(val any) Type

Default returns a type that substitutes val when the input is Undefined (dry-types' `.default(val)`). The substituted value is returned as-is (the gem does not re-run coercion on a static default).

func (HashSchema) DefaultFn

func (b HashSchema) DefaultFn(fn func() any) Type

DefaultFn returns a type whose default is produced by calling fn (dry-types' `.default { ... }` block form).

func (HashSchema) Enum

func (b HashSchema) Enum(values ...any) Type

Enum returns a type that additionally requires the (coerced) value to be one of values (dry-types' `.enum(...)`), reporting the `included_in?` constraint on a miss.

func (HashSchema) GetMeta

func (b HashSchema) GetMeta() map[string]any

GetMeta returns the attached metadata (dry-types' `.meta`).

func (HashSchema) Meta

func (b HashSchema) Meta(m map[string]any) Type

Meta returns a copy of the type carrying the merged metadata (dry-types' `.meta(...)`).

func (HashSchema) Optional

func (b HashSchema) Optional() Type

Optional returns a type that also accepts nil (dry-types' `.optional`, i.e. `Nil | self`). A nil input yields nil; any other input goes through the base.

func (HashSchema) Or

func (b HashSchema) Or(other Type) Type

Or returns the sum type `self | other` (dry-types' `A | B`): it tries the base first, then other; on total failure it reports other's error (matching the gem, whose sum surfaces the right-hand branch's message).

func (*HashSchema) Strict

func (s *HashSchema) Strict() *HashSchema

Strict returns a copy of the schema that rejects unexpected keys (dry-types' `.strict`), raising *UnknownKeysError.

type Map

type Map struct {
	// contains filtered or unexported fields
}

Map is an insertion-ordered Ruby Hash. Schema coercion yields a *Map so key order round-trips.

func NewMap

func NewMap() *Map

NewMap returns an empty ordered Map.

func (*Map) Get

func (m *Map) Get(key any) (any, bool)

Get returns the value for a comparable key and whether it was present.

func (*Map) Len

func (m *Map) Len() int

Len reports the number of entries.

func (*Map) Pairs

func (m *Map) Pairs() []Pair

Pairs returns the entries in insertion order. The slice must not be mutated.

func (*Map) Set

func (m *Map) Set(key, val any)

Set inserts or replaces the entry for key (comparable keys deduplicate).

type MissingKeyError

type MissingKeyError struct{ Message string }

MissingKeyError is raised by a strict schema when a required key is absent: `:key is missing in Hash input`.

func (*MissingKeyError) Error

func (e *MissingKeyError) Error() string

type Pair

type Pair struct {
	Key any
	Val any
}

Pair is one entry of an ordered mapping.

type Result

type Result struct {
	// contains filtered or unexported fields
}

Result is the outcome of Try: a success carrying the coerced value or a failure carrying the error (mirrors Dry::Types::Result::Success/Failure).

func Try

func Try(t Type, input any) Result

Try applies t and returns a Result (dry-types' `type.try`): Success carries the coerced value, Failure carries the error.

func (Result) Error

func (r Result) Error() error

Error returns the failure's error, or nil on success.

func (Result) Failure

func (r Result) Failure() bool

Failure reports whether the Result is a failure.

func (Result) Input

func (r Result) Input() any

Input returns the coerced value (on success) or the offending input (on failure), matching Dry::Types::Result#input.

func (Result) Success

func (r Result) Success() bool

Success reports whether the Result is a success.

type SchemaError

type SchemaError struct{ Message string }

SchemaError is raised when a schema member fails coercion/validation: `<value> (<Class>) has invalid type for :key <inner rule>`.

func (*SchemaError) Error

func (e *SchemaError) Error() string

type SchemaKey

type SchemaKey struct {
	Key      Symbol
	Type     Type
	Optional bool
}

SchemaKey is one member of a HashSchema: a key, its type, and whether the key is optional (dry-types' trailing `?` on the key name).

type Symbol

type Symbol string

Symbol is a Ruby Symbol (`:name`).

type Time

type Time = time.Time

Time is a Ruby Time / DateTime. It wraps a Go time.Time; the *::DateTime types coerce into the same shape (dry-types coerces both via Date._parse-style parsing and yields a Time-like object the host distinguishes by its own class mapping).

type Type

type Type interface {
	// Call coerces and validates input, returning the coerced value or an error.
	Call(input any) (any, error)

	// Optional returns a type that also accepts nil (dry-types' `.optional`).
	Optional() Type
	// Default substitutes val when the input is [Undefined] (`.default(val)`).
	Default(val any) Type
	// DefaultFn substitutes fn() when the input is [Undefined] (`.default { }`).
	DefaultFn(fn func() any) Type
	// Constructor pre-processes input through fn, then applies the base type.
	Constructor(fn Callable) Type
	// Meta returns a copy carrying the merged metadata (`.meta(...)`).
	Meta(m map[string]any) Type
	// GetMeta returns the attached metadata.
	GetMeta() map[string]any
	// Or returns the sum type `self | other` (`A | B`).
	Or(other Type) Type
	// Enum requires the coerced value to be one of values (`.enum(...)`).
	Enum(values ...any) Type
	// Constrained applies dry-logic predicates after coercion (`.constrained`).
	Constrained(cs ...Constraint) Type
	// contains filtered or unexported methods
}

Type is a composable dry-types type: it coerces-and-validates an input, and carries the combinator methods so types chain fluently (e.g. `StrictInteger().Constrained(...).Optional()`).

Call applies the type: it returns the coerced value on success or a *CoercionError / *ConstraintError / schema error on failure — the same value and message the dry-types gem's `type[input]` produces.

The interface is closed to this package (every Type is a *baseType) via the unexported node method, which keeps combinator composition total.

func CoercibleFloat

func CoercibleFloat() Type

CoercibleFloat is Types::Coercible::Float (Kernel#Float).

func CoercibleInteger

func CoercibleInteger() Type

CoercibleInteger is Types::Coercible::Integer (Kernel#Integer).

func CoercibleString

func CoercibleString() Type

CoercibleString is Types::Coercible::String (#to_s).

func CoercibleSymbol

func CoercibleSymbol() Type

CoercibleSymbol is Types::Coercible::Symbol (#to_sym).

func JSONDate

func JSONDate() Type

JSONDate is Types::JSON::Date.

func JSONDateTime

func JSONDateTime() Type

JSONDateTime is Types::JSON::DateTime.

func JSONNil

func JSONNil() Type

JSONNil is Types::JSON::Nil (passes nil through, else type-checks nil).

func JSONSymbol

func JSONSymbol() Type

JSONSymbol is Types::JSON::Symbol.

func JSONTime

func JSONTime() Type

JSONTime is Types::JSON::Time.

func NominalArray

func NominalArray() Type

NominalArray is Types::Nominal::Array (no constraint).

func NominalBool

func NominalBool() Type

NominalBool is Types::Nominal::Bool (no constraint).

func NominalFloat

func NominalFloat() Type

NominalFloat is Types::Nominal::Float (no constraint).

func NominalHash

func NominalHash() Type

NominalHash is Types::Nominal::Hash (no constraint).

func NominalInteger

func NominalInteger() Type

NominalInteger is Types::Nominal::Integer (no constraint).

func NominalString

func NominalString() Type

NominalString is Types::Nominal::String (no constraint).

func NominalSymbol

func NominalSymbol() Type

NominalSymbol is Types::Nominal::Symbol (no constraint).

func ParamsBool

func ParamsBool() Type

ParamsBool is Types::Params::Bool.

func ParamsDate

func ParamsDate() Type

ParamsDate is Types::Params::Date.

func ParamsDateTime

func ParamsDateTime() Type

ParamsDateTime is Types::Params::DateTime.

func ParamsFloat

func ParamsFloat() Type

ParamsFloat is Types::Params::Float.

func ParamsInteger

func ParamsInteger() Type

ParamsInteger is Types::Params::Integer.

func ParamsNil

func ParamsNil() Type

ParamsNil is Types::Params::Nil ("" → nil).

func ParamsSymbol

func ParamsSymbol() Type

ParamsSymbol is Types::Params::Symbol.

func ParamsTime

func ParamsTime() Type

ParamsTime is Types::Params::Time.

func StrictArray

func StrictArray() Type

StrictArray is Types::Strict::Array.

func StrictBool

func StrictBool() Type

StrictBool is Types::Strict::Bool.

func StrictDate

func StrictDate() Type

StrictDate is Types::Strict::Date.

func StrictDateTime

func StrictDateTime() Type

StrictDateTime is Types::Strict::DateTime.

func StrictFloat

func StrictFloat() Type

StrictFloat is Types::Strict::Float.

func StrictHash

func StrictHash() Type

StrictHash is Types::Strict::Hash.

func StrictInteger

func StrictInteger() Type

StrictInteger is Types::Strict::Integer.

func StrictNil

func StrictNil() Type

StrictNil is Types::Strict::Nil.

func StrictString

func StrictString() Type

StrictString is Types::Strict::String.

func StrictSymbol

func StrictSymbol() Type

StrictSymbol is Types::Strict::Symbol.

func StrictTime

func StrictTime() Type

StrictTime is Types::Strict::Time.

type UnknownKeysError

type UnknownKeysError struct{ Message string }

UnknownKeysError is raised by a strict schema on unexpected keys: `unexpected keys [:a, :b] in Hash input`.

func (*UnknownKeysError) Error

func (e *UnknownKeysError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL