Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ cran-comments.md
^\.claude$
^design$
^CLAUDE\.md$
^coverage_check\.R$
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ the dosing including dose amount and route.
* 9 volume of distribution at steady state parameters (`vss.*`)
* 13 terminal volume of distribution parameters (`vz.*`)

## Improvements

* Replaced all `stop()` and `warning()` calls with `rlang::abort()` and
`rlang::warn()` respectively, providing structured error and warning classes
for programmatic handling (e.g. `tryCatch(..., pknca_error_* = ...)` and
`withCallingHandlers(..., pknca_warning_* = ...)`).
* Replaced manual scalar, type, and range checks with `checkmate` assertions
(`assert_number()`, `assert_numeric()`, `assert_string()`, `assert_class()`,
etc.) for cleaner and more consistent input validation across all functions.

## Bug Fixes

* `normalize.data.frame()` no longer triggers a dplyr deprecation warning
Expand Down
127 changes: 73 additions & 54 deletions R/001-add.interval.col.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,13 @@ add.interval.col <- function(name,
"individual",
"population")) {
# Check inputs
if (!is.character(name)) {
stop("name must be a character string")
} else if (length(name) != 1) {
stop("name must have length == 1")
}
if (length(FUN) != 1) {
stop("FUN must have length == 1")
} else if (!(is.character(FUN) || is.na(FUN))) {
stop("FUN must be a character string or NA")
}
if (!is.null(depends)) {
if (!is.character(depends)) {
stop("'depends' must be NULL or a character vector")
}
}
checkmate::assert_logical(sparse, any.missing=FALSE, len=1)
checkmate::assert_character(x = name, len = 1, min.chars = 1, any.missing = FALSE)
checkmate::assert_character(x = FUN, len = 1, any.missing = TRUE) # allows NA
checkmate::assert_logical(x = sparse, len = 1, any.missing=FALSE)
checkmate::assert_character(x = pretty_name, len = 1, min.chars = 1, any.missing=FALSE)
checkmate::assert_character(x = desc, len = 1, any.missing=FALSE)
checkmate::assert_character(x = depends, null.ok = TRUE)

unit_type <-
match.arg(
unit_type,
Expand All @@ -123,42 +114,64 @@ add.interval.col <- function(name,
"clearance", "renal_clearance", "renal_clearance_dosenorm"
)
)
stopifnot("pretty_name must be a scalar"=length(pretty_name) == 1)
stopifnot("pretty_name must be a character"=is.character(pretty_name))
stopifnot("pretty_name must not be an empty string"=nchar(pretty_name) > 0)

# Validate datatype
datatype <- match.arg(datatype)
if (!(datatype %in% "interval")) {
stop("Only the 'interval' datatype is currently supported.")
}
if (length(desc) != 1) {
stop("desc must have length == 1")
} else if (!is.character(desc)) {
stop("desc must be a character string")
}
if (!is.list(formalsmap)) {
stop("formalsmap must be a list")
} else if (length(formalsmap) > 0 &&
is.null(names(formalsmap))) {
stop("formalsmap must be a named list")
} else if (length(formalsmap) > 0 &&
is.na(FUN)) {
stop("formalsmap may not be given when FUN is NA.")
} else if (!all(nchar(names(formalsmap)) > 0)) {
stop("All formalsmap elements must be named")
#c("interval", "individual", "population"), # Currently only interval datatype is supported
checkmate::assert_choice(x = datatype, choices = "interval")

# Validate formalsmap
checkmate::assert_list(
x = formalsmap,
names = if (length(formalsmap) > 0) "named" else NULL
)

# Validate formalsmap and function compatibility
if (length(formalsmap) > 0) {
# Ensure FUN exists
if (is.na(FUN)) {
rlang::abort(
message = "`formalsmap` may not be provided when `FUN` is NA",
class = "pknca_error_invalid_formalsmap"
)
}
# Ensure formalsmap names are unique
checkmate::assert_character(x= names(formalsmap), min.chars = 1,
any.missing = FALSE, unique = TRUE)
}

# Ensure that the function exists
if (!is.na(FUN) &&
length(utils::getAnywhere(FUN)$objs) == 0) {
stop("The function named '", FUN, "' is not defined. Please define the function before calling add.interval.col.")
}
if (!is.na(FUN) &&
length(formalsmap) > 0) {
# Ensure that the formalsmap parameters are all in the list of
# formal arguments to the function.
if (!all(names(formalsmap) %in% names(formals(utils::getAnywhere(FUN)$objs[[1]])))) {
stop("All names for the formalsmap list must be arguments to the function.")
if (!is.na(FUN)) {
# Ensure that the function exists
fun_obj <- utils::getAnywhere(FUN)
if (length(fun_obj$objs) == 0) {
rlang::abort(
message = sprintf(
"The function named '%s' is not defined. Please define it before calling add.interval.col().",
FUN
),
class = "pknca_error_fun_not_found"
) }

# Validate formalsmap parameters match function formals
if (length(formalsmap) > 0) {
fun_formals <- names(formals(fun_obj$objs[[1]]))
invalid_formals <- setdiff(names(formalsmap), fun_formals)
if (length(invalid_formals) > 0) {
rlang::abort(
message = sprintf(
"All names in `formalsmap` must be arguments to the function '%s'. Invalid names: %s",
FUN,
paste(dQuote(invalid_formals), collapse = ", ")
),
class = "pknca_error_invalid_formalsmap"
)
}
}

}


current <- get("interval.cols", envir=.PKNCAEnv)
current[[name]] <-
list(
Expand Down Expand Up @@ -190,7 +203,7 @@ sort.interval.cols <- function() {
myorder <- rep(NA, length(current))
names(myorder) <- names(current)
nextnum <- 1
while (any(is.na(myorder))) {
while (anyNA(myorder)) {
for (nextorder in seq_along(myorder)[is.na(myorder)]) {
if (length(current[[nextorder]]$depends) == 0) {
# If it doesn't depend on anything then it can go next in order.
Expand All @@ -201,14 +214,20 @@ sort.interval.cols <- function() {
deps <- unique(unlist(current[[nextorder]]$depends))
missing_deps <- deps[!(deps %in% names(myorder))]
if (length(missing_deps) > 0) {
stop(
"Invalid dependencies for interval column (please report this as a bug): ",
names(myorder)[nextorder],
" The following dependencies are missing: ",
paste(missing_deps, collapse=", ")
rlang::abort(
message = sprintf(
paste0(
"Invalid dependencies for interval column ",
"(please report this as a bug): %s ",
"The following dependencies are missing: %s"
),
names(myorder)[nextorder],
paste(missing_deps, collapse = ", ")
),
class = "pknca_error_invalid_dependency"
)
}
if (!any(is.na(myorder[deps]))) {
if (!anyNA(myorder[deps])) {
myorder[nextorder] <- nextnum
nextnum <- nextnum + 1
}
Expand Down
8 changes: 7 additions & 1 deletion R/AIC.list.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ AIC.list <- function(object, ..., assess.best=TRUE) {
if ("indentation" %in% names(ret)) {
ret$indentation <- ret$indentation + 1
} else {
stop("Unknown way to get a data.frame without indentation set. This is likely a bug.") # nocov
rlang::abort(
message = paste(
"Unknown way to get a data.frame without indentation set.",
"This is likely a bug." # nocov
),
class = "pknca_error_unknown_dataframe_indentation"
)
}
}
}
Expand Down
Loading
Loading