Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Valgopt

Warning: Valgopt is still being tested and is not ready for production use.

Valgopt is the bridge between Valgo and Opt. It provides Valgo-style fluent validators for fields represented by:

  • null.Val[T]: present, but possibly null
  • omit.Val[T]: a value or unset
  • omitnull.Val[T]: a value, explicitly null, or unset

Valgopt preserves the distinction between missing, null, zero, and concrete values while keeping validation in the same valgo.Validation chain as ordinary Go values. It complements Valgo; it does not replace Valgo's validation sessions, error handling, localization, or logical operators.

Installation

go get github.com/cohesivestack/valgopt

Import Valgo and Valgopt together:

import (
	"github.com/cohesivestack/valgo"
	"github.com/cohesivestack/valgopt"
)

Quick start

package main

import (
	"github.com/aarondl/opt/null"
	"github.com/aarondl/opt/omit"
	"github.com/aarondl/opt/omitnull"
	"github.com/cohesivestack/valgo"
	"github.com/cohesivestack/valgopt"
)

type UpdateUser struct {
	Age         null.Val[int]        // must contain a value
	DisplayName omit.Val[string]     // may be omitted
	Level       omitnull.Val[int]    // may be omitted or null
}

func validate(input UpdateUser) *valgo.Validation {
	return valgo.New().
		Is(
			valgopt.NullInt(input.Age, "age").
				GreaterThan(0).
				LessThan(150),

			valgopt.OmitString(input.DisplayName, "display_name").
				Not().Set().
				OrElse().
				LengthBetween(3, 80),

			valgopt.OmitNullInt(input.Level, "level").
				NilOrUnset().
				OrElse().
				Between(1, 10),
		)
}

The three chains intentionally express different state policies:

  • Age must be non-null because ordinary value rules only pass when a concrete value exists.
  • DisplayName may be unset; when set, it must contain 3–80 characters.
  • Level may be null or unset; when it contains a value, it must be between 1 and 10.

Choose a validator family

Every public constructor follows the same pattern:

valgopt.OmitString(value)                        // generated field name
valgopt.OmitString(value, "display_name")        // explicit name; title is humanized
valgopt.OmitString(value, "display_name", "Name") // explicit name and title

Choose the prefix from the Opt wrapper and the suffix from the Go value:

Opt wrapper Constructor prefix State-specific rules
null.Val[T] Null... Nil, plus type-specific ...OrNil rules
omit.Val[T] Omit... Set, plus type-specific ...OrUnset rules
omitnull.Val[T] OmitNull... Set, Nil, NilOrUnset, plus type-specific combined rules
Value kind Constructor suffixes
Any value Any, Typed
Comparable value Comparable
Boolean Boolean
String String
Any numeric type Number
Signed integer Int, Int8, Int16, Int32, Int64
Unsigned integer Uint, Uint8, Uint16, Uint32, Uint64, Byte
Floating point Float32, Float64
Time Time

For example, omitnull.Val[time.Time] uses OmitNullTime, while null.Val[uint16] uses NullUint16.

See the complete interface reference for every constructor and validator method.

State policies

Normal value rules such as EqualTo, Between, or LengthBetween require a concrete value. A null or unset wrapper therefore fails those rules unless the chain explicitly accepts that state.

Use these patterns to make the intended policy visible:

Policy Pattern
Nullable value must be non-null NullString(v).MinLength(1)
Nullable value may be null NullString(v).Nil().OrElse().MinLength(1)
Omittable value must be set OmitString(v).MinLength(1)
Omittable value may be unset OmitString(v).Not().Set().OrElse().MinLength(1)
Omit-null value must contain a value OmitNullString(v).MinLength(1)
Omit-null value is required but may be null OmitNullString(v).Nil().OrElse().MinLength(1)
Omit-null value may be null or unset OmitNullString(v).NilOrUnset().OrElse().MinLength(1)

Set() on an omitnull.Val[T] means “not unset,” so an explicitly null value counts as set. There is no Unset() method; express unset as Not().Set().

Or() builds an OR group. OrElse() also short-circuits the rest of the chain when the left side succeeds, which makes it the right operator for “accept this optional state, otherwise validate the value.”

Available interfaces

All validator families provide:

  • Context() for extending a validator
  • Not() to invert the next rule
  • Or() and OrElse() for logical composition
  • Passing() for a custom predicate that receives the original Opt wrapper
  • EqualTo() and additional type-specific rules where applicable

Type-specific interfaces include:

  • strings: ordering, empty/blank checks, regular expressions, byte length, and rune length
  • numbers: ordering, inclusive ranges, zero checks, sign checks, and slice membership
  • floats: numeric rules plus NaN, Infinite, and Finite
  • booleans: True, False, and optional-state variants
  • time values: After, AfterOrEqualTo, Before, BeforeOrEqualTo, ranges, and zero checks
  • comparable values: equality and slice membership
  • typed or arbitrary values: reflection-aware equality, nil checks where meaningful, and custom predicates

String byte and character rules are deliberately separate:

var text omit.Val[string]
text.Set("虎視眈々") // 12 bytes, 4 runes

validation := valgo.Is(
	valgopt.OmitString(text).
		ByteLength(12).
		Length(4),
)

Use ByteLength, ByteLengthBetween, MinBytes, and MaxBytes for bytes. Use Length, LengthBetween, MinLength, and MaxLength for runes. The older OfByteLength, OfByteLengthBetween, OfLength, and OfLengthBetween names are deprecated.

Custom validation

Passing receives the wrapper rather than only its contained value, so a custom rule can inspect value, null, and unset states:

var code omitnull.Val[string]
code.Set("AB-123")

validation := valgo.Is(
	valgopt.OmitNullString(code, "code").
		Passing(func(value omitnull.Val[string]) bool {
			code, ok := value.Get()
			return ok && len(code) == 6
		}),
)

Prefer built-in rules when one exists because they preserve Valgo's standard error keys and templates.

Agent skill

This repository includes an Agent Skill for implementing and reviewing Valgopt validation. The skill follows the SKILL.md format discovered by the skills CLI:

npx skills add cohesivestack/valgopt --skill valgopt

The skill is self-contained under skills/valgopt. Its detailed interface reference is also the canonical method index linked by this README, avoiding a second duplicated API list.

Compatibility

Valgopt tracks the Valgo API version declared in go.mod. This checkout targets Valgo v0.8.1 and adapts that validator surface to Opt wrappers.

In practice:

  • use Valgopt constructors for Opt-backed fields
  • use Valgo's Validation, Is, Check, localization, logical composition, and error APIs
  • keep Valgopt and Valgo versions aligned when upgrading
  • check go doc github.com/cohesivestack/valgopt for the exact API in the version selected by your module

License

Copyright © 2025 Carlos Forero

Valgopt is released under the MIT License.

About

Valgopt provides a set of validators for the Valgo validation library that work seamlessly with optional value types from the Opt library.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages