| 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.3.0 |
| Built: | 2026-10-13 13:38:28 UTC |
| Source: | https://github.com/gcol33/restrictr |
Marks a validator as accepting NULL: a NULL value passes without running
any step, and every other value is validated by all steps as usual. Use it
for optional arguments such as weights = NULL.
allow_null(restriction)allow_null(restriction)
restriction |
a |
The marker is order-independent: it can sit anywhere in the pipe.
A NULL value returns before the context check, so context a step needs
(e.g. data in require_length_matches(~ nrow(data))) is not required
when the value is NULL. A validator that allows NULL also accepts a
NULL (or absent) element when it is lifted with require_col(),
require_each(), require_fields() or require_valid().
The modified restriction object.
Other composition:
require_any(),
require_col(),
require_each(),
require_fields(),
require_valid()
weights_v <- restrict("weights") |> require_numeric(no_na = TRUE) |> require_positive() |> allow_null() weights_v(NULL) # passes weights_v(c(1, 2)) # passes is_valid(weights_v, -1) # FALSEweights_v <- restrict("weights") |> require_numeric(no_na = TRUE) |> require_positive() |> allow_null() weights_v(NULL) # passes weights_v(c(1, 2)) # passes is_valid(weights_v, -1) # FALSE
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(),
steps(),
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(),
steps(),
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)
testthat expectation that the value violates the validator, optionally with a message matching a regular expression.
expect_invalid(validator, value, ..., regexp = NULL)expect_invalid(validator, value, ..., regexp = NULL)
validator |
a |
value |
the value to validate. |
... |
context arguments passed to the validator (e.g. |
regexp |
optional regular expression the failure messages must match. All messages are joined with newlines before matching. |
Requires the testthat package, which is checked when the expectation runs. A missing context dependency is a usage error and propagates.
value, invisibly.
Other testthat expectations:
expect_valid()
if (requireNamespace("testthat", quietly = TRUE)) { v <- restrict("x") |> require_numeric() expect_invalid(v, "a", regexp = "must be numeric") }if (requireNamespace("testthat", quietly = TRUE)) { v <- restrict("x") |> require_numeric() expect_invalid(v, "a", regexp = "must be numeric") }
testthat expectation that reports the validator's own messages when the
value is invalid, so a failing test shows newdata$x2: must be numeric
rather than a generic "error thrown".
expect_valid(validator, value, ...)expect_valid(validator, value, ...)
validator |
a |
value |
the value to validate. |
... |
context arguments passed to the validator (e.g. |
Requires the testthat package, which is checked when the
expectation runs. Every violation is reported, as with
validation_errors().
value, invisibly.
Other testthat expectations:
expect_invalid()
if (requireNamespace("testthat", quietly = TRUE)) { v <- restrict("x") |> require_numeric() expect_valid(v, 1:3) }if (requireNamespace("testthat", quietly = TRUE)) { v <- restrict("x") |> require_numeric() expect_valid(v, 1:3) }
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(),
steps(),
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(),
steps(),
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
Passes when the value satisfies at least one of the alternative validators. When none passes, the error lists the first failure of every alternative.
require_any(restriction, ..., .label = NULL)require_any(restriction, ..., .label = NULL)
restriction |
a |
... |
|
.label |
optional character(1) description for |
Each alternative runs in fail-first mode. Context needed by any
alternative is required from the caller, like for any other step. Missing
context is a usage error, not an alternative failing. In .on_fail = "all" mode the step reports a single failure.
The modified restriction object.
Other composition:
allow_null(),
require_col(),
require_each(),
require_fields(),
require_valid()
num_or_df <- restrict("x") |> require_any( restrict("x") |> require_numeric(), restrict("x") |> require_df() |> require_has_cols("value") ) num_or_df(1:3) num_or_df(data.frame(value = 1)) try(num_or_df("a"))num_or_df <- restrict("x") |> require_any( restrict("x") |> require_numeric(), restrict("x") |> require_df() |> require_has_cols("value") ) num_or_df(1:3) num_or_df(data.frame(value = 1)) try(num_or_df("a"))
Validates that all elements of a value fall within a specified range. The
value can be numeric, Date, POSIXct, difftime or an ordered factor.
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 |
lower bound (default |
upper |
upper bound (default |
exclusive_lower |
logical; if |
exclusive_upper |
logical; if |
The value and the bounds must be the same kind: a Date bound
against a numeric value fails with a type error. Ordered-factor bounds are
single values of a factor with the same levels as the validated value.
Other 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_contains(),
require_disjoint(),
require_levels(),
require_negative(),
require_one_of(),
require_positive(),
require_set_equal(),
require_unique()
period <- restrict("start") |> require_between(as.Date("2020-01-01"), as.Date("2020-12-31")) period(as.Date("2020-06-15")) try(period(as.Date("2021-01-01")))period <- restrict("start") |> require_between(as.Date("2020-01-01"), as.Date("2020-12-31")) period(as.Date("2020-06-15")) try(period(as.Date("2021-01-01")))
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, matrix, environment, 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 |
A matrix is require_class("matrix") (inherits() is TRUE for
matrices since R 4.0) and an environment is
require_class("environment"); neither needs a dedicated step. Use
require_dim() for the shape.
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("m") |> require_class("matrix") |> require_dim(c(NA, 3))restrict("d") |> require_class("Date") restrict("f") |> require_class("factor") restrict("model") |> require_class("lm") restrict("m") |> require_class("matrix") |> require_dim(c(NA, 3))
Lifts any validator onto one column of a data.frame. Errors keep the
column path (e.g. newdata$age: must be in [0, 120]), so one set of steps
serves both a standalone argument and a column.
require_col(restriction, col, validator)require_col(restriction, col, validator)
restriction |
a |
col |
character(1) column name. |
validator |
a |
The value must be a data.frame containing col, unless
validator allows NULL (see allow_null()), in which case a missing
column passes. Context passed to the outer validator is visible to formula
steps in validator. With .on_fail = "all" each inner failure is
reported on its own.
The modified restriction object.
Other composition:
allow_null(),
require_any(),
require_each(),
require_fields(),
require_valid()
age <- restrict("age") |> require_integer(no_na = TRUE) |> require_between(0, 120) newdata_v <- restrict("newdata") |> require_df() |> require_col("age", age) |> require_col("sex", restrict("sex") |> require_one_of(c("f", "m"))) newdata_v(data.frame(age = 30L, sex = "f")) validation_errors(newdata_v, data.frame(age = 130L, sex = "x"))age <- restrict("age") |> require_integer(no_na = TRUE) |> require_between(0, 120) newdata_v <- restrict("newdata") |> require_df() |> require_col("age", age) |> require_col("sex", restrict("sex") |> require_one_of(c("f", "m"))) newdata_v(data.frame(age = 30L, sex = "f")) validation_errors(newdata_v, data.frame(age = 130L, sex = "x"))
Validates that the value contains every element of values. Extra
elements are allowed.
require_contains(restriction, values)require_contains(restriction, values)
restriction |
a |
values |
vector of values that must be present. |
NA elements of the validated value are ignored. A factor is
compared by its labels. For the opposite direction (every element must be
one of values) use require_one_of().
The modified restriction object.
Other value checks:
require_between(),
require_disjoint(),
require_levels(),
require_negative(),
require_one_of(),
require_positive(),
require_set_equal(),
require_unique()
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(),
steps(),
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 dim(value) of a data.frame, matrix or array. NA entries in
dims match any extent.
require_dim(restriction, dims)require_dim(restriction, dims)
restriction |
a |
dims |
numeric vector of required extents, one per dimension; |
The modified restriction object.
Other structure checks:
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
design <- restrict("X") |> require_class("matrix") |> require_dim(c(NA, 3)) design(matrix(0, 10, 3)) try(design(matrix(0, 10, 2)))design <- restrict("X") |> require_class("matrix") |> require_dim(c(NA, 3)) design(matrix(0, 10, 3)) try(design(matrix(0, 10, 2)))
Validates that every path of a character value exists and is a directory.
require_dir_exists(restriction)require_dir_exists(restriction)
restriction |
a |
Non-character input fails with a type error. NA paths are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other file checks:
require_file_exists(),
require_readable(),
require_writable()
Validates that no element of the value occurs in the vector a formula
evaluates to, for example that train and test ids do not overlap. The
formula is evaluated using only explicitly passed context arguments, plus
.value and .name.
require_disjoint(restriction, formula)require_disjoint(restriction, formula)
restriction |
a |
formula |
a one-sided formula (e.g. |
NA elements are ignored. A factor is compared by its labels.
The modified restriction object.
Other value checks:
require_between(),
require_contains(),
require_levels(),
require_negative(),
require_one_of(),
require_positive(),
require_set_equal(),
require_unique()
train_ids <- restrict("train_ids") |> require_disjoint(~ test_ids) train_ids(1:3, test_ids = 4:6) try(train_ids(1:3, test_ids = 3:6))train_ids <- restrict("train_ids") |> require_disjoint(~ test_ids) train_ids(1:3, test_ids = 4:6) try(train_ids(1:3, test_ids = 3:6))
Applies a validator to each element of a list: list arguments, list-columns,
collected ..., configuration lists. Errors carry the element path,
layers[[2]] for unnamed elements and layers$key for named ones.
require_each(restriction, validator)require_each(restriction, validator)
restriction |
a |
validator |
a |
The value must be a list. An empty list passes. In fail-first mode
the first invalid element stops the check; with .on_fail = "all" every
failing element is reported.
The modified restriction object.
Other composition:
allow_null(),
require_any(),
require_col(),
require_fields(),
require_valid()
layer <- restrict("layer") |> require_class("data.frame") layers_v <- restrict("layers") |> require_class("list") |> require_each(layer) layers_v(list(data.frame(a = 1), data.frame(b = 2))) try(layers_v(list(data.frame(a = 1), "oops")))layer <- restrict("layer") |> require_class("data.frame") layers_v <- restrict("layers") |> require_class("list") |> require_each(layer) layers_v(list(data.frame(a = 1), data.frame(b = 2))) try(layers_v(list(data.frame(a = 1), "oops")))
Applies a different validator to each named element of a list, for
heterogeneous configuration or argument lists. Errors carry the field path,
e.g. opts$alpha.
require_fields(restriction, ..., .required = TRUE)require_fields(restriction, ..., .required = TRUE)
restriction |
a |
... |
named |
.required |
logical; if |
The modified restriction object.
Other composition:
allow_null(),
require_any(),
require_col(),
require_each(),
require_valid()
opts_v <- restrict("opts") |> require_class("list") |> require_fields( alpha = restrict("alpha") |> require_numeric() |> require_between(0, 1), label = restrict("label") |> require_character() ) opts_v(list(alpha = 0.05, label = "a")) validation_errors(opts_v, list(alpha = 2))opts_v <- restrict("opts") |> require_class("list") |> require_fields( alpha = restrict("alpha") |> require_numeric() |> require_between(0, 1), label = restrict("label") |> require_character() ) opts_v(list(alpha = 0.05, label = "a")) validation_errors(opts_v, list(alpha = 2))
Validates that every path of a character value exists and is a file.
Found: shows the normalized path of the first offender, so a relative
path resolved against the wrong working directory is visible.
require_file_exists(restriction, extension = NULL)require_file_exists(restriction, extension = NULL)
restriction |
a |
extension |
optional character vector of accepted extensions, with or without the leading dot, compared case-insensitively. Adds a second step that checks the extension. |
Non-character input fails with a type error. NA paths are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other file checks:
require_dir_exists(),
require_readable(),
require_writable()
csv_in <- restrict("path") |> require_file_exists(extension = "csv") tmp <- tempfile(fileext = ".csv") invisible(file.create(tmp)) csv_in(tmp) try(csv_in(file.path(tempdir(), "missing.csv")))csv_in <- restrict("path") |> require_file_exists(extension = "csv") tmp <- tempfile(fileext = ".csv") invisible(file.create(tmp)) csv_in(tmp) try(csv_in(file.path(tempdir(), "missing.csv")))
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 the value is a function, optionally with named arguments or a call signature. Meant for callback arguments.
require_function(restriction, args = NULL, nargs = NULL)require_function(restriction, args = NULL, nargs = NULL)
restriction |
a |
args |
optional character vector of argument names the function must declare. |
nargs |
optional whole number: the function must be callable with
exactly this many positional arguments, so it declares at least that many
(or |
args looks at the declared formals only: a function declaring
... does not satisfy a named argument.
The modified restriction object.
callback <- restrict("fn") |> require_function(nargs = 2L) callback(function(x, y) x + y) try(callback(function(x) x))callback <- restrict("fn") |> require_function(nargs = 2L) callback(function(x, y) x + y) try(callback(function(x) x))
Validates that a data.frame contains all specified columns. Equivalent to
require_names(cols, mode = "superset") with column wording in the
message.
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_dim(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
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 |
Inf and -Inf are not whole numbers and are rejected in both
modes; NaN counts as NA.
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_dim(),
require_has_cols(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
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_dim(),
require_has_cols(),
require_length(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
Compares levels(value) of a factor with a reference. The classic
predict(newdata) failure, a factor with a level the model has not seen,
is require_levels(levels(train$f), mode = "subset").
require_levels( restriction, levels, mode = c("identical", "subset", "superset") )require_levels( restriction, levels, mode = c("identical", "subset", "superset") )
restriction |
a |
levels |
character vector of reference levels. |
mode |
how the observed levels relate to
|
The levels of the factor are compared, whether or not a level occurs in the data. Non-factor input fails with a type error.
The modified restriction object.
Other value checks:
require_between(),
require_contains(),
require_disjoint(),
require_negative(),
require_one_of(),
require_positive(),
require_set_equal(),
require_unique()
f_v <- restrict("f") |> require_levels(c("a", "b"), mode = "subset") f_v(factor(c("a", "b"))) try(f_v(factor(c("a", "z"))))f_v <- restrict("f") |> require_levels(c("a", "b"), mode = "subset") f_v(factor(c("a", "b"))) try(f_v(factor(c("a", "z"))))
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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
Compares names(value) with a reference. One verb covers "exactly these
names in this order", "only allowed names" and "at least these names".
require_names( restriction, names, mode = c("identical", "subset", "superset", "permutation") )require_names( restriction, names, mode = c("identical", "subset", "superset", "permutation") )
restriction |
a |
names |
character vector of reference names. |
mode |
how the observed names relate to
|
A value without names has no names: it fails every mode except
"subset". The failure lists the missing and the unexpected names.
The modified restriction object.
Other structure checks:
require_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
cfg <- restrict("cfg") |> require_names(c("alpha", "beta"), mode = "subset") cfg(list(alpha = 1)) try(cfg(list(alpha = 1, gamma = 2)))cfg <- restrict("cfg") |> require_names(c("alpha", "beta"), mode = "subset") cfg(list(alpha = 1)) try(cfg(list(alpha = 1, gamma = 2)))
Validates the number of characters of every non-NA element of a
character value.
require_nchar(restriction, min = 0, max = Inf)require_nchar(restriction, min = 0, max = Inf)
restriction |
a |
min |
minimum number of characters (default 0). |
max |
maximum number of characters (default |
Length is counted in characters, not bytes. Non-character input
fails with a type error. NA elements are skipped; chain
require_no_na() to reject them.
The modified restriction object.
Other character checks:
require_nonempty(),
require_pattern()
initials <- restrict("initials") |> require_nchar(min = 2, max = 3) initials(c("AB", "ABC")) try(initials("A"))initials <- restrict("initials") |> require_nchar(min = 2, max = 3) initials(c("AB", "ABC")) try(initials("A"))
Validates that ncol(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_ncol_matches(restriction, formula)require_ncol_matches(restriction, formula)
restriction |
a |
formula |
a one-sided formula (e.g. |
The modified restriction object.
Other structure checks:
require_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
Validates that a data.frame or matrix has at least n columns.
require_ncol_min(restriction, n)require_ncol_min(restriction, n)
restriction |
a |
n |
integer(1) minimum column count. |
The modified restriction object.
Other structure checks:
require_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
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_contains(),
require_disjoint(),
require_levels(),
require_one_of(),
require_positive(),
require_set_equal(),
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 no non-NA element of a character value is empty or made of
whitespace only.
require_nonempty(restriction)require_nonempty(restriction)
restriction |
a |
Non-character input fails with a type error. NA elements are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other character checks:
require_nchar(),
require_pattern()
label <- restrict("label") |> require_nonempty() label(c("a", "b")) try(label(c("a", " ")))label <- restrict("label") |> require_nonempty() label(c("a", "b")) try(label(c("a", " ")))
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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_min(),
require_scalar(),
require_sorted(),
require_unique_names()
Validates that a data.frame or matrix 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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_scalar(),
require_sorted(),
require_unique_names()
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. For
a vector this is the subset test: every element must be one of 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.
Use require_contains() for the reverse direction (the value must hold
all of values) and require_set_equal() for both.
The modified restriction object.
Other value checks:
require_between(),
require_contains(),
require_disjoint(),
require_levels(),
require_negative(),
require_positive(),
require_set_equal(),
require_unique()
Validates that every non-NA element of a character value matches a
regular expression. Found: shows the first element that does not match
and At: lists every offender.
require_pattern(restriction, regex, fixed = FALSE, ignore_case = FALSE)require_pattern(restriction, regex, fixed = FALSE, ignore_case = FALSE)
restriction |
a |
regex |
character(1) regular expression (extended syntax, as in
|
fixed |
logical; if |
ignore_case |
logical; if |
A match anywhere in the string counts; anchor the pattern with ^
and $ to match the whole string. Non-character input fails with a type
error. NA elements are skipped; chain require_no_na() to reject them.
The modified restriction object.
Other character checks:
require_nchar(),
require_nonempty()
code <- restrict("code") |> require_character() |> require_pattern("^[A-Z]{3}$") code(c("ABC", "XYZ")) try(code(c("ABC", "xy")))code <- restrict("code") |> require_character() |> require_pattern("^[A-Z]{3}$") code(c("ABC", "XYZ")) try(code(c("ABC", "xy")))
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_contains(),
require_disjoint(),
require_levels(),
require_negative(),
require_one_of(),
require_set_equal(),
require_unique()
Validates that every path of a character value exists and can be read,
judged by file.access().
require_readable(restriction)require_readable(restriction)
restriction |
a |
Non-character input fails with a type error. NA paths are
skipped; chain require_no_na() to reject them.
The modified restriction object.
Other file checks:
require_dir_exists(),
require_file_exists(),
require_writable()
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_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_sorted(),
require_unique_names()
Validates that the value and values hold the same distinct elements.
Order and duplicates are ignored.
require_set_equal(restriction, values)require_set_equal(restriction, values)
restriction |
a |
values |
vector of values that must be present. |
The modified restriction object.
Other value checks:
require_between(),
require_contains(),
require_disjoint(),
require_levels(),
require_negative(),
require_one_of(),
require_positive(),
require_unique()
Validates that the elements are in order and reports the position of the first element that breaks it.
require_sorted(restriction, decreasing = FALSE, strict = FALSE)require_sorted(restriction, decreasing = FALSE, strict = FALSE)
restriction |
a |
decreasing |
logical; if |
strict |
logical; if |
Works on numeric, character, logical, Date, POSIXct,
difftime and ordered-factor values; other input fails with a type
error. Character values compare in the collation order of the session
locale. NA elements are skipped; chain require_no_na() to reject
them.
The modified restriction object.
Other structure checks:
require_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_unique_names()
ts_v <- restrict("time") |> require_sorted(strict = TRUE) ts_v(c(1, 2, 5)) try(ts_v(c(1, 3, 2)))ts_v <- restrict("time") |> require_sorted(strict = TRUE) ts_v(c(1, 2, 5)) try(ts_v(c(1, 3, 2)))
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_contains(),
require_disjoint(),
require_levels(),
require_negative(),
require_one_of(),
require_positive(),
require_set_equal()
Validates that no name occurs twice. Unnamed elements are ignored; use
require_named() to reject them.
require_unique_names(restriction)require_unique_names(restriction)
restriction |
a |
The modified restriction object.
Other structure checks:
require_dim(),
require_has_cols(),
require_length(),
require_length_matches(),
require_length_max(),
require_length_min(),
require_named(),
require_names(),
require_ncol_matches(),
require_ncol_min(),
require_nrow_matches(),
require_nrow_min(),
require_scalar(),
require_sorted()
Splices the steps of validator into restriction, so two independently
defined validators combine into one. Splicing happens when the validator is
built: print() shows one flat list of steps and there is no nesting at
run time.
require_valid(restriction, validator)require_valid(restriction, validator)
restriction |
a |
validator |
a |
If validator allows NULL (see allow_null()), its steps are
skipped for a NULL value and the combined validator does not itself
become NULL-tolerant.
The modified restriction object.
Other composition:
allow_null(),
require_any(),
require_col(),
require_each(),
require_fields()
id_v <- restrict("id") |> require_integer(no_na = TRUE) pos_v <- restrict("x") |> require_positive(strict = TRUE) restrict("id") |> require_valid(id_v) |> require_valid(pos_v)id_v <- restrict("id") |> require_integer(no_na = TRUE) pos_v <- restrict("x") |> require_positive(strict = TRUE) restrict("id") |> require_valid(id_v) |> require_valid(pos_v)
Validates that every path of a character value can be written: an existing file or directory must be writable, and a path that does not exist yet needs a writable parent directory. Use it for output files and directories.
require_writable(restriction)require_writable(restriction)
restriction |
a |
Writability is judged by file.access(). On Windows it reflects
the read-only attribute and not access control lists, so a directory
that is denied by permissions can still pass. The check cannot tell
whether the disk has room.
The modified restriction object.
Other file checks:
require_dir_exists(),
require_file_exists(),
require_readable()
out_dir <- restrict("out") |> require_writable() out_dir(tempdir()) out_dir(file.path(tempdir(), "result.csv"))out_dir <- restrict("out") |> require_writable() out_dir(tempdir()) out_dir(file.path(tempdir(), "result.csv"))
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")
In "all" mode a type or structure guard (wrong type, not a data.frame,
missing column) is reported once per path: later steps that fail the same
guard on the same path are not repeated. Independent value failures are all
reported.
For a non-throwing result, use is_valid() or validation_errors().
Other core:
as_contract_block(),
as_contract_text(),
fail(),
is_valid(),
require_custom(),
steps(),
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)
Returns the steps of a validator as a data.frame, one row per step in the
order they run, for introspection and tooling. It shows the same steps as
print().
steps(x)steps(x)
x |
a |
A data.frame with the columns step (position), label (the
description shown by print()), deps (list column: context names the
step needs) and fields (list column: the parameters the step was built
with, NULL when it has none).
Other core:
as_contract_block(),
as_contract_text(),
fail(),
is_valid(),
require_custom(),
restrict(),
validation_errors()
v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_between(0, 1) steps(v) steps(v)$fields[[2]]v <- restrict("x") |> require_numeric(no_na = TRUE) |> require_between(0, 1) steps(v) steps(v)$fields[[2]]
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(),
steps()
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