|
| 1 | +#' Create a file mapping for multi-repository deployment |
| 2 | +#' |
| 3 | +#' @param ... Named arguments where names are local file paths and values are repository paths |
| 4 | +#' @param dir Character string specifying a directory to search for files. Default is NULL. |
| 5 | +#' @param pattern Character string with a regular expression pattern to match files in dir. Default is NULL. |
| 6 | +#' @param target_prefix Character string to prefix to all target paths. Default is "". |
| 7 | +#' @param preserve_structure Logical indicating whether to preserve directory structure in target. Default is FALSE. |
| 8 | +#' |
| 9 | +#' @return A named list where names are local file paths and values are repository paths |
| 10 | +#' @export |
| 11 | +#' |
| 12 | +#' @examples |
| 13 | +#' \dontrun{ |
| 14 | +#' # Individual files |
| 15 | +#' mapping <- file_mapping( |
| 16 | +#' "local/path/ci.yml" = ".github/workflows/ci.yml", |
| 17 | +#' "local/path/lint.R" = ".lintr" |
| 18 | +#' ) |
| 19 | +#' |
| 20 | +#' # All yaml files from a directory to .github/workflows |
| 21 | +#' mapping <- file_mapping( |
| 22 | +#' dir = "local/workflows", |
| 23 | +#' pattern = "\\.ya?ml$", |
| 24 | +#' target_prefix = ".github/workflows/" |
| 25 | +#' ) |
| 26 | +#' |
| 27 | +#' # Preserve directory structure |
| 28 | +#' mapping <- file_mapping( |
| 29 | +#' dir = "templates", |
| 30 | +#' preserve_structure = TRUE |
| 31 | +#' ) |
| 32 | +#' } |
| 33 | +file_mapping <- function(..., dir = NULL, pattern = NULL, |
| 34 | + target_prefix = "", preserve_structure = FALSE, quiet = FALSE) { |
| 35 | + mapping <- list(...) |
| 36 | + |
| 37 | + # Process files from directory if specified |
| 38 | + if (!is.null(dir)) { |
| 39 | + if (!dir.exists(dir)) { |
| 40 | + cli::cli_abort("Directory does not exist: {.file {dir}}") |
| 41 | + } |
| 42 | + |
| 43 | + # Get all files in directory |
| 44 | + files <- list.files(dir, pattern = pattern, recursive = TRUE, full.names = TRUE) |
| 45 | + |
| 46 | + if (length(files) == 0) { |
| 47 | + cli::cli_alert_warning("No files found in directory {.file {dir}}") |
| 48 | + return(mapping) |
| 49 | + } |
| 50 | + |
| 51 | + for (file in files) { |
| 52 | + if (preserve_structure) { |
| 53 | + # Preserve directory structure |
| 54 | + rel_path <- sub(paste0("^", dir, "/"), "", file) |
| 55 | + mapping[[file]] <- paste0(target_prefix, rel_path) |
| 56 | + } else { |
| 57 | + # Flatten structure |
| 58 | + file_name <- basename(file) |
| 59 | + mapping[[file]] <- paste0(target_prefix, file_name) |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + # Validate mapping |
| 65 | + for (local_file in names(mapping)) { |
| 66 | + if (!file.exists(local_file)) { |
| 67 | + cli::cli_alert_warning("Local file does not exist: {.file {local_file}}") |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + cli::cli_alert_info("Created file mapping with {length(mapping)} files") |
| 72 | + |
| 73 | + # Assign class to enable S3 method dispatch |
| 74 | + class(mapping) <- c("file_mapping", "list") |
| 75 | + |
| 76 | + return(mapping) |
| 77 | +} |
| 78 | + |
| 79 | + |
| 80 | +#' Print method for file_mapping objects |
| 81 | +#' |
| 82 | +#' @param x A file_mapping object to print |
| 83 | +#' @param max_files Maximum number of files to display. Default is 20. |
| 84 | +#' @param ... Additional arguments passed to print methods (not used) |
| 85 | +#' |
| 86 | +#' @return Invisibly returns the file_mapping object |
| 87 | +#' @export |
| 88 | +print.file_mapping <- function(x, max_files = 20, ...) { |
| 89 | + n_files <- length(x) |
| 90 | + |
| 91 | + # Print header |
| 92 | + cli::cli_h2("File Mapping") |
| 93 | + cli::cli_text("{n_files} file{?s} mapped for deployment") |
| 94 | + |
| 95 | + # Print each mapping entry |
| 96 | + if (n_files > 0) { |
| 97 | + display_count <- min(n_files, max_files) |
| 98 | + |
| 99 | + # Add newline before the list |
| 100 | + cli::cli_text("") |
| 101 | + |
| 102 | + # Create a bullet list of mappings |
| 103 | + cli::cli_ul() |
| 104 | + |
| 105 | + for (i in seq_len(display_count)) { |
| 106 | + local_file <- names(x)[i] |
| 107 | + repo_path <- x[[i]] |
| 108 | + |
| 109 | + # Check if local file exists |
| 110 | + file_exists <- file.exists(local_file) |
| 111 | + |
| 112 | + # Use different styling based on file existence |
| 113 | + if (file_exists) { |
| 114 | + status <- cli::col_green(cli::symbol$tick) |
| 115 | + } else { |
| 116 | + status <- cli::col_red(cli::symbol$cross) |
| 117 | + } |
| 118 | + |
| 119 | + # Use bullets for each mapping with existence indicator |
| 120 | + cli::cli_li("{status} {.file {local_file}} {cli::symbol$arrow_right} {.path {repo_path}}") |
| 121 | + } |
| 122 | + |
| 123 | + # End bullet list |
| 124 | + cli::cli_end() |
| 125 | + |
| 126 | + # Add newline after the list |
| 127 | + cli::cli_text("") |
| 128 | + |
| 129 | + # Add note explaining symbols at the end |
| 130 | + cli::cli_text("Note: {cli::col_green(cli::symbol$tick)} indicates file exists, {cli::col_red(cli::symbol$cross)} indicates file not found") |
| 131 | + |
| 132 | + # If we didn't show all files, indicate how many more there are |
| 133 | + if (n_files > max_files) { |
| 134 | + remaining <- n_files - max_files |
| 135 | + cli::cli_alert_info("... and {remaining} more file{?s} not shown") |
| 136 | + } |
| 137 | + } else { |
| 138 | + cli::cli_alert_warning("No files in mapping") |
| 139 | + } |
| 140 | + |
| 141 | + # Return invisibly |
| 142 | + invisible(x) |
| 143 | +} |
0 commit comments