| Title: | Composable Runtime Contracts |
|---|---|
| Description: | Build reusable validators from small building blocks using the base pipe operator. Define runtime contracts once with restrict() and enforce them anywhere in code. Validators compose naturally, support dependent rules via formulas, and produce clear, path-aware error messages. No domain-specific language, no operator overloading, just idiomatic R. |
| Authors: | Gilles Colling [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-3070-6066>) |
| Maintainer: | Gilles Colling <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0 |
| Built: | 2026-06-29 17:40:05 UTC |
| Source: | https://github.com/gcol33/restrictr |
Produces a multi-line text summary suitable for roxygen @details
documentation. Each step appears on its own line as a bullet point.
as_contract_block(x)as_contract_block(x)
x |
a |
A character(1) string with one step per line.
Other core:
as_contract_text(),
fail(),
is_valid(),
require_custom(),
restrict(),
validation_errors()
v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_length(1L) as_contract_block(v)v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_length(1L) as_contract_block(v)
Produces a single-line text summary suitable for roxygen @param
documentation. Use with inline R code in roxygen: `r as_contract_text(validator)`.
as_contract_text(x)as_contract_text(x)
x |
a |
A character(1) string describing the validation contract.
Other core:
as_contract_block(),
fail(),
is_valid(),
require_custom(),
restrict(),
validation_errors()
v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_length(1L) as_contract_text(v)v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_length(1L) as_contract_text(v)
Produces a consistently formatted error message and stops execution.
Intended for use inside custom validation steps created with
require_custom(), so they produce the same structured errors as built-in
steps.
fail(path, message, found = NULL, at = NULL)fail(path, message, found = NULL, at = NULL)
path |
the full path (e.g. |
message |
the specific failure message. |
found |
optional value to show on a |
at |
optional integer positions to show on an |
Format: path: message, with optional Found: and At: lines.
No return value. Called for its side effect: it signals a classed
restrictR_failure condition (an error) carrying path, message, and
the optional found and at details.
Other core:
as_contract_block(),
as_contract_text(),
is_valid(),
require_custom(),
restrict(),
validation_errors()
# fail() signals an error; wrap in try() to show the formatted message try(fail("x", "must be positive", found = -3, at = 2L))# fail() signals an error; wrap in try() to show the formatted message try(fail("x", "must be positive", found = -3, at = 2L))
Non-throwing predicate: returns TRUE when value passes every step of
validator, FALSE otherwise. Use it to branch on validity instead of
wrapping a validator call in tryCatch().
is_valid(validator, value, ...)is_valid(validator, value, ...)
validator |
a |
value |
the value to validate. |
... |
context arguments passed to the validator (e.g. |
Like validation_errors(), only validation failures are caught;
a missing context dependency still raises so a calling mistake is not
mistaken for invalid data.
A length-1 logical.
validation_errors() for the failure messages.
Other core:
as_contract_block(),
as_contract_text(),
fail(),
require_custom(),
restrict(),
validation_errors()
v <- restrict("x") |> require_numeric(no_na = TRUE) is_valid(v, 1:5) # TRUE is_valid(v, c(1, NA)) # FALSEv <- restrict("x") |> require_numeric(no_na = TRUE) is_valid(v, 1:5) # TRUE is_valid(v, c(1, NA)) # FALSE
Validates that all elements of a numeric value fall within a specified range.
require_between( restriction, lower = -Inf, upper = Inf, exclusive_lower = FALSE, exclusive_upper = FALSE )require_between( restriction, lower = -Inf, upper = Inf, exclusive_lower = FALSE, exclusive_upper = FALSE )
restriction |
a |
lower |
numeric(1) lower bound (default |
upper |
numeric(1) upper bound (default |
exclusive_lower |
logical; if |
exclusive_upper |
logical; if |
Non-numeric input fails with a type error. NA elements are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other value checks:
require_negative(),
require_one_of(),
require_positive(),
require_unique()
Validates that the value is character. Optionally checks for NA values.
require_character(restriction, no_na = FALSE)require_character(restriction, no_na = FALSE)
restriction |
a |
no_na |
logical; if |
The modified restriction object.
Other type checks:
require_class(),
require_df(),
require_integer(),
require_logical(),
require_numeric()
Validates that the value belongs to a given class. One verb covers the
types without a dedicated check, including factor, Date, POSIXct,
list, and fitted-model objects such as lm.
require_class(restriction, class, exact = FALSE)require_class(restriction, class, exact = FALSE)
restriction |
a |
class |
character(1) class name to require. |
exact |
logical; if |
The modified restriction object.
Other type checks:
require_character(),
require_df(),
require_integer(),
require_logical(),
require_numeric()
restrict("d") |> require_class("Date") restrict("f") |> require_class("factor") restrict("model") |> require_class("lm")restrict("d") |> require_class("Date") restrict("f") |> require_class("factor") restrict("model") |> require_class("lm")
Validates that all values in a column fall within a specified range.
require_col_between( restriction, col, lower = -Inf, upper = Inf, exclusive_lower = FALSE, exclusive_upper = FALSE )require_col_between( restriction, col, lower = -Inf, upper = Inf, exclusive_lower = FALSE, exclusive_upper = FALSE )
restriction |
a |
col |
character(1) column name. |
lower |
numeric(1) lower bound (default |
upper |
numeric(1) upper bound (default |
exclusive_lower |
logical; if |
exclusive_upper |
logical; if |
A non-numeric column fails with a type error. NA elements are
skipped; use require_col_numeric(col, no_na = TRUE) to reject them.
The modified restriction object.
Other column checks:
require_col_character(),
require_col_numeric(),
require_col_one_of()
Validates that a specific column in a data.frame is character. Produces path-aware error messages.
require_col_character(restriction, col, no_na = FALSE)require_col_character(restriction, col, no_na = FALSE)
restriction |
a |
col |
character(1) column name. |
no_na |
logical; if |
The modified restriction object.
Other column checks:
require_col_between(),
require_col_numeric(),
require_col_one_of()
Validates that a specific column in a data.frame is numeric. Produces
path-aware error messages (e.g. newdata$x2: must be numeric).
require_col_numeric(restriction, col, no_na = FALSE, finite = FALSE)require_col_numeric(restriction, col, no_na = FALSE, finite = FALSE)
restriction |
a |
col |
character(1) column name. |
no_na |
logical; if |
finite |
logical; if |
The modified restriction object.
Other column checks:
require_col_between(),
require_col_character(),
require_col_one_of()
Validates that all values in a column are among the allowed values.
require_col_one_of(restriction, col, values)require_col_one_of(restriction, col, values)
restriction |
a |
col |
character(1) column name. |
values |
vector of allowed values. |
NA elements are skipped; use require_col_character(col, no_na = TRUE) (or the matching column type check) to reject them.
The modified restriction object.
Other column checks:
require_col_between(),
require_col_character(),
require_col_numeric()
Allows advanced users to define their own validation step without
growing the package's built-in API surface. The step function receives
(value, name, ctx) and should call fail() on validation failure.
require_custom(restriction, label, fn, deps = character(0L))require_custom(restriction, label, fn, deps = character(0L))
restriction |
a |
label |
character(1) human-readable description for printing. |
fn |
a function with signature |
deps |
character vector of context names this step requires (default: none). |
A new restriction object with the custom step appended.
Other core:
as_contract_block(),
as_contract_text(),
fail(),
is_valid(),
restrict(),
validation_errors()
# Custom step: require all values to be unique require_unique_id <- restrict("id") |> require_custom( label = "must contain unique values", fn = function(value, name, ctx) { dupes <- which(duplicated(value)) if (length(dupes) > 0L) { fail(name, "contains duplicates", at = dupes) } } )# Custom step: require all values to be unique require_unique_id <- restrict("id") |> require_custom( label = "must contain unique values", fn = function(value, name, ctx) { dupes <- which(duplicated(value)) if (length(dupes) > 0L) { fail(name, "contains duplicates", at = dupes) } } )
Validates that the value is a data.frame.
require_df(restriction)require_df(restriction)
restriction |
a |
The modified restriction object.
Other type checks:
require_character(),
require_class(),
require_integer(),
require_logical(),
require_numeric()
Validates that a numeric value contains no Inf, -Inf, or NaN values.
Does not check for NA (use require_no_na() for that).
require_finite(restriction)require_finite(restriction)
restriction |
a |
The modified restriction object.
Other missingness checks:
require_no_na(),
require_not_null()
Validates that a data.frame contains all specified columns.
require_has_cols(restriction, cols)require_has_cols(restriction, cols)
restriction |
a |
cols |
character vector of required column names. |
The modified restriction object.
Other structure checks:
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that the value contains whole numbers. By default accepts both
integer and numeric types as long as all values are whole
(x == floor(x)). Set strict = TRUE to require the R integer type.
require_integer(restriction, no_na = FALSE, strict = FALSE)require_integer(restriction, no_na = FALSE, strict = FALSE)
restriction |
a |
no_na |
logical; if |
strict |
logical; if |
The modified restriction object.
Other type checks:
require_character(),
require_class(),
require_df(),
require_logical(),
require_numeric()
Validates that the value has exact length n.
require_length(restriction, n)require_length(restriction, n)
restriction |
a |
n |
integer(1) required length. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that length(value) equals the result of evaluating a formula.
The formula is evaluated using only explicitly passed context arguments,
plus .value (the validated value) and .name (the restriction name).
require_length_matches(restriction, formula)require_length_matches(restriction, formula)
restriction |
a |
formula |
a one-sided formula (e.g. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that the value has at most length n.
require_length_max(restriction, n)require_length_max(restriction, n)
restriction |
a |
n |
integer(1) maximum length. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that the value has at least length n.
require_length_min(restriction, n)require_length_min(restriction, n)
restriction |
a |
n |
integer(1) minimum length. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_named(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that the value is logical. Optionally checks for NA values.
require_logical(restriction, no_na = FALSE)require_logical(restriction, no_na = FALSE)
restriction |
a |
no_na |
logical; if |
The modified restriction object.
Other type checks:
require_character(),
require_class(),
require_df(),
require_integer(),
require_numeric()
Validates that the value is fully named: every element has a non-empty,
non-NA name. A partially-named value (e.g. c(a = 1, 2)) fails and the
positions of the unnamed elements are reported.
require_named(restriction)require_named(restriction)
restriction |
a |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar()
Validates that all elements are negative. By default uses <= 0
(non-positive); set strict = TRUE for < 0.
require_negative(restriction, strict = FALSE)require_negative(restriction, strict = FALSE)
restriction |
a |
strict |
logical; if |
Non-numeric input fails with a type error. NA elements are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other value checks:
require_between(),
require_one_of(),
require_positive(),
require_unique()
Validates that the value contains no NA values. Works on any atomic type.
require_no_na(restriction)require_no_na(restriction)
restriction |
a |
The modified restriction object.
Other missingness checks:
require_finite(),
require_not_null()
Validates that the value is not NULL. Place this step first in the
pipeline when NULL is a possible input.
require_not_null(restriction)require_not_null(restriction)
restriction |
a |
The modified restriction object.
Other missingness checks:
require_finite(),
require_no_na()
Validates that nrow(value) equals the result of evaluating a formula.
The formula is evaluated using only explicitly passed context arguments,
plus .value (the validated value) and .name (the restriction name).
require_nrow_matches(restriction, formula)require_nrow_matches(restriction, formula)
restriction |
a |
formula |
a one-sided formula (e.g. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_min(),
require_scalar()
Validates that a data.frame has at least n rows.
require_nrow_min(restriction, n)require_nrow_min(restriction, n)
restriction |
a |
n |
integer(1) minimum row count. |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_scalar()
Validates that the value is numeric. Optionally checks for NA and non-finite values.
require_numeric(restriction, no_na = FALSE, finite = FALSE)require_numeric(restriction, no_na = FALSE, finite = FALSE)
restriction |
a |
no_na |
logical; if |
finite |
logical; if |
The modified restriction object.
Other type checks:
require_character(),
require_class(),
require_df(),
require_integer(),
require_logical()
Validates that all elements of the value are among the allowed values.
require_one_of(restriction, values)require_one_of(restriction, values)
restriction |
a |
values |
vector of allowed values. |
NA elements are skipped; chain require_no_na() to reject them.
The modified restriction object.
Other value checks:
require_between(),
require_negative(),
require_positive(),
require_unique()
Validates that all elements are positive. By default uses >= 0
(non-negative); set strict = TRUE for > 0.
require_positive(restriction, strict = FALSE)require_positive(restriction, strict = FALSE)
restriction |
a |
strict |
logical; if |
Non-numeric input fails with a type error. NA elements are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other value checks:
require_between(),
require_negative(),
require_one_of(),
require_unique()
Validates that the value has length 1. Rejects NULL, zero-length vectors,
and vectors with more than one element.
require_scalar(restriction)require_scalar(restriction)
restriction |
a |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_nrow_matches(),
require_nrow_min()
Validates that the value contains no duplicates. Reports the positions of duplicated elements.
require_unique(restriction)require_unique(restriction)
restriction |
a |
The modified restriction object.
Other value checks:
require_between(),
require_negative(),
require_one_of(),
require_positive()
Creates a callable validation object that accumulates checks via the base
pipe operator |>. The resulting object behaves like a function: call it
with a value to validate.
restrict(name)restrict(name)
name |
character(1) name used in error messages (e.g. |
A restriction object (callable function) with no validation steps.
Validators accept value as the first argument, plus context via named
arguments in ... or as a named list in .ctx:
require_pred(out, newdata = df) require_pred(out, .ctx = list(newdata = df))
Named arguments in ... take precedence over .ctx entries with the
same name. If a step declares dependencies (e.g. require_length_matches(~ nrow(newdata))), the validator checks that all required context is present
before running any steps and errors early if not.
By default a validator is fail-fast: the first failing step stops with its
error. Pass .on_fail = "all" to run every step and report all violations
in one aggregated error:
require_pred(out, .on_fail = "all")
For a non-throwing result, use is_valid() or validation_errors().
Other core:
as_contract_block(),
as_contract_text(),
fail(),
is_valid(),
require_custom(),
validation_errors()
# Define a validator require_positive <- restrict("x") |> require_numeric(no_na = TRUE) |> require_between(lower = 0, exclusive_lower = TRUE) # Use it require_positive(5) # passes silently # Compose with pipe require_score <- restrict("score") |> require_numeric() |> require_length(1L) |> require_between(lower = 0, upper = 100)# Define a validator require_positive <- restrict("x") |> require_numeric(no_na = TRUE) |> require_between(lower = 0, exclusive_lower = TRUE) # Use it require_positive(5) # passes silently # Compose with pipe require_score <- restrict("score") |> require_numeric() |> require_length(1L) |> require_between(lower = 0, upper = 100)
Runs a validator against a value and returns the validation messages
instead of raising an error. Every step is checked (as with
.on_fail = "all"), so all violations are reported in one pass.
validation_errors(validator, value, ...)validation_errors(validator, value, ...)
validator |
a |
value |
the value to validate. |
... |
context arguments passed to the validator (e.g. |
Only validation failures are caught. A usage error, such as a missing context dependency declared by a formula step, still propagates so the calling mistake is not silently reported as invalid data.
A character vector of failure messages, or character(0) if the
value satisfies every step. Each element is the full path-aware message
for one failing step.
is_valid() for a logical predicate.
Other core:
as_contract_block(),
as_contract_text(),
fail(),
is_valid(),
require_custom(),
restrict()
v <- restrict("x") |> require_numeric() |> require_positive() validation_errors(v, 5) # character(0) validation_errors(v, c(-1)) # one messagev <- restrict("x") |> require_numeric() |> require_positive() validation_errors(v, 5) # character(0) validation_errors(v, c(-1)) # one message