Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelTurnbach committed Feb 24, 2021
0 parents commit 1206e4b
Show file tree
Hide file tree
Showing 143 changed files with 12,828 additions and 0 deletions.
31 changes: 31 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Package: pkglite
Title: Compact Package Representations
Version: 0.1.0
Authors@R: c(
person("Nan", "Xiao", email = "[email protected]", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-0250-5673")),
person("Yilong", "Zhang", email = "[email protected]", role = c("aut")),
person("Keaven", "Anderson", email = "[email protected]", role = c("aut")),
person("Amin", "Shirazi", email = "[email protected]", role = c("ctb")),
person("Jeff", "Cheng", email = "[email protected]", role = c("ctb")),
person("Merck Sharp & Dohme Corp", role = "cph")
)
Description: A tool, grammar, and standard to represent and exchange
R package source code as text files. Converts one or more source
packages to a text file and restores the package structures from the file.
License: GPL-3
URL: https://merck.github.io/pkglite/, https://github.com/Merck/pkglite
BugReports: https://github.com/Merck/pkglite/issues
Encoding: UTF-8
LazyData: true
VignetteBuilder: knitr
Depends: R (>= 3.5.0)
Imports:
cli,
magrittr,
remotes
Suggests:
knitr,
rmarkdown
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions LICENSES_THIRD_PARTY
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pkglite uses third-party R packages which may be distributed under different
licenses. We have listed all of these third party packages and their licenses
below. However, the most up-to-date information can be found via inspecting
the Imports field in the DESCRIPTION file.

You must agree to the terms of these licenses, in addition to the pkglite
source code license, in order to use this software.

--------------------------------------------------
Third party R packages listed by License type
[Format: Name - URL]
--------------------------------------------------

MIT / X11 License (or adaptations) (https://www.opensource.org/licenses/MIT)
* cli - https://cli.r-lib.org/LICENSE.html
* magrittr - https://magrittr.tidyverse.org/LICENSE.html

GPL-2 | GPL-3 [expanded from: GPL (>=2)]
* remotes - https://remotes.r-lib.org/
38 changes: 38 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by roxygen2: do not edit by hand

S3method(print,file_collection)
S3method(print,file_spec)
export("%>%")
export(collate)
export(ext_binary)
export(ext_text)
export(file_auto)
export(file_data)
export(file_default)
export(file_ectd)
export(file_man)
export(file_r)
export(file_root_all)
export(file_root_core)
export(file_spec)
export(file_src)
export(file_vignettes)
export(is_file_collection)
export(is_file_spec)
export(pack)
export(pattern_file_root_all)
export(pattern_file_root_core)
export(pattern_file_sanitize)
export(remove_content)
export(sanitize_file_collection)
export(unpack)
export(verify_ascii)
importFrom(cli,cli_h1)
importFrom(cli,cli_li)
importFrom(cli,cli_rule)
importFrom(cli,cli_text)
importFrom(cli,cli_ul)
importFrom(magrittr,"%>%")
importFrom(remotes,install_local)
importFrom(stats,setNames)
importFrom(tools,showNonASCIIfile)
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# pkglite 0.1.0

- First version.
298 changes: 298 additions & 0 deletions R/collate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
# Copyright (c) 2021 Merck Sharp & Dohme Corp. a subsidiary of Merck & Co., Inc., Kenilworth, NJ, USA.
#
# This file is part of the pkglite program.
#
# pkglite is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

#' Evaluate a list of file specifications
#'
#' Evaluate a list of file specifications and bind the results
#' as a file collection.
#'
#' @param pkg Path to the package directory.
#' @param ... One or more file specification objects.
#'
#' @return A file collection object containing the package name,
#' file paths, and file format types.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Get package metadata, for example, the package name.
#' \item Flatten the input list of file specification(s).
#' \item Evaluate the file specification(s) under the \code{pkg} directory.
#' \item Remove duplicated files from all evaluation results and store
#' the file path and format information in a data frame.
#' \item Return the package metadata and the data frame in a list.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @export collate
#'
#' @examples
#' system.file("examples/pkg1/", package = "pkglite") %>%
#' collate(file_default())
collate <- function(pkg = ".", ...) {
path <- normalizePath(pkg, winslash = "/")

# get package metadata ----
pkg_name <- get_pkg_name(path)

# get file collection ----
lst_spec <- list(...)
# this is useful for file spec templates like `file_default()`
# where the return can be a list of file specs
lst_spec <- flatten_file_spec(lst_spec)
k <- length(lst_spec)
if (k == 0L) stop("Please provide at least one file specification")
lst_collection <- lapply(1:k, function(i) fs_to_df(path, lst_spec[[i]]))
df <- do.call(rbind, lst_collection)
# remove duplicates
df <- unique(df)

# bind everything together ----
lst <- list("pkg_name" = pkg_name, "df" = df)
class(lst) <- "file_collection"

lst
}

#' Evaluate a file specification and return a file collection data frame
#'
#' @param pkg_path Path to the package directory.
#' @param file_spec A file specification.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Evaluate a file specification under a package directory
#' using \code{list.files()}.
#' \item Return the a data frame containing the absolute path,
#' relative path, and format of the files.
#' \item Return a data frame of 0 rows if there are no matching files.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @noRd
fs_to_df <- function(pkg_path, file_spec) {
pkg_path <- normalizePath(pkg_path, winslash = "/")
rel_path <- file_spec$path

full_path <- paste(pkg_path, rel_path, sep = "/")
df <- create_fc_df(nrow = 0L)
if (!dir.exists(full_path)) {
return(df)
}

full_path <- normalizePath(full_path, winslash = "/")
file_path_abs <- list.files(
path = full_path, pattern = file_spec$pattern, full.names = TRUE,
recursive = file_spec$recursive, ignore.case = file_spec$ignore_case,
all.files = file_spec$all_files, include.dirs = FALSE, no.. = TRUE
)

k <- length(file_path_abs)
if (k == 0L) {
return(df)
}

df <- create_fc_df(nrow = k)
df$"path_abs" <- file_path_abs
df$"path_rel" <- get_rel_path(pkg_path, file_path_abs)
df$"format" <- file_spec$format

df
}

#' Is this a file collection object?
#'
#' @param object Any R object
#'
#' @return Logical. `TRUE` if it is a file collection object,
#' `FALSE` otherwise.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Check if the object has the class \code{file_collection}.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @export is_file_collection
#'
#' @examples
#' system.file("examples/pkg1/", package = "pkglite") %>%
#' collate(file_default()) %>%
#' is_file_collection()
is_file_collection <- function(object) {
if ("file_collection" %in% class(object)) TRUE else FALSE
}

#' Print a file collection
#'
#' @param x An object of class `file_collection`.
#' @param ... Additional parameters for [print()] (not used).
#'
#' @return The input `file_collection` object.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Print the metadata and the data frame in a file collection object.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @method print file_collection
#'
#' @importFrom cli cli_h1 cli_rule
#'
#' @export
#'
#' @examples
#' fc <- system.file("examples/pkg1/", package = "pkglite") %>%
#' collate(file_default())
#' fc
print.file_collection <- function(x, ...) {
pkg_name <- x$pkg_name
df <- x$df
df$"path_abs" <- NULL # only prints relative path

cli_h1("File collection")
cli_rule(left = "Package: {.pkg {pkg_name}}")
print(df)
invisible(x)
}

#' Sanitize file collection
#'
#' Remove commonly excluded files and directories from a file collection.
#'
#' @param x a file collection object
#'
#' @return The sanitized file collection object.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Remove the files whose names match certain patterns from the
#' file collection and return a sanitized file collection.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @export sanitize_file_collection
#'
#' @examples
#' system.file("examples/pkg1/", package = "pkglite") %>%
#' collate(file_default()) %>%
#' sanitize_file_collection()
sanitize_file_collection <- function(x) {
lst <- x
df <- lst$df
df <- df[!grepl(cat_patterns(pattern_file_sanitize()), df$"path_abs"), ]
row.names(df) <- NULL
lst$df <- df
lst
}

#' Flatten a nested list of file specifications to a single-level list
#'
#' @param lst A (nested) list of file specifications.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Given a possibly nested list of file specifications,
#' parse the list recursively and flatten the list structure
#' until it becomes a single-level list of file specifications.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @noRd
flatten_file_spec <- function(lst) {
if (!all(sapply(lapply(lst, class), `%in%`, c("file_spec", "list")))) {
stop("Input has objects that are not file specifications")
}
do.call(c, lapply(lst, function(x) if (!is_file_spec(x)) flatten_file_spec(x) else list(x)))
}

#' Get file's relative path to the package root
#'
#' @param pkg_path Path to the package directory.
#' @param file_path_abs The file's absolute path.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Given the package directory path and a vector of the file's
#' absolute path, return the file's path relative to the package root.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @noRd
get_rel_path <- function(pkg_path, file_path_abs) {
gsub(paste0("^", pkg_path, "/"), "", file_path_abs)
}

#' Create a data frame for the file collection
#'
#' @param nrow Number of rows in the data frame.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Return a data frame that has \code{nrow} rows that equals to
#' the number of files in a file collection. The columns will contain
#' the absolute path, relative path, and format of the files.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @importFrom stats setNames
#'
#' @noRd
create_fc_df <- function(nrow) {
setNames(
data.frame(matrix(nrow = nrow, ncol = 3L), stringsAsFactors = FALSE),
c("path_abs", "path_rel", "format")
)
}

#' Get package name from the DESCRIPTION file
#'
#' @param path Package directory path.
#'
#' @section Specification:
#' \if{latex}{
#' \itemize{
#' \item Read the DESCRIPTION file under \code{path}.
#' \item Parse and return the value of the \code{Package} field.
#' }
#' }
#' \if{html}{The contents of this section are shown in PDF user manual only.}
#'
#' @noRd
get_pkg_name <- function(path) {
path <- paste0(path, "/DESCRIPTION")
if (!file.exists(path)) stop("Package root does not have a DESCRIPTION file")
desc_file <- normalizePath(path, winslash = "/")
pkg_name <- unname(read.dcf(desc_file)[, "Package"])
pkg_name
}
Loading

0 comments on commit 1206e4b

Please sign in to comment.