File: rdiff.R

package info (click to toggle)
r-cran-diffobj 0.3.5-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 2,432 kB
  • sloc: ansic: 455; javascript: 96; sh: 32; makefile: 8
file content (153 lines) | stat: -rwxr-xr-x 5,469 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright (C) 2021 Brodie Gaslam
#
# This file is part of "diffobj - Diffs for R Objects"
#
# This program 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 2 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.
#
# Go to <https://www.r-project.org/Licenses/GPL-2> for a copy of the license.

#' Run Rdiff Directly on R Objects
#'
#' These functions are here for reference and testing purposes.  They are
#' wrappers to \code{tools::Rdiff} and rely on an existing system diff utility.
#' You should be using \code{\link{ses}} or \code{\link{diffChr}} instead of
#' \code{Rdiff_chr} and \code{\link{diffPrint}} instead of \code{Rdiff_obj}.
#' See limitations in note.
#'
#' \code{Rdiff_chr} runs diffs on character vectors or objects coerced to
#' character vectors, where each value in the vectors is treated as a line in a
#' file.  \code{Rdiff_chr} always runs with the \code{useDiff} and \code{Log}
#' parameters set to \code{TRUE}.
#'
#' \code{Rdiff_obj} runs diffs on the \code{print}ed representation of
#' the provided objects.  For each of \code{from}, \code{to}, will check if they
#' are 1 length character vectors referencing an RDS file, and will use the
#' contents of that RDS file as the object to compare.
#'
#' @note These functions will try to use the system \code{diff} utility. This
#'   will fail in systems that do not have that utility available (e.g. windows
#'   installation without Rtools).
#' @importFrom tools Rdiff
#' @export
#' @seealso \code{\link{ses}}, \code{\link[=diffPrint]{diff*}}
#' @param from character or object coercible to character for \code{Rdiff_chr},
#'   any R object with \code{Rdiff_obj}, or a file pointing to an RDS object
#' @param to character same as \code{from}
#' @param nullPointers passed to \code{tools::Rdiff}
#' @param silent TRUE or FALSE, whether to display output to screen
#' @param minimal TRUE or FALSE, whether to exclude the lines that show the
#'   actual differences or only the actual edit script commands
#' @return the Rdiff output, invisibly if \code{silent} is FALSE
#' Rdiff_chr(letters[1:5], LETTERS[1:5])
#' Rdiff_obj(letters[1:5], LETTERS[1:5])

Rdiff_chr <- function(from, to, silent=FALSE, minimal=FALSE, nullPointers=TRUE) {
  A <- try(as.character(from))
  if(inherits(A, "try-error")) stop("Unable to coerce `target` to character.")
  B <- try(as.character(to))
  if(inherits(B, "try-error")) stop("Unable to coerce `current` to character.")

  af <- tempfile()
  bf <- tempfile()
  writeLines(A, af)
  writeLines(B, bf)
  on.exit(unlink(c(af, bf)))

  Rdiff_run(
    silent=silent, minimal=minimal, from=af, to=bf, nullPointers=nullPointers
  )
}
#' @export
#' @rdname Rdiff_chr

Rdiff_obj <- function(from, to, silent=FALSE, minimal=FALSE, nullPointers=TRUE) {
  dummy.env <- new.env()  # used b/c unique object
  files <- try(
    vapply(
      list(from, to),
      function(x) {
        if(
          is.character(x) && length(x) == 1L && !is.na(x) && file_test("-f", x)
        ) {
          rdstry <- tryCatch(readRDS(x), error=function(x) dummy.env)
          if(!identical(rdstry, dummy.env)) x <- rdstry
        }
        f <- tempfile()
        on.exit(unlink(f))
        capture.output(if(isS4(x)) show(x) else print(x), file=f)
        on.exit()
        f
      },
      character(1L)
  ) )
  if(inherits(files, "try-error"))
    stop("Unable to store text representation of objects")
  on.exit(unlink(files))
  Rdiff_run(
    from=files[[1L]], to=files[[2L]], silent=silent, minimal=minimal,
    nullPointers=nullPointers
  )
}
# Internal use only: BEWARE, will unlink from, to

Rdiff_run <- function(from, to, nullPointers, silent, minimal) {
  stopifnot(
    isTRUE(silent) || identical(silent, FALSE),
    isTRUE(minimal) || identical(minimal, FALSE)
  )
  res <- tryCatch(
    Rdiff(
      from=from, to=to, useDiff=TRUE, Log=TRUE, nullPointers=nullPointers
    )$out,
    warning=function(e)
      stop(
        "`tools::Rdiff` returned a warning; this likely means you are running ",
        "without a `diff` utility accessible to R"
      )
  )
  if(!is.character(res))
    # nocov start
    stop("Internal Error: Unexpected tools::Rdiff output, contact maintainer")
    # nocov end

  res <- if(minimal) res[!grepl("^[<>-]", res)] else res
  if(silent) res else {
    cat(res, sep="\n")
    invisible(res)
  }
}
#' Attempt to Detect Whether diff Utility is Available
#'
#' Checks whether \code{\link[=Rdiff]{tools::Rdiff}} issues a warning when
#' running with \code{useDiff=TRUE} and if it does assumes this is because the
#' diff utility is not available.  Intended primarily for testing purposes.
#'
#' @export
#' @return TRUE or FALSE
#' @param test.with function to test for diff presence with, typically Rdiff
#' @examples
#' has_Rdiff()

has_Rdiff <- function(test.with=tools::Rdiff) {
  f.a <- tempfile()
  f.b <- tempfile()
  on.exit(unlink(c(f.a, f.b)))
  writeLines(letters[1:3], f.a)
  writeLines(LETTERS, f.b)
  tryCatch(
    {
      test.with(
        from=f.a, to=f.b, useDiff=TRUE, Log=TRUE, nullPointers=FALSE
      )
      TRUE
    }, warning=function(e) FALSE
  )
}