Skip to main content

Validation Showcase

This guide demonstrates how to build reliable forms with Ziva: cover every built-in validator, assemble validation chains, and plug in external checks.

Queue with all built-in validators

func ShowBuiltInValidators() {
    v := ziva.DefaultValidators

    tasks := []ziva.Task{
        ziva.NewInputTask("Name", "Required field:").
            WithValidator(v.Required()),

        ziva.NewInputTask("Username", "3-20 characters, latin/number:").
            WithValidator(v.Username()),

        ziva.NewInputTask("Short description", "Up to 40 characters:").
            WithValidator(v.MaxLength(40)),

        ziva.NewInputTask("Comment", "At least 5 characters:").
            WithValidator(v.MinLength(5)),

        ziva.NewInputTask("Code", "Exactly 8 characters:").
            WithValidator(v.Length(8)),

        ziva.NewInputTask("Email", "Email address:").
            WithInputType(ziva.InputTypeEmail).
            WithValidator(v.Email()),

        ziva.NewInputTask("Website", "URL (optional):").
            WithValidator(v.OptionalURL()),

        ziva.NewInputTask("IP", "IPv4 or IPv6:").
            WithInputType(ziva.InputTypeIP).
            WithValidator(v.IP()),

        ziva.NewInputTask("Domain", "Domain name:").
            WithInputType(ziva.InputTypeDomain).
            WithValidator(v.Domain()),

        ziva.NewInputTask("Port", "Range 1-65535:").
            WithInputType(ziva.InputTypeNumber).
            WithValidator(v.Port()),

        ziva.NewInputTask("Number", "Range 1-100:").
            WithInputType(ziva.InputTypeNumber).
            WithValidator(v.Range(1, 100)),

        ziva.NewInputTask("Alphanumeric", "Letters and digits only:").
            WithValidator(v.AlphaNumeric()),

        ziva.NewInputTask("Password", "Minimum 8 characters:").
            WithInputType(ziva.InputTypePassword).
            WithValidator(v.StandardPassword()),

        ziva.NewInputTask("Strong password", "Minimum 12 characters, mixed set:").
            WithInputType(ziva.InputTypePassword).
            WithValidator(v.StrongPassword()),
    }

    queue := ziva.NewQueue("Validation tour").
        WithTasksNumbered(true, "[%02d]")
    queue.AddTasks(tasks...)
    _ = queue.Run()
}
Each task triggers its validator after the user presses Enter. When validation fails, the TUI shows the error and keeps the task focused until the value matches the rules.

Custom validation chain

type Chain struct {
    validators []ziva.Validator
}

func (c Chain) Validate(input string) error {
    for _, validator := range c.validators {
        if err := validator.Validate(input); err != nil {
            return err
        }
    }
    return nil
}

func (c Chain) Description() string {
    parts := make([]string, len(c.validators))
    for i, validator := range c.validators {
        parts[i] = validator.Description()
    }
    return strings.Join(parts, "; ")
}

func AskSecureToken() {
    v := ziva.DefaultValidators

    token := ziva.NewInputTask(
        "API token",
        "Required, 16-64 characters, alphanumeric",
    ).WithValidator(Chain{
        validators: []ziva.Validator{
            v.Required(),
            v.MinLength(16),
            v.MaxLength(64),
            v.AlphaNumeric(),
        },
    })

    queue := ziva.NewQueue("Token validation")
    queue.AddTasks(token)
    _ = queue.Run()

    fmt.Println("Token accepted:", token.GetValue())
}

External validator example

func emailNotInCRM(email string) bool {
    // Replace with actual API call.
    return !strings.HasSuffix(email, "@banned.example")
}

type UniqueEmail struct{}

func (UniqueEmail) Validate(input string) error {
    if !emailNotInCRM(input) {
        return errors.New("email is already registered")
    }
    return nil
}

func (UniqueEmail) Description() string {
    return "Email must be unique in CRM"
}

func SignUpForm() error {
    v := ziva.DefaultValidators

    email := ziva.NewInputTask("Email", "Enter work email:").
        WithInputType(ziva.InputTypeEmail).
        WithValidator(v.Email())

    password := ziva.NewInputTask(
        "Password",
        "Minimum 12 characters, letters/digits/symbols",
    ).WithInputType(ziva.InputTypePassword).
        WithValidator(v.StrongPassword())

    confirm := ziva.NewYesNoTask(
        "Confirmation",
        "Submit data to CRM?",
    ).WithDefaultYes()

    queue := ziva.NewQueue("User signup")
    queue.AddTasks(email, password, confirm)
    if err := queue.Run(); err != nil {
        return err
    }

    check := ziva.NewInputTask("Uniqueness check", "Email must be unique:").
        WithInputType(ziva.InputTypeEmail).
        WithValidator(UniqueEmail{})

    followUp := ziva.NewQueue("CRM check")
    followUp.AddTasks(check)
    return followUp.Run()
}
Use a FuncTask after collecting input if you need to call an external service and display the outcome (success, warning, retry hints) inside the TUI.

Quick tips

  • Keep Description() human-friendly — Ziva shows it near the input field.
  • Combine WithAllowEmpty(true) with validators that handle empty strings correctly.
  • Break long forms into several queues; it simplifies returning to the problem field.
  • Write unit tests for elaborate validators — they are plain Go code.
Head over to the best practices page to see how these building blocks fit together in real projects.
I