Nothing
# Generate a Valid Save File Path
#
# Constructs an absolute file path using user input with sensible defaults and robust path validation.
# It normalizes paths by replacing backslashes, handles directory inputs, verifies existence of specified directories,
# and ensures the resulting file path includes a file extension.
#
# @param save_as Optional character string specifying the desired save path or directory.
# If a directory is specified (ends with a slash or exists as a directory), the returned path includes the default file name.
# If a full file path is provided, it validates the directory part.
# If a file name only is provided, the file is placed in the \code{default_dir}.
# If \code{NULL}, returns path combining \code{default_dir} and \code{default_name}.
# @param default_name Character string specifying the default file name to use when \code{save_as} is \code{NULL} or a directory.
# Defaults to \code{"output"}. If no extension is present, \code{file.ext} will be appended.
# @param default_dir Character string specifying the default directory to save files in when \code{save_as} is \code{NULL} or only a file name.
# Defaults to \code{tempdir()}.
# @param file.ext Character string specifying the file extension to append if the filename does not already have one.
# Defaults to \code{".pdf"}. Should include the leading dot (e.g., \code{".csv"}, \code{".txt"}).
# If the filename already contains an extension, this parameter is ignored.
#
# @return A character string of a normalized save path with forward slashes and guaranteed file extension.
#
# @details
# - If \code{save_as} is \code{NULL}, the function returns the default directory combined with the default file name.
# - If \code{save_as} is a directory path (either ends with slash or exists), it returns the directory path combined with the default file name.
# - If \code{save_as} is a full file path, it verifies that the directory exists and returns a normalized full path.
# - If a file name only is given, the file will be placed in the \code{default_dir}.
# - The function checks if the final filename has an extension (pattern: one or more characters after a dot).
# If no extension is detected, \code{file.ext} is automatically appended.
# - Extension checking works on the final filename component, not on directory names containing dots.
#
# throws Error if the specified directory in \code{save_as} does not exist.
#
# @examples
# # Default path with .pdf extension
# get_save_path()
#
# # Directory only - uses default name with extension
# get_save_path("mydata/")
#
# # Full path with extension specified - keeps original extension
# get_save_path("mydata/output.csv")
#
# # Filename without extension - adds default .pdf
# get_save_path("my_file")
#
# # Custom extension
# get_save_path("report", file.ext = ".pdf")
#
# # Filename with extension in custom directory
# get_save_path("my_file.txt", default_dir = "~/Documents")
#
get_save_path <- function(save_as = NULL,
default_name = "output",
default_dir = tempdir(),
file.ext = ".pdf") {
# remove \\ and use / instead
default_dir <- gsub("\\\\", "/", default_dir)
# Function to ensure file extension
ensure_extension <- function(filename, ext) {
if (grepl("\\.[^\\.]+$", filename)) {
# Has some extension, return as is
filename
} else {
# Append given extension
paste0(filename, ext)
}
}
# 1. Default Case: If no input, use default path and name.
if (is.null(save_as)) {
file_with_ext <- ensure_extension(default_name, file.ext)
return(file.path(default_dir, file_with_ext))
}
# Normalize input by removing trailing slashes for cleaner path resolution later
save_as_normalized <- sub("/+$|\\\\+$", "", save_as)
# Determine if the user intended a directory:
# A. Input ends with a slash (e.g., "mydata/").
# B. Input is the path to an existing directory (e.g., "mydata" where 'mydata' exists).
is_directory_only_intent <- grepl("/$|\\\\$", save_as) || dir.exists(save_as_normalized)
# --- Case Handling ---
# 2. Case: Directory Only Intent (e.g., "mydata/" or "C:/reports")
# Output: 'mydata/default_name'
if (is_directory_only_intent) {
# Check if the directory exists.
if (!dir.exists(save_as_normalized)) {
stop(paste0("Error: The specified directory does not exist: '", save_as_normalized, "'"))
}
# If it exists, normalize the path and apply the default name with extension.
base_dir <- normalizePath(save_as_normalized, winslash = "/", mustWork = FALSE)
file_with_ext <- ensure_extension(default_name, file.ext)
return(file.path(base_dir, file_with_ext))
}
# 3. Case: Full Path or Filename
# Extract the directory component
dir_part <- dirname(save_as)
# Check if the input is a full path (i.e., not just a filename, where dir_part == ".")
if (dir_part != ".") {
# A: Full Path Provided (e.g., "nonexistent/output.csv")
# Check if the directory part of the path exists.
if (!dir.exists(dir_part)) {
# Stop execution with a clear error if the directory is missing.
stop(paste0("Error: The directory part of the path does not exist: '", dir_part, "'"))
}
# Ensure the filename has the extension
filename <- basename(save_as)
file_with_ext <- ensure_extension(filename, file.ext)
# Normalize the DIRECTORY (which is guaranteed to exist here) to an
# absolute path, then append the file name. Normalizing the directory
# rather than the full path matters: on Unix, normalizePath(mustWork =
# FALSE) leaves a path relative when the final file does not yet exist,
# and a relative output path later breaks rmarkdown::render(), which
# changes the working directory to the input file location before
# resolving the output path.
base_dir <- normalizePath(dir_part, winslash = "/", mustWork = TRUE)
full_path <- file.path(base_dir, file_with_ext)
return(full_path)
} else {
# B: Only a Filename provided (e.g., "my_file" or "my_file.txt").
file_with_ext <- ensure_extension(save_as, file.ext)
# Output: 'default_dir/filename'
return(file.path(default_dir, file_with_ext))
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.