From 4d1f41b7689c04ef57918778ac94d33d55e0f4cb Mon Sep 17 00:00:00 2001 From: kbvernon Date: Mon, 18 Nov 2024 11:22:01 -0700 Subject: [PATCH] refactor cargo-processx functions --- R/clean.R | 55 ++++++------- R/license_note.R | 167 ++++++++++++++++++++++++++-------------- R/read_cargo_metadata.R | 31 +++++--- R/use_crate.R | 45 ++++++----- 4 files changed, 176 insertions(+), 122 deletions(-) diff --git a/R/clean.R b/R/clean.R index d59c475a..a3e21864 100644 --- a/R/clean.R +++ b/R/clean.R @@ -5,8 +5,19 @@ #' (found by default at `pkg_root/src/rust/target/`). #' Useful when Rust code should be recompiled from scratch. #' @param path \[ string \] Path to the package root. +#' @param echo logical scalar, should cargo command and outputs be printed to +#' console (default is TRUE) +#' #' @export -clean <- function(path = ".") { +#' +#' @examples +#' \dontrun{ +#' clean() +#' } +clean <- function(path = ".", echo = TRUE) { + check_string(path, class = "rextendr_error") + check_bool(echo, class = "rextendr_error") + root <- rprojroot::find_package_root_file(path = path) rust_folder <- normalizePath( @@ -15,7 +26,7 @@ clean <- function(path = ".") { mustWork = FALSE ) - toml_path <- normalizePath( + manifest_path <- normalizePath( file.path(rust_folder, "Cargo.toml"), winslash = "/", mustWork = FALSE @@ -28,7 +39,7 @@ clean <- function(path = ".") { mustWork = FALSE ) - if (!file.exists(toml_path)) { + if (!file.exists(manifest_path)) { cli::cli_abort(c( "Unable to clean binaries.", "!" = "{.file Cargo.toml} not found in {.path {rust_folder}}.", @@ -36,44 +47,26 @@ clean <- function(path = ".") { )) } - cargo_envvars <- get_cargo_envvars() - args <- c( "clean", - glue("--manifest-path={toml_path}"), - glue("--target-dir={target_dir}"), + glue::glue("--manifest-path={manifest_path}"), + glue::glue("--target-dir={target_dir}"), if (tty_has_colors()) { "--color=always" } else { "--color=never" - }, - "--quiet" + } ) - exec_result <- processx::run( + + out <- processx::run( command = "cargo", args = args, - echo_cmd = FALSE, - windows_verbatim_args = FALSE, - stderr = "|", - stdout = "|", - error_on_status = FALSE, - env = cargo_envvars + error_on_status = TRUE, + wd = rust_folder, + echo_cmd = echo, + echo = echo, + env = get_cargo_envvars() ) - if (!isTRUE(exec_result$status == 0)) { - if (!tty_has_colors()) { - err_msg <- cli::ansi_strip(exec_result$stderr) - } else { - err_msg <- exec_result$stderr - } - cli::cli_abort( - c( - "Unable to execute {.code cargo clean}.", - "x" = paste(err_msg, collapse = "\n") - ), - call = caller_env(), - class = "rextendr_error" - ) - } pkgbuild::clean_dll(path = root) } diff --git a/R/license_note.R b/R/license_note.R index d3c19b00..e52da7f5 100644 --- a/R/license_note.R +++ b/R/license_note.R @@ -1,92 +1,141 @@ #' Generate LICENSE.note file. #' -#' LICENSE.note generated by this function contains information about Rust crate dependencies. -#' To use this function, the [cargo-license](https://crates.io/crates/cargo-license) command must be installed. -#' @param force Logical indicating whether to regenerate LICENSE.note if LICENSE.note already exists. -#' @inheritParams register_extendr +#' LICENSE.note generated by this function contains information about Rust crate +#' dependencies. To use this function, the +#' [cargo-license](https://crates.io/crates/cargo-license) command must be +#' installed. +#' +#' @param path character scalar, the R package directory +#' @param echo logical scalar, whether to print cargo command and outputs to the +#' console (default is `TRUE`) +#' @param quiet logical scalar, whether to signal successful writing of +#' LICENSE.note (default is `FALSE`) +#' @param force logical scalar, whether to regenerate LICENSE.note if +#' LICENSE.note already exists (default is `TRUE`) +#' #' @return No return value, called for side effects. +#' #' @export -write_license_note <- function(path = ".", quiet = FALSE, force = TRUE) { +#' +#' @examples +#' \dontrun{ +#' write_license_note() +#' } +write_license_note <- function( + path = ".", + echo = TRUE, + quiet = FALSE, + force = TRUE) { + check_string(path, class = "rextendr_error") + check_bool(echo, class = "rextendr_error") + check_bool(force, class = "rextendr_error") + if (!cargo_command_available(c("license", "--help"))) { cli::cli_abort( c( - "The {.code cargo license} command is required to run the {.fun write_license_note} function.", - "*" = "Please install cargo-license ({.url https://crates.io/crates/cargo-license}) first.", + "The {.code cargo license} command is required \\ + to run the {.fun write_license_note} function.", + "*" = "Please install cargo-license \\ + ({.url https://crates.io/crates/cargo-license}) first.", i = "Run {.code cargo install cargo-license} from your terminal." ), class = "rextendr_error" ) } - manifest_file <- rprojroot::find_package_root_file("src", "rust", "Cargo.toml", path = path) - outfile <- rprojroot::find_package_root_file("LICENSE.note", path = path) + rust_folder <- rprojroot::find_package_root_file( + "src", "rust", + path = path + ) + + manifest_path <- normalizePath( + file.path(rust_folder, "Cargo.toml"), + winslash = "/", + mustWork = FALSE + ) - if (!isTRUE(force) && file.exists(outfile)) { + outfile <- rprojroot::find_package_root_file( + "LICENSE.note", + path = path + ) + + if (isFALSE(force) && file.exists(outfile)) { cli::cli_abort( c( "LICENSE.note already exists.", - "If you want to regenerate LICENSE.note, set `force = TRUE` to {.fun write_license_note}." + "If you want to regenerate LICENSE.note, \\ + set `force = TRUE` to {.fun write_license_note}." ), class = "rextendr_error" ) } - list_license <- processx::run( - "cargo", - c( - "license", - "--authors", - "--json", - "--avoid-build-deps", - "--avoid-dev-deps", - "--manifest-path", manifest_file - ) - )$stdout %>% - jsonlite::parse_json() - - package_names <- processx::run( - "cargo", - c( - "metadata", - "--no-deps", - "--format-version", "1", - "--manifest-path", manifest_file - ) - )$stdout %>% - jsonlite::parse_json() %>% - purrr::pluck("packages") %>% - purrr::map_chr("name") + args <- c( + "license", + "--authors", + "--json", + "--avoid-build-deps", + "--avoid-dev-deps", + "--manifest-path", manifest_path + ) + + out <- processx::run( + command = "cargo", + args = args, + error_on_status = TRUE, + wd = rust_folder, + echo_cmd = echo, + echo = echo, + env = get_cargo_envvars() + ) + + licenses <- jsonlite::parse_json( + out[["stdout"]], + simplifyDataFrame = TRUE + ) + + metadata <- read_cargo_metadata(path = path) + + package_names <- metadata[["packages"]][["dependencies"]][[1]][["name"]] + + licenses <- licenses[licenses[["name"]] %in% package_names, ] .prep_authors <- function(authors, package) { - ifelse(!is.null(authors), authors, paste0(package, " authors")) %>% - stringi::stri_replace_all_regex(r"(\ <.+?>)", "") %>% - stringi::stri_replace_all_regex(r"(\|)", ", ") + authors <- ifelse( + is.na(authors), + paste0(package, " authors"), + authors + ) + + authors <- stringi::stri_replace_all_regex(authors, r"(\ <.+?>)", "") + + stringi::stri_replace_all_regex(authors, r"(\|)", ", ") } separator <- "-------------------------------------------------------------" - note_header <- paste0( - "The binary compiled from the source code of this package contains the following Rust crates:\n", - "\n", - "\n", - separator + note_header <- glue::glue( + " + The binary compiled from the source code of this package \\ + contains the following Rust crates: + + + {separator} + " ) - note_body <- list_license %>% - purrr::discard(function(x) x$name %in% package_names) %>% - purrr::map_chr( - function(x) { - paste0( - "\n", - "Name: ", x$name, "\n", - "Repository: ", x$repository, "\n", - "Authors: ", .prep_authors(x$authors, x$name), "\n", - "License: ", x$license, "\n", - "\n", - separator - ) - } - ) + note_body <- glue::glue_data( + licenses, + " + + Name: {name} + Repository: {repository} + Authors: {.prep_authors(authors, name)} + License: {license} + + {separator} + " + ) write_file( text = c(note_header, note_body), diff --git a/R/read_cargo_metadata.R b/R/read_cargo_metadata.R index 4e314c3d..43817d02 100644 --- a/R/read_cargo_metadata.R +++ b/R/read_cargo_metadata.R @@ -1,6 +1,8 @@ #' Retrieve metadata for packages and workspaces #' #' @param path character scalar, the R package directory +#' @param echo logical scalar, should cargo command and outputs be printed to +#' console (default is TRUE) #' #' @details #' For more details, see @@ -25,22 +27,29 @@ #' read_cargo_metadata() #' } #' -read_cargo_metadata <- function(path = ".") { +read_cargo_metadata <- function(path = ".", echo = TRUE) { check_string(path, class = "rextendr_error") + check_bool(echo, class = "rextendr_error") - root <- rprojroot::find_package_root_file(path = path) - - rust_folder <- normalizePath( - file.path(root, "src", "rust"), - winslash = "/", - mustWork = FALSE + rust_folder <- rprojroot::find_package_root_file( + "src", "rust", + path = path ) + args <- c("metadata", "--format-version=1", "--no-deps") + out <- processx::run( - "cargo", - args = c("metadata", "--format-version=1", "--no-deps"), - wd = rust_folder + command = "cargo", + args = args, + error_on_status = TRUE, + wd = rust_folder, + echo_cmd = echo, + echo = echo, + env = get_cargo_envvars() ) - jsonlite::fromJSON(out[["stdout"]]) + jsonlite::parse_json( + out[["stdout"]], + simplifyDataFrame = TRUE + ) } diff --git a/R/use_crate.R b/R/use_crate.R index 66496a7c..5cf287ea 100644 --- a/R/use_crate.R +++ b/R/use_crate.R @@ -10,6 +10,8 @@ #' @param optional boolean scalar, whether to mark the dependency as optional #' (FALSE by default) #' @param path character scalar, the package directory +#' @param echo logical scalar, should cargo command and outputs be printed to +#' console (default is TRUE) #' #' @details #' For more details regarding these and other options, see the @@ -43,14 +45,15 @@ use_crate <- function( git = NULL, version = NULL, optional = FALSE, - path = ".") { - # check args - check_string(crate) - check_character(features, allow_null = TRUE) - check_string(git, allow_null = TRUE) - check_string(version, allow_null = TRUE) - check_bool(optional) - check_string(path) + path = ".", + echo = TRUE) { + check_string(crate, class = "rextendr_error") + check_character(features, allow_null = TRUE, class = "rextendr_error") + check_string(git, allow_null = TRUE, class = "rextendr_error") + check_string(version, allow_null = TRUE, class = "rextendr_error") + check_bool(optional, class = "rextendr_error") + check_string(path, class = "rextendr_error") + check_bool(echo, class = "rextendr_error") if (!is.null(version) && !is.null(git)) { cli::cli_abort( @@ -80,21 +83,21 @@ use_crate <- function( optional <- NULL } - # get rust directory in project folder - root <- rprojroot::find_package_root_file(path = path) - - rust_folder <- normalizePath( - file.path(root, "src", "rust"), - winslash = "/", - mustWork = FALSE + rust_folder <- rprojroot::find_package_root_file( + "src", "rust", + path = path ) - # run the commmand - processx::run( - "cargo", - c("add", crate, features, git, optional), - echo_cmd = TRUE, - wd = rust_folder + args <- c("add", crate, features, git, optional) + + out <- processx::run( + command = "cargo", + args = args, + error_on_status = TRUE, + wd = rust_folder, + echo_cmd = echo, + echo = echo, + env = get_cargo_envvars() ) invisible()