diff --git a/NAMESPACE b/NAMESPACE index 1fed441..7a5d4bd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,10 @@ # Generated by roxygen2: do not edit by hand export(apache_httpd_version_history) +export(apple_ios_version_history) +export(complete_semver) export(google_chrome_version_history) -export(is_valid) +export(is_valid_semver) export(lighttpd_version_history) export(memcached_version_history) export(mongodb_version_history) @@ -50,6 +52,7 @@ importFrom(rvest,xml_nodes) importFrom(stringi,stri_count_fixed) importFrom(stringi,stri_detect_fixed) importFrom(stringi,stri_detect_regex) +importFrom(stringi,stri_extract_all_regex) importFrom(stringi,stri_extract_first_regex) importFrom(stringi,stri_match_first_regex) importFrom(stringi,stri_replace_all_fixed) diff --git a/NEWS.md b/NEWS.md index f203403..6ed7383 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ 0.1.0 * Initial release -* Support for Apache httpd, Google Chrome lighttpd, memcached, mongodb, nginx, mysql, - openresty, openssh, sendmail and sqlite \ No newline at end of file +* Support for Apache httpd, Apple iOS, Google Chrome lighttpd, memcached, mongodb, nginx, + mysql, openresty, openssh, sendmail and sqlite \ No newline at end of file diff --git a/R/RcppExports.R b/R/RcppExports.R index 6a6ec6c..b943091 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,11 +1,11 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -#' Test if semantic version strings are valid +#' Test if semantic version strings are valid tri-string #' #' @param v character verctor of version strings #' @export -is_valid <- function(v) { - .Call('_vershist_is_valid', PACKAGE = 'vershist', v) +is_valid_semver <- function(v) { + .Call('_vershist_is_valid_semver', PACKAGE = 'vershist', v) } diff --git a/R/ios.R b/R/ios.R new file mode 100644 index 0000000..bb61745 --- /dev/null +++ b/R/ios.R @@ -0,0 +1,62 @@ +#' Retrieve Apple iOS Version Release History +#' +#' Reads to build a data +#' frame of Apple iOS version release numbers and dates with pseudo-semantic version +#' strings parsed and separate fields added. The data frame is also arranged in +#' order from lowest version to latest version and the `vers` column is an +#' ordered factor. +#' +#' @md +#' @note This does not distinguish by device target and only pulls the first release +#' date and excludes betas. If more granular data is needed, file an issue or PR. +#' @export +apple_ios_version_history <- function() { + + pg <- xml2::read_html("https://en.wikipedia.org/wiki/IOS_version_history") + + vers_nodes <- rvest::html_nodes(pg, xpath=".//th[contains(@id, '.') or contains(., '.')]") + + dplyr::data_frame( + vers = rvest::html_text(vers_nodes), + rls_date = purrr::map_chr( + vers_nodes, + ~rvest::html_node(.x, xpath=".//following-sibling::td[3]") %>% + rvest::html_text() + ) + ) %>% + dplyr::filter(!stri_detect_regex(vers, "Table|Post|Apple")) %>% + dplyr::mutate(vers = stri_replace_all_regex(vers, "\\[.*\\]", "")) -> xdf + + dplyr::filter(xdf, !stri_detect_fixed(vers, "/")) %>% + dplyr::mutate( + rls_date = stri_extract_first_regex( + rls_date, "[[:alpha:]]{2,}[[:space:]]+[[:digit:]]{1,2},[[:space:]]+[[:digit:]]{4}" + ) + ) -> simple + + more_complex <- dplyr::filter(xdf, stri_detect_fixed(vers, "/")) + + c_v <- stri_extract_all_regex(more_complex$vers, "[[:digit:]\\.]+") + c_d <- stri_extract_all_regex(more_complex$rls_date, "[[:alpha:]]{2,}[[:space:]]+[[:digit:]]{1,2},[[:space:]]+[[:digit:]]{4}") + + purrr::map2_df(c_v, c_d, ~{ + dplyr::data_frame( + vers = .x, + rls_date = .y + ) + }) -> more_complex + + dplyr::bind_rows(simple, more_complex) %>% + dplyr::mutate(rls_date = lubridate::mdy(rls_date)) %>% + dplyr::filter(!stri_detect_fixed(vers, "Beta")) %>% + dplyr::mutate( + vers = ifelse(stri_count_fixed(vers, ".") == 1, sprintf("%s.0", vers), vers) + ) %>% + dplyr::bind_cols( + semver::parse_version(.$vers) %>% + dplyr::as_data_frame() + ) %>% + dplyr::arrange(major, minor, patch) %>% + dplyr::mutate(vers = factor(vers, levels = vers)) + +} \ No newline at end of file diff --git a/R/lighttpd.R b/R/lighttpd.R index 724fc83..84207d4 100644 --- a/R/lighttpd.R +++ b/R/lighttpd.R @@ -3,7 +3,7 @@ #' Reads from the `lighttpd` releases and snapshot downloads to build a #' data frame of version release numbers and dates. The caller is responsible #' for extracting out the version components due to the non-standard -#' semantic versioning used. The [is_valid()] function can be used to test the +#' semantic versioning used. The [is_valid_semver()] function can be used to test the #' validity of version strings. #' #' @md diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..37ea05e --- /dev/null +++ b/R/utils.R @@ -0,0 +1,34 @@ +#' Turn partial "valid" semantic version strings into a complete semver-tri or quad strings +#' +#' For MAJOR.MINOR.PATCH (semver-tri), turn `1` into `1.0.0`; `1.1` into `1.1.0`; `1.1.1` +#' into `1.1.1`. For MAJOR.MINOR.PATCH.EXTENSION (semver-quad), turn `1` into `1.0.0.0`; +#' `1.1` into `1.1.0.0`; `1.1.1.0` into `1.1.1.0`. +#' +#' Partial validity checking is performed to test if the input strings contain only +#' digits and periods. "Invalid" input is returned unscathed. +#' +#' @param x a character vector of full or partial version strings +#' @param quad (logical) if `TRUE` then a three-dot semver is returned, else a two-dot semver +#' is returned. Default: `FALSE`. +#' @export +complete_semver <- function(x, quad = FALSE) { + + x <- stri_trim_both(x) + x <- stri_replace_all_regex(x, "(^\\.|\\.$)", "") + + max_dots <- if (quad) 3 else 2 + + purrr::map_chr(x, ~{ + if (stri_detect_regex(.x, "^[[:digit:]\\.]+$")) { + times <- max_dots - stri_count_fixed(.x, ".") + if (times > 0) { + sprintf("%s%s", .x, paste0(rep(".0", times), collapse="")) + } else { + .x + } + } else { + .x + } + }) + +} diff --git a/R/vershist-package.R b/R/vershist-package.R index 8c4d2e5..21f728b 100644 --- a/R/vershist-package.R +++ b/R/vershist-package.R @@ -15,6 +15,7 @@ #' @importFrom stringi stri_replace_all_regex stri_replace_first_fixed stri_trans_tolower #' @importFrom stringi stri_extract_first_regex stri_sub stri_replace_first_regex #' @importFrom stringi stri_replace_all_fixed stri_split_fixed stri_count_fixed stri_trim_both +#' @importFrom stringi stri_extract_all_regex #' @importFrom lubridate year mdy mdy_hms parse_date_time #' @importFrom readr read_lines #' @importFrom utils globalVariables diff --git a/README.Rmd b/README.Rmd index 58ca014..7145b7b 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,5 +1,7 @@ --- output: rmarkdown::github_document +editor_options: + chunk_output_type: console --- # vershist @@ -18,6 +20,7 @@ The following functions are implemented: Core: - `apache_httpd_version_history`: Retrieve Apache httpd Version Release History +- `apple_ios_version_history`: Retrieve Apple iOS Version Release History - `google_chrome_version_history`: Retrieve Google Chrome Version Release History - `lighttpd_version_history`: Retrieve lighttpd Version Release History - `memcached_version_history`: Retrieve memcached Version Release History @@ -31,7 +34,8 @@ Core: Utility: -- `is_valid`: Test if semantic version strings are valid +- `is_valid_semver`: Test if semantic version strings are valid +- `complete_semver`: Turn partial "valid" semantic version strings into a complete semver-tri or quad strings ## Installation @@ -52,12 +56,31 @@ library(vershist) packageVersion("vershist") ``` +Utility + +```{r utility} +versions <- c("steve", "1", "2.1", "3.2.1", "4.3.2.1") + +# Technically, a "valid" semver string is MAJOR.MINOR.PATCH +is_valid_semver(versions) + +complete_semver(versions) + +complete_semver(versions, quad=TRUE) +``` + Apache ```{r apache, cache=TRUE} apache_httpd_version_history() ``` +Apple iOS + +```{r apple_ios, cache=TRUE} +apple_ios_version_history() +``` + Google Chrome ```{r chrome, cache=TRUE} diff --git a/README.md b/README.md index 2b42b7d..6d77a11 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Core: - `apache_httpd_version_history`: Retrieve Apache httpd Version Release History + - `apple_ios_version_history`: Retrieve Apple iOS Version Release + History - `google_chrome_version_history`: Retrieve Google Chrome Version Release History - `lighttpd_version_history`: Retrieve lighttpd Version Release @@ -34,7 +36,9 @@ Core: Utility: - - `is_valid`: Test if semantic version strings are valid + - `is_valid_semver`: Test if semantic version strings are valid + - `complete_semver`: Turn partial “valid” semantic version strings + into a complete semver-tri or quad strings ## Installation @@ -53,6 +57,29 @@ packageVersion("vershist") ## [1] '0.1.0' +Utility + +``` r +versions <- c("steve", "1", "2.1", "3.2.1", "4.3.2.1") + +# Technically, a "valid" semver string is MAJOR.MINOR.PATCH +is_valid_semver(versions) +``` + + ## [1] FALSE TRUE TRUE TRUE FALSE + +``` r +complete_semver(versions) +``` + + ## [1] "steve" "1.0.0" "2.1.0" "3.2.1" "4.3.2.1" + +``` r +complete_semver(versions, quad=TRUE) +``` + + ## [1] "steve" "1.0.0.0" "2.1.0.0" "3.2.1.0" "4.3.2.1" + Apache ``` r @@ -74,6 +101,27 @@ apache_httpd_version_history() ## 10 1.3.14 2000-10-10 2000 1 3 14 "" "" ## # ... with 19 more rows +Apple iOS + +``` r +apple_ios_version_history() +``` + + ## # A tibble: 112 x 7 + ## vers rls_date major minor patch prerelease build + ## + ## 1 1.0.0 2007-06-29 1 0 0 "" "" + ## 2 1.0.1 2007-07-31 1 0 1 "" "" + ## 3 1.0.2 2007-08-21 1 0 2 "" "" + ## 4 1.1.0 2007-09-14 1 1 0 "" "" + ## 5 1.1.1 2007-09-27 1 1 1 "" "" + ## 6 1.1.2 2007-11-12 1 1 2 "" "" + ## 7 1.1.3 2008-01-15 1 1 3 "" "" + ## 8 1.1.4 2008-02-26 1 1 4 "" "" + ## 9 1.1.5 2008-07-15 1 1 5 "" "" + ## 10 2.0.0 2008-07-11 2 0 0 "" "" + ## # ... with 102 more rows + Google Chrome ``` r diff --git a/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.RData b/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.RData new file mode 100644 index 0000000..2484746 Binary files /dev/null and b/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.RData differ diff --git a/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.rdb b/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.rdb new file mode 100644 index 0000000..e69de29 diff --git a/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.rdx b/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.rdx new file mode 100644 index 0000000..868ca76 Binary files /dev/null and b/README_cache/gfm/apple_ios_c87a0ba8187fd58c6318fd9ff44ca2a6.rdx differ diff --git a/man/apple_ios_version_history.Rd b/man/apple_ios_version_history.Rd new file mode 100644 index 0000000..daa4fb5 --- /dev/null +++ b/man/apple_ios_version_history.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ios.R +\name{apple_ios_version_history} +\alias{apple_ios_version_history} +\title{Retrieve Apple iOS Version Release History} +\usage{ +apple_ios_version_history() +} +\description{ +Reads \url{https://en.wikipedia.org/wiki/IOS_version_history"} to build a data +frame of Apple iOS version release numbers and dates with pseudo-semantic version +strings parsed and separate fields added. The data frame is also arranged in +order from lowest version to latest version and the \code{vers} column is an +ordered factor. +} +\note{ +This does not distinguish by device target and only pulls the first release +date and excludes betas. If more granular data is needed, file an issue or PR. +} diff --git a/man/complete_semver.Rd b/man/complete_semver.Rd new file mode 100644 index 0000000..b5f462e --- /dev/null +++ b/man/complete_semver.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{complete_semver} +\alias{complete_semver} +\title{Turn partial "valid" semantic version strings into a complete semver-tri or quad strings} +\usage{ +complete_semver(x, quad = FALSE) +} +\arguments{ +\item{x}{a character vector of full or partial version strings} + +\item{quad}{(logical) if `TRUE` then a three-dot semver is returned, else a two-dot semver +is returned. Default: `FALSE`.} +} +\description{ +For MAJOR.MINOR.PATCH (semver-tri), turn `1` into `1.0.0`; `1.1` into `1.1.0`; `1.1.1` +into `1.1.1`. For MAJOR.MINOR.PATCH.EXTENSION (semver-quad), turn `1` into `1.0.0.0`; +`1.1` into `1.1.0.0`; `1.1.1.0` into `1.1.1.0`. +} +\details{ +Partial validity checking is performed to test if the input strings contain only +digits and periods. "Invalid" input is returned unscathed. +} diff --git a/man/is_valid.Rd b/man/is_valid.Rd deleted file mode 100644 index c456e40..0000000 --- a/man/is_valid.Rd +++ /dev/null @@ -1,14 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/RcppExports.R -\name{is_valid} -\alias{is_valid} -\title{Test if semantic version strings are valid} -\usage{ -is_valid(v) -} -\arguments{ -\item{v}{character verctor of version strings} -} -\description{ -Test if semantic version strings are valid -} diff --git a/man/is_valid_semver.Rd b/man/is_valid_semver.Rd new file mode 100644 index 0000000..1e4e0b9 --- /dev/null +++ b/man/is_valid_semver.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{is_valid_semver} +\alias{is_valid_semver} +\title{Test if semantic version strings are valid tri-string} +\usage{ +is_valid_semver(v) +} +\arguments{ +\item{v}{character verctor of version strings} +} +\description{ +Test if semantic version strings are valid tri-string +} diff --git a/man/lighttpd_version_history.Rd b/man/lighttpd_version_history.Rd index 3cf2db0..cb720ae 100644 --- a/man/lighttpd_version_history.Rd +++ b/man/lighttpd_version_history.Rd @@ -10,6 +10,6 @@ lighttpd_version_history() Reads from the \code{lighttpd} releases and snapshot downloads to build a data frame of version release numbers and dates. The caller is responsible for extracting out the version components due to the non-standard -semantic versioning used. The \code{\link[=is_valid]{is_valid()}} function can be used to test the +semantic versioning used. The \code{\link[=is_valid_semver]{is_valid_semver()}} function can be used to test the validity of version strings. } diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 790bcc9..920cd2a 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -5,20 +5,20 @@ using namespace Rcpp; -// is_valid -std::vector < bool > is_valid(std::vector < std::string > v); -RcppExport SEXP _vershist_is_valid(SEXP vSEXP) { +// is_valid_semver +std::vector < bool > is_valid_semver(std::vector < std::string > v); +RcppExport SEXP _vershist_is_valid_semver(SEXP vSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::vector < std::string > >::type v(vSEXP); - rcpp_result_gen = Rcpp::wrap(is_valid(v)); + rcpp_result_gen = Rcpp::wrap(is_valid_semver(v)); return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { - {"_vershist_is_valid", (DL_FUNC) &_vershist_is_valid, 1}, + {"_vershist_is_valid_semver", (DL_FUNC) &_vershist_is_valid_semver, 1}, {NULL, NULL, 0} }; diff --git a/src/vershist-main.cpp b/src/vershist-main.cpp index ca360e6..b2418b6 100644 --- a/src/vershist-main.cpp +++ b/src/vershist-main.cpp @@ -15,12 +15,12 @@ bool one_is_valid(std::string v) { } -//' Test if semantic version strings are valid +//' Test if semantic version strings are valid tri-string //' //' @param v character verctor of version strings //' @export // [[Rcpp::export]] -std::vector < bool > is_valid(std::vector < std::string > v) { +std::vector < bool > is_valid_semver(std::vector < std::string > v) { std::vector < bool > ret(v.size());