R/rextendr_document.R

Defines functions check_namespace_file check_if_dyn_lib_used check_if_roxygen_used document

Documented in document

#' Compile Rust code and generate package documentation.
#'
#' The function `rextendr::document()` updates the package documentation for an
#' R package that uses `extendr` code, taking into account any changes that were
#' made in the Rust code. It is a wrapper for [devtools::document()], and it
#' executes `extendr`-specific routines before calling [devtools::document()].
#' Specifically, it ensures that Rust code is recompiled (when necessary) and that
#' up-to-date R wrappers are generated before regenerating the package documentation.
#' @inheritParams devtools::document
#' @return No return value, called for side effects.
#' @export
document <- function(pkg = ".", quiet = FALSE, roclets = NULL) {
  try_save_all(quiet = quiet)

  withr::local_envvar(devtools::r_env_vars())

  register_extendr(path = pkg, quiet = quiet)

  rlang::check_installed("devtools")
  devtools::document(pkg = pkg, roclets = roclets, quiet = quiet)
  if (!isTRUE(quiet)) {
    check_namespace_file(pkg)
  }
}

check_if_roxygen_used <- function(namespace_content) {
  any(stringi::stri_startswith_fixed(namespace_content, "# Generated by roxygen2:"))
}

check_if_dyn_lib_used <- function(namespace_content, pkg_name) {
  expected_pattern <- glue::glue("useDynLib\\({pkg_name},\\s*\\.registration = TRUE\\)")

  any(stringi::stri_detect_regex(namespace_content, expected_pattern))
}

check_namespace_file <- function(path = ".") {
  namespace_file_path <- rprojroot::find_package_root_file("NAMESPACE", path = path)
  description_file_path <- rprojroot::find_package_root_file("DESCRIPTION", path = path)
  package_name <- desc::desc_get_field("Package", file = description_file_path)
  namespace_content <- brio::read_lines(namespace_file_path)
  namespace_content <- stringi::stri_trim_both(namespace_content[nzchar(namespace_content)])

  is_roxygen_used <- check_if_roxygen_used(namespace_content)
  is_dyn_lib_used <- check_if_dyn_lib_used(namespace_content, package_name)

  if (!is_dyn_lib_used) {
    roxygen_message <- ifelse(is_roxygen_used, NULL,
      paste(
        "Alternatively, allow {.pkg roxygen2} to generate {.file NAMESPACE} exports for you.",
        "Annotate exported functions with {.code # @export} directive,",
        "delete the {.file NAMESPACE} file and run {.code rextendr::document()} again to regenerate exports."
      )
    )

    use_dyn_lib_ref <- glue::glue("useDynLib({package_name}, .registration = TRUE)") # nolint: object_usage_linter.

    cli::cli_warn(
      c(
        "The {.file NAMESPACE} file does not contain the expected {.code useDynLib} directive.",
        "x" = "This prevents your package from loading Rust code.",
        "*" = "Add the following line to the {.file NAMESPACE} file: {.code {use_dyn_lib_ref}}",
        "*" = roxygen_message
      )
    )
  }
}
extendr/rextendr documentation built on April 4, 2024, 3:03 a.m.