diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index fe0a2568c..7df511a69 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -18,12 +18,22 @@ jobs: fail-fast: false matrix: config: - - { os: "ubuntu-latest", R: "latest" } - - { os: "macOS-latest", R: "latest" } + + # Check latest R versions + - { os: "ubuntu-latest", R: "latest" } + - { os: "macOS-latest", R: "latest" } - { os: "windows-latest", R: "latest" } - - { os: "ubuntu-latest", R: "devel" } + - { os: "ubuntu-latest", R: "devel" } - { os: "windows-latest", R: "devel" } + # Check older versions of R + - { os: "ubuntu-latest", R: 'oldrel-1'} + - { os: "ubuntu-latest", R: 'oldrel-2'} + - { os: "ubuntu-latest", R: 'oldrel-3'} + - { os: "ubuntu-latest", R: 'oldrel-4'} + + - { os: "windows-latest", R: "oldrel-4" } + env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/paws.common/DESCRIPTION b/paws.common/DESCRIPTION index 28316a776..45a13b7a9 100644 --- a/paws.common/DESCRIPTION +++ b/paws.common/DESCRIPTION @@ -1,7 +1,7 @@ Package: paws.common Type: Package Title: Paws Low-Level Amazon Web Services API -Version: 0.8.7 +Version: 0.8.8 Authors@R: c( person("David", "Kretch", email = "david.kretch@gmail.com", role = "aut"), person("Adam", "Banker", email = "adam.banker39@gmail.com", role = "aut"), @@ -14,6 +14,7 @@ Description: Functions for making low-level API requests to Amazon Web Services higher-level interfaces to individual services, such as Simple Storage Service (S3). License: Apache License (>= 2.0) +Depends: R (>= 4.1.0) URL: https://github.com/paws-r/paws, https://paws-r.r-universe.dev/paws.common, https://www.paws-r-sdk.com BugReports: https://github.com/paws-r/paws/issues Encoding: UTF-8 diff --git a/paws.common/NEWS.md b/paws.common/NEWS.md index 3378a4c5e..1c012898a 100644 --- a/paws.common/NEWS.md +++ b/paws.common/NEWS.md @@ -1,3 +1,6 @@ +# paws.common 0.8.8 +* fix C++ compilation issues on older R versions by refactoring code to use C++11 standard and requiring R >= 4.1.0 (#957). Thanks to @detule for raising the issue. + # paws.common 0.8.7 * fix timezone handling in bearer token tests (#955). Thanks to @kyleam for raising issue. diff --git a/paws.common/src/RcppExports.cpp b/paws.common/src/RcppExports.cpp index 3c7a0ca97..7eee77425 100644 --- a/paws.common/src/RcppExports.cpp +++ b/paws.common/src/RcppExports.cpp @@ -68,7 +68,7 @@ BEGIN_RCPP END_RCPP } // json_build_any -std::string json_build_any(SEXP values); +CharacterVector json_build_any(SEXP values); RcppExport SEXP _paws_common_json_build_any(SEXP valuesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; diff --git a/paws.common/src/json_builder.cpp b/paws.common/src/json_builder.cpp index 19c86372a..969e19ebd 100644 --- a/paws.common/src/json_builder.cpp +++ b/paws.common/src/json_builder.cpp @@ -67,7 +67,7 @@ inline std::string tag_get(SEXP object, const char *tag) case STRSXP: if (Rf_length(val) > 0) { - return std::string(CHAR(STRING_ELT(val, 0))); + return std::string(Rf_translateCharUTF8(STRING_ELT(val, 0))); } break; case LGLSXP: @@ -135,8 +135,8 @@ std::string type(SEXP object) } // -------------------- Forward declaration -------------------- -// This function will be defined later and called recursively -std::string json_build_any(SEXP values); +// Internal function that builds JSON and returns std::string (used internally) +std::string json_build_internal(SEXP values); // -------------------- Helper function to safely convert to string -------------------- // This function converts an SEXP to a string representation, handling various R types. @@ -151,17 +151,17 @@ std::string safe_as_string(SEXP x) switch (type) { case STRSXP: - // Directly return string for character SEXPs - return std::string(CHAR(STRING_ELT(x, 0))); + // Directly return string for character SEXPs, ensuring UTF-8 encoding + return std::string(Rf_translateCharUTF8(STRING_ELT(x, 0))); case INTSXP: case REALSXP: case LGLSXP: case RAWSXP: // Coerce to character and then return string for numeric, logical, raw - return std::string(CHAR(STRING_ELT(Rf_coerceVector(x, STRSXP), 0))); + return std::string(Rf_translateCharUTF8(STRING_ELT(Rf_coerceVector(x, STRSXP), 0))); default: // Coerce any other type to character as a fallback - return std::string(CHAR(STRING_ELT(Rf_coerceVector(x, STRSXP), 0))); + return std::string(Rf_translateCharUTF8(STRING_ELT(Rf_coerceVector(x, STRSXP), 0))); } } @@ -255,7 +255,7 @@ std::string json_build_list(SEXP values) for (R_xlen_t i = 0; i < n; i++) { // Recursively build JSON for each element - std::string elem_json = json_build_any(VECTOR_ELT(values, i)); + std::string elem_json = json_build_internal(VECTOR_ELT(values, i)); // Only add non-empty elements, excluding "empty" JSON array/object strings if (!elem_json.empty() && elem_json != "[]" && elem_json != "{}") { @@ -325,7 +325,7 @@ std::string json_build_structure(SEXP values) } // Recursively build JSON for the payload's value - return json_build_any(payload_val); + return json_build_internal(payload_val); } SEXP names = Rf_getAttrib(values, symbol_cache.names_sym); @@ -360,11 +360,11 @@ std::string json_build_structure(SEXP values) val = uuid_val; // Use the newly created UUID SEXP } - std::string key = CHAR(STRING_ELT(names, i)); // Get the field name + std::string key = Rf_translateCharUTF8(STRING_ELT(names, i)); // Get the field name std::string loc_name = tag_get(val, "locationName"); // Check for locationName tag std::string name = loc_name.empty() ? key : loc_name; // Use locationName if present - std::string json_val = json_build_any(val); // Recursively build JSON for field value + std::string json_val = json_build_internal(val); // Recursively build JSON for field value // Only add non-empty fields, excluding "empty" JSON array/object strings if (!json_val.empty() && json_val != "[]" && json_val != "{}") { @@ -420,14 +420,14 @@ inline std::string json_build_map(SEXP values) // Populate the vector with key-value pairs from the R object for (R_xlen_t i = 0; i < n; ++i) { - std::string key = CHAR(STRING_ELT(names_sexp, i)); + std::string key = Rf_translateCharUTF8(STRING_ELT(names_sexp, i)); SEXP val = VECTOR_ELT(values, i); named_elements.push_back({key, val}); } // Sort the vector based on the string key in each pair std::sort(named_elements.begin(), named_elements.end(), - [](const auto &a, const auto &b) + [](const std::pair &a, const std::pair &b) { return a.first < b.first; }); @@ -442,7 +442,7 @@ inline std::string json_build_map(SEXP values) SEXP val = element.second; // Recursively build JSON for the value - std::string json_val = json_build_any(val); + std::string json_val = json_build_internal(val); // Only add non-empty pairs to the final JSON string if (!json_val.empty() && json_val != "[]" && json_val != "{}") @@ -469,17 +469,8 @@ inline std::string json_build_map(SEXP values) return result; } -/** - * @brief Build Json Strings Using AWS Attributes for JSON Template - * - * @param object A list to be parsed into JSON string - * - * @return a JSON String - */ -//' @useDynLib paws.common _paws_common_json_build_any -//' @importFrom Rcpp evalCpp -// [[Rcpp::export]] -std::string json_build_any(SEXP values) +// Internal implementation that returns std::string (used by recursive calls) +std::string json_build_internal(SEXP values) { // Determine the effective type of the R object for JSON building std::string t = type(values); @@ -494,3 +485,20 @@ std::string json_build_any(SEXP values) // Default to scalar if none of the above, or if `type` returns "scalar" return json_build_scalar(values); } + +/** + * @brief Build Json Strings Using AWS Attributes for JSON Template + * + * @param object A list to be parsed into JSON string + * + * @return a JSON String + */ +//' @useDynLib paws.common _paws_common_json_build_any +//' @importFrom Rcpp evalCpp +// [[Rcpp::export]] +CharacterVector json_build_any(SEXP values) +{ + // Call internal implementation and UTF-8 encode result + std::string result = json_build_internal(values); + return CharacterVector::create(String(result, CE_UTF8)); +} diff --git a/paws.common/src/json_escape.cpp b/paws.common/src/json_escape.cpp index fbc1d9ba4..a90cf4cfd 100644 --- a/paws.common/src/json_escape.cpp +++ b/paws.common/src/json_escape.cpp @@ -143,6 +143,7 @@ CharacterVector json_convert_string(CharacterVector x) for (int i = 0; i < n; ++i) { String cur = x[i]; + cur.set_encoding(CE_UTF8); // Ensure UTF-8 encoding out[i] = json_escape(cur); } return out;