R/seasonder_specsHandling.R

Defines functions seasonder_defaultSpecsPathForFile seasonder_find_spectra_file_type seasonder_readYAMLSpecs seasonder_defaultSpecsFilePath

Documented in seasonder_defaultSpecsFilePath seasonder_defaultSpecsPathForFile seasonder_find_spectra_file_type seasonder_readYAMLSpecs

# Set valid YAML versions for seasondecs and seasondecssy specifications
seasonder_the$valid_yaml_seasondecs_versions <- c("1.0.0")  # Valid version for seasondecs YAML specs
seasonder_the$valid_yaml_seasondecssy_versions <- c("1.0.0")  # Valid version for seasondecssy YAML specs


#' Get the Default Specifications File Path
#'
#' This function returns the default file path for the specifications YAML file corresponding
#' to the provided type. The type must be one of the names defined in the default paths (i.e., "CS" or "CSSY").
#'
#' @param type A character string specifying the type of specifications file. Default is "CS".
#'
#' @return A character string representing the full path to the YAML specifications file.
#'
#' @importFrom rlang arg_match
#'
#' @examples
#' 
#'   # Retrieve the default CS specifications file path
#'   cs_specs_path <- seasonder_defaultSpecsFilePath("CS")
#'
#'   # Retrieve the default CSSY specifications file path
#'   cssy_specs_path <- seasonder_defaultSpecsFilePath("CSSY")
#' @export
seasonder_defaultSpecsFilePath <- function(type = "CS") {
  # Retrieve the list of default specifications file paths from the shared environment
  default_paths <- list(
    "CS" = system.file("specs", "CS_V1.yaml", package = "SeaSondeR"),
    "CSSY" = system.file("specs", "CSSY_V1.yaml", package = "SeaSondeR"),
    "CSSW" = system.file("specs", "CSSW_V1.yaml", package = "SeaSondeR")
  )

  # Match the provided type against the allowed names in the default paths list
  rlang::arg_match(type, names(default_paths))

  # Extract the file path corresponding to the specified type
  out <- default_paths[[type]]

  # Return the obtained file path
  return(out)
}



#' Read Specifications from a YAML File
#'
#' This function reads a YAML file containing specifications, handles potential
#' reading errors, and extracts specific information based on a provided path.
#'
#' @param file_path A string. The path to the YAML file.
#' @param path A character vector. Represents the path within the YAML file to
#'   access the desired information. For example, to access fields of version V2
#'   of the header, the path would be \code{c("header", "versions", "V2")}.
#'
#' @return A list. The information extracted from the YAML file based on the
#'   provided path.
#' @seealso \code{\link[yaml]{read_yaml}} for the underlying YAML reading.
#' @seealso \code{\link[purrr]{pluck}} for the data extraction mechanism used.
#'
#' @details
#' This function provides built-in error handling which aborts execution and logs
#' detailed error messages in case of:
#' - File not found.
#' - Error in reading the YAML content.
#' - The read YAML content is not a list.
#' - No data found for the provided path in the YAML content.
#'
#' Errors generated are of class \code{"seasonder_read_yaml_file_error"}. For logging and aborting,
#' this function uses \code{\link[=seasonder_logAndAbort]{seasonder_logAndAbort}}.
#' @importFrom yaml read_yaml
#'
#' @examples
#' # Example: Read the CS header specifications (version V1) from the default specs file
#' specs_path <- seasonder_defaultSpecsFilePath("CS")
#' result <- seasonder_readYAMLSpecs(specs_path, c("header", "V1"))
#' str(result)
#' @export
seasonder_readYAMLSpecs <- function(file_path, path = rlang::zap()) {

  conditions_params <- list(calling_function = "seasonder_readYAMLSpecs",class="seasonder_read_yaml_file_error",seasonder_yaml_file_path=file_path,seasonder_yaml_specs_path=path)
  # Check if the file exists
  if (!file.exists(file_path)) {
    rlang::inject(seasonder_logAndAbort(glue::glue("File '{file_path %||% ''}' not found."),!!!conditions_params))
  }

  # Read the content from the YAML file
  yaml_content <- rlang::try_fetch({
    yaml::read_yaml(file_path)
  }, error = function(e) {
    rlang::inject(seasonder_logAndAbort(glue::glue("Reading error. The file '{file_path %||% ''}' might not be a valid YAML. Reason: {conditionMessage(e)}"),!!!conditions_params,parent=e))
  })

  # If the content is not a list, throw an error
  if (!is.list(yaml_content)) {
    rlang::inject(seasonder_logAndAbort(glue::glue("Invalid YAML structure in file '{file_path}'."),!!!conditions_params))
  }

  extracted_data<- yaml_content
  if(!rlang::is_zap(path) && is.character(path)){
    # Extract the desired data based on the given path
    extracted_data <- purrr::pluck(yaml_content, !!!path)

    # If no data is found for the provided path, throw an error
    if (is.null(extracted_data)) {
      rlang::inject(seasonder_logAndAbort(glue::glue("Invalid specs path '{path}' for file '{file_path}'."),!!!conditions_params))
    }

  }

  return(extracted_data)
}


#' Determine the Spectra File Type
#'
#' This function identifies the type of a spectra file (either "CS" or "CSSY") by reading its header block based on YAML specifications.
#' It first attempts to read a key size block using the CSSY specifications, and if that fails, it reopens the file and tries to read the CS header block.
#'
#' @param filepath A character string specifying the path to the spectra file.
#' @param endian A character string indicating the file's byte order ("big" by default).
#'
#' @return A character string representing the spectra file type ("CS" or "CSSY").
#'
#' @details
#' The function sets up error handling parameters and uses YAML specifications retrieved via
#' \code{seasonder_readYAMLSpecs} and \code{seasonder_defaultSpecsFilePath}. It opens the file in binary read mode
#' and ensures the connection is closed upon exit. If reading the key size block fails, it reopens the file to try
#' reading the CS header block. The final file type is determined by the key returned from the file block.
#'
#' @importFrom rlang try_fetch inject inherits_any
#' @importFrom glue glue
#'
seasonder_find_spectra_file_type <- function(filepath, endian = "big") {
  # Set up error handling parameters with function name, error class, and file path
  conditions_params <- list(
    calling_function = "seasonder_find_spectra_file_type",
    class = "seasonder_read_cs_file_error",
    seasonder_cs_filepath = filepath
  )

  # Initialize output type to "CS" by default
  out <- "CS"

  # Retrieve YAML specifications for the key size block from the CSSY specifications file
  specs_key_size <- seasonder_readYAMLSpecs(seasonder_defaultSpecsFilePath("CSSY"), "key_size_block")

  # Attempt to open the file in binary mode ("rb") with warnings suppressed
  connection <- rlang::try_fetch(
    suppressWarnings(file(filepath, "rb")),
    error = function(e) {
      # Abort if the file connection cannot be opened, including the error message
      rlang::inject(
        seasonder_logAndAbort(
          glue::glue("Could no open connection to file {filepath %||% ''}. Reason: {conditionMessage(e)}."),
          !!!conditions_params,
          parent = e
        )
      )
    }
  )
  # Ensure the file connection is closed when the function exits
  on.exit(close(connection), add = TRUE)

  # Try to read the file block using the CSSY key size specifications
  file_id <- rlang::try_fetch(
    seasonder_readSeaSondeCSFileBlock(specs_key_size, connection, endian),
    error = function(e) {
      # If reading the CSSY block fails, attempt to reopen the file
      connection <- rlang::try_fetch(
        suppressWarnings(file(filepath, "rb")),
        error = function(e) {
          # Abort if reconnection fails
          rlang::inject(
            seasonder_logAndAbort(
              glue::glue("Could no open connection to file {filepath %||% ''}. Reason: {conditionMessage(e)}."),
              !!!conditions_params,
              parent = e
            )
          )
        }
      )
      # Ensure the new connection is closed on exit
      on.exit(close(connection), add = TRUE)

      # Retrieve YAML specifications for the CS header block (version V1)
      CS_header_specs <- seasonder_readYAMLSpecs(seasonder_defaultSpecsFilePath("CS"), c("header","V1"))

      # Attempt to read the CS header block; use try() to capture any errors silently
      V1_header <- try(seasonder_readSeaSondeCSFileBlock(CS_header_specs, connection, endian), silent = TRUE)

      # Determine if the CS header was read successfully (i.e., no error occurred)
      is_CS <- !rlang::inherits_any(V1_header, "try-error")

      # If successful, return a list with key "CS"; otherwise, propagate the error
      if (is_CS) return(list(key = "CS")) else e
    }
  )
  # Use a switch to determine the output based on the key read from the file block
  out <- switch(
    file_id$key,
    CSSW = "CSSW",
    CSSY = "CSSY",
    CS = "CS",
    # If the key is not recognized, abort with an error message
    rlang::inject(
      seasonder_logAndAbort(
        glue::glue("Spectra file type not recognized. File: {filepath %||% ''}."),
        !!!conditions_params
      )
    )
  )

  # Return the determined spectra file type
  return(out)
}

#' Get the Default Specifications Path for a Spectra File
#'
#' This function returns the default YAML specifications file path corresponding to a given spectra file.
#' It first determines the file type by analyzing the file content and then retrieves the associated default
#' specifications path.
#'
#' @param filepath A character string specifying the path to the spectra file.
#' @param endian A character string indicating the file's byte order ("big" by default).
#'
#' @return A character string representing the default YAML specifications file path for the detected file type.
#'
#' @details
#' The function leverages \code{seasonder_find_spectra_file_type} to determine whether the file is of type "CS" or "CSSY".
#' It then uses \code{seasonder_defaultSpecsFilePath} to obtain the corresponding default specifications path.
#'
#' @seealso \code{\link{seasonder_find_spectra_file_type}}, \code{\link{seasonder_defaultSpecsFilePath}}
#'
seasonder_defaultSpecsPathForFile <- function(filepath, endian = "big") {
  # Determine the file type ("CS" or "CSSY") by analyzing the spectra file
  file_type <- seasonder_find_spectra_file_type(filepath, endian = endian)

  # Retrieve the default specifications file path based on the detected file type
  specs_path <- seasonder_defaultSpecsFilePath(type = file_type)

  # Return the specifications file path
  return(specs_path)
}

Try the SeaSondeR package in your browser

Any scripts or data that you put into this service are public.

SeaSondeR documentation built on June 8, 2025, 10:50 a.m.