diff --git a/R/standalone-types-check.R b/R/standalone-types-check.R index 38f2cf2bc..79abf8a98 100644 --- a/R/standalone-types-check.R +++ b/R/standalone-types-check.R @@ -1,7 +1,7 @@ # --- # repo: r-lib/rlang # file: standalone-types-check.R -# last-updated: 2023-03-13 +# last-updated: 2025-09-19 # license: https://unlicense.org # dependencies: standalone-obj-type.R # imports: rlang (>= 1.1.0) @@ -11,6 +11,9 @@ # # 2025-09-19: # - `check_logical()` gains an `allow_na` argument (@jonthegeek, #1724) +# - Rename `check_number_decimal()` to `check_number()` (@khusmann, #1714) +# - Add `check_numeric()` and `check_numeric_whole()`, vectorized versions +# of `check_number()` and `check_number_whole()` (@khusmann, #1714) # # 2024-08-15: # - `check_character()` gains an `allow_na` argument (@martaalcalde, #1724) @@ -178,7 +181,7 @@ IS_NUMBER_true <- 0 IS_NUMBER_false <- 1 IS_NUMBER_oob <- 2 -check_number_decimal <- function( +check_number <- function( x, ..., min = NULL, @@ -502,6 +505,78 @@ check_formula <- function( # TODO: Figure out what to do with logical `NA` and `allow_na = TRUE` +check_numeric <- function( + x, + ..., + allow_na = TRUE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is.numeric(x)) { + if (!allow_na && any(is.na(x))) { + abort( + sprintf("`%s` can't contain NA values.", arg), + arg = arg, + call = call + ) + } + + return(invisible(NULL)) + } + + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a numeric vector", + ..., + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_numeric_whole <- function( + x, + ..., + allow_na = TRUE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env() +) { + if (!missing(x)) { + if (is_integerish(x)) { + if (!allow_na && any(is.na(x))) { + abort( + sprintf("`%s` can't contain NA values.", arg), + arg = arg, + call = call + ) + } + + return(invisible(NULL)) + } + + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a numeric vector with whole numbers", + ..., + allow_null = allow_null, + arg = arg, + call = call + ) +} + check_character <- function( x, ..., diff --git a/tests/testthat/_snaps/standalone-types-check.md b/tests/testthat/_snaps/standalone-types-check.md index e180af6b1..a11daf6c4 100644 --- a/tests/testthat/_snaps/standalone-types-check.md +++ b/tests/testthat/_snaps/standalone-types-check.md @@ -244,76 +244,76 @@ Error in `check()`: ! `max` must be a number, not missing. -# `check_number_decimal()` checks +# `check_number()` checks Code - err(checker(, check_number_decimal)) + err(checker(, check_number)) Output Error in `checker()`: ! `foo` must be a number, not absent. Code - err(checker(NA, check_number_decimal)) + err(checker(NA, check_number)) Output Error in `checker()`: ! `foo` must be a number, not `NA`. Code - err(checker(NULL, check_number_decimal)) + err(checker(NULL, check_number)) Output Error in `checker()`: ! `foo` must be a number, not `NULL`. Code - err(checker(int(), check_number_decimal, allow_na = TRUE)) + err(checker(int(), check_number, allow_na = TRUE)) Output Error in `checker()`: ! `foo` must be a number or `NA`, not an empty integer vector. Code - err(checker(na_dbl, check_number_decimal)) + err(checker(na_dbl, check_number)) Output Error in `checker()`: ! `foo` must be a number, not a numeric `NA`. Code - err(checker(na_int, check_number_decimal)) + err(checker(na_int, check_number)) Output Error in `checker()`: ! `foo` must be a number, not an integer `NA`. Code - err(checker(10:11, check_number_decimal, allow_na = TRUE, allow_null = TRUE)) + err(checker(10:11, check_number, allow_na = TRUE, allow_null = TRUE)) Output Error in `checker()`: ! `foo` must be a number, `NA`, or `NULL`, not an integer vector. Code - err(checker(Inf, check_number_decimal, allow_infinite = FALSE)) + err(checker(Inf, check_number, allow_infinite = FALSE)) Output Error in `checker()`: ! `foo` must be a number, not `Inf`. Code - err(checker(-Inf, check_number_decimal, allow_infinite = FALSE)) + err(checker(-Inf, check_number, allow_infinite = FALSE)) Output Error in `checker()`: ! `foo` must be a number, not `-Inf`. Code - err(checker(10, min = NA, check_number_decimal)) + err(checker(10, min = NA, check_number)) Output Error in `check()`: ! `min` must be a single double value. Code - err(checker(10, min = NaN, check_number_decimal)) + err(checker(10, min = NaN, check_number)) Output Error in `check()`: ! `min` must be a number, not missing. Code - err(checker(10, max = NaN, check_number_decimal)) + err(checker(10, max = NaN, check_number)) Output Error in `check()`: @@ -418,6 +418,90 @@ Error in `checker()`: ! `foo` must be an environment or `NULL`, not a list. +# `check_numeric()` checks + + Code + err(checker(, check_numeric)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector, not absent. + Code + err(checker(NULL, check_numeric)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector, not `NULL`. + Code + err(checker(NA, check_numeric)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector, not `NA`. + Code + err(checker("foo", check_numeric)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector, not the string "foo". + Code + err(checker(list(1, 2), check_numeric, allow_null = TRUE)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector or `NULL`, not a list. + Code + err(checker(c(1, NA), check_numeric, allow_na = FALSE)) + Output + + Error in `checker()`: + ! `foo` can't contain NA values. + +# `check_numeric_whole()` checks + + Code + err(checker(, check_numeric_whole)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers, not absent. + Code + err(checker(1.1, check_numeric_whole)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers, not the number 1.1. + Code + err(checker(NULL, check_numeric_whole)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers, not `NULL`. + Code + err(checker(NA, check_numeric_whole)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers, not `NA`. + Code + err(checker("foo", check_numeric_whole)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers, not the string "foo". + Code + err(checker(list(1, 2), check_numeric_whole, allow_null = TRUE)) + Output + + Error in `checker()`: + ! `foo` must be a numeric vector with whole numbers or `NULL`, not a list. + Code + err(checker(c(1, NA), check_numeric_whole, allow_na = FALSE)) + Output + + Error in `checker()`: + ! `foo` can't contain NA values. + # `check_character()` checks Code @@ -505,7 +589,7 @@ Error: ! `factor("a")` must be a whole number, not a object. Code - (expect_error(check_number_decimal(as.Date("2000-01-01")))) + (expect_error(check_number(as.Date("2000-01-01")))) Output Error: diff --git a/tests/testthat/test-standalone-types-check.R b/tests/testthat/test-standalone-types-check.R index 06567a8d8..f1e8931ac 100644 --- a/tests/testthat/test-standalone-types-check.R +++ b/tests/testthat/test-standalone-types-check.R @@ -84,35 +84,35 @@ test_that("`check_number_whole()` checks", { }) }) -test_that("`check_number_decimal()` checks", { - expect_null(check_number_decimal(10)) - expect_null(check_number_decimal(10L)) - expect_null(check_number_decimal(10.5)) - expect_null(check_number_decimal(NA, allow_na = TRUE)) - expect_null(check_number_decimal(na_dbl, allow_na = TRUE)) - expect_null(check_number_decimal(na_int, allow_na = TRUE)) - expect_null(check_number_decimal(NULL, allow_null = TRUE)) - expect_null(check_number_decimal(Inf)) - expect_null(check_number_decimal(-Inf)) +test_that("`check_number()` checks", { + expect_null(check_number(10)) + expect_null(check_number(10L)) + expect_null(check_number(10.5)) + expect_null(check_number(NA, allow_na = TRUE)) + expect_null(check_number(na_dbl, allow_na = TRUE)) + expect_null(check_number(na_int, allow_na = TRUE)) + expect_null(check_number(NULL, allow_null = TRUE)) + expect_null(check_number(Inf)) + expect_null(check_number(-Inf)) expect_snapshot({ - err(checker(, check_number_decimal)) - err(checker(NA, check_number_decimal)) - err(checker(NULL, check_number_decimal)) - err(checker(int(), check_number_decimal, allow_na = TRUE)) - err(checker(na_dbl, check_number_decimal)) - err(checker(na_int, check_number_decimal)) + err(checker(, check_number)) + err(checker(NA, check_number)) + err(checker(NULL, check_number)) + err(checker(int(), check_number, allow_na = TRUE)) + err(checker(na_dbl, check_number)) + err(checker(na_int, check_number)) err(checker( 10:11, - check_number_decimal, + check_number, allow_na = TRUE, allow_null = TRUE )) - err(checker(Inf, check_number_decimal, allow_infinite = FALSE)) - err(checker(-Inf, check_number_decimal, allow_infinite = FALSE)) - err(checker(10, min = NA, check_number_decimal)) - err(checker(10, min = NaN, check_number_decimal)) - err(checker(10, max = NaN, check_number_decimal)) + err(checker(Inf, check_number, allow_infinite = FALSE)) + err(checker(-Inf, check_number, allow_infinite = FALSE)) + err(checker(10, min = NA, check_number)) + err(checker(10, min = NaN, check_number)) + err(checker(10, max = NaN, check_number)) }) }) @@ -155,6 +155,50 @@ test_that("`check_environment()` checks", { }) }) +test_that("`check_numeric()` checks", { + expect_null(check_numeric(0)) + expect_null(check_numeric(0L)) + expect_null(check_numeric(na_dbl)) + expect_null(check_numeric(na_int)) + expect_null(check_numeric(c(1, NA))) + expect_null(check_numeric(double())) + expect_null(check_numeric(10.1)) + expect_null(check_numeric(1:10)) + expect_null(check_numeric(1:10 + 0.1)) + expect_null(check_numeric(NULL, allow_null = TRUE)) + + expect_snapshot({ + err(checker(, check_numeric)) + err(checker(NULL, check_numeric)) + err(checker(NA, check_numeric)) + err(checker("foo", check_numeric)) + err(checker(list(1, 2), check_numeric, allow_null = TRUE)) + err(checker(c(1, NA), check_numeric, allow_na = FALSE)) + }) +}) + +test_that("`check_numeric_whole()` checks", { + expect_null(check_numeric_whole(0)) + expect_null(check_numeric_whole(0L)) + expect_null(check_numeric_whole(na_dbl)) + expect_null(check_numeric_whole(na_int)) + expect_null(check_numeric_whole(c(1, NA))) + expect_null(check_numeric_whole(double())) + expect_null(check_numeric_whole(integer())) + expect_null(check_numeric_whole(1:10)) + expect_null(check_numeric_whole(NULL, allow_null = TRUE)) + + expect_snapshot({ + err(checker(, check_numeric_whole)) + err(checker(1.1, check_numeric_whole)) + err(checker(NULL, check_numeric_whole)) + err(checker(NA, check_numeric_whole)) + err(checker("foo", check_numeric_whole)) + err(checker(list(1, 2), check_numeric_whole, allow_null = TRUE)) + err(checker(c(1, NA), check_numeric_whole, allow_na = FALSE)) + }) +}) + test_that("`check_character()` checks", { expect_null(check_character("")) expect_null(check_character(na_chr)) @@ -195,7 +239,7 @@ test_that("`check_logical()` checks", { test_that("non-numeric types are not numbers", { expect_snapshot({ (expect_error(check_number_whole(factor("a")))) - (expect_error(check_number_decimal(as.Date("2000-01-01")))) + (expect_error(check_number(as.Date("2000-01-01")))) }) })