├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── NAMESPACE ├── R ├── clean_build.R ├── headr.R ├── init.R ├── rust.R ├── rustinr-package.R ├── rustr_check.R ├── rustrize.R └── zzz.R ├── README.md ├── README.old.md ├── appveyor.yml ├── inst └── init │ ├── Cargo.toml │ ├── Cargo_deps.toml │ ├── Makevars │ ├── Makevars.bsd │ ├── Makevars.win │ └── lib.rs ├── man ├── RUSTR_TEMP.Rd ├── headr.Rd ├── rust.Rd ├── rustinr-package.Rd ├── rustr_check.Rd ├── rustr_clean_build.Rd ├── rustr_init.Rd └── rustrize.Rd ├── rustinr.Rproj └── tests ├── testthat.R └── testthat └── test-init.r /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^packrat/ 4 | ^\.travis\.yml$ 5 | ^\.dat$ 6 | ^.*\.txt$ 7 | ^.*\.dat$ 8 | ^appveyor\.yml$ 9 | .vscode/ 10 | .Rprofile 11 | ^src/rustlib/target$ 12 | ig.R 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | src/*.o 5 | src/*.so 6 | src/*.dll 7 | inst/doc 8 | .vscode/ 9 | .Rprofile 10 | src/rustlib/target 11 | Cargo.lock 12 | ig.R 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Sample .travis.yml for R projects. 2 | # 3 | # See the r-travis repo and its wiki 4 | # https://github.com/craigcitro/r-travis/wiki 5 | # https://github.com/eddelbuettel/r-travis/ 6 | language: rust 7 | # necessary for `travis-cargo coveralls --no-sudo` 8 | addons: 9 | apt: 10 | packages: 11 | - libcurl4-openssl-dev 12 | - libelf-dev 13 | - libdw-dev 14 | 15 | dist: trusty 16 | 17 | os: 18 | - linux 19 | - osx 20 | 21 | osx_image: xcode7.1 22 | 23 | sudo: required 24 | 25 | env: 26 | global: 27 | - OS=$(uname -s) 28 | - R_BUILD_ARGS="--no-build-vignettes --no-manual" 29 | - R_CHECK_ARGS="--no-vignettes --no-manual" 30 | - BOOTSTRAP_PANDOC="1" 31 | matrix: 32 | - _R_CHECK_CRAN_INCOMING_=FALSE NOT_CRAN=true 33 | - _R_CHECK_CRAN_INCOMING_=TRUE R_CHECK_ARGS="--no-vignettes --no-manual --as-cran" 34 | 35 | before_install: 36 | - echo "options(repos = c(CRAN='http://cran.r-project.org'))" >> ~/.Rprofile 37 | - curl -OL http://raw.github.com/craigcitro/r-travis/master/scripts/travis-tool.sh 38 | - chmod 755 ./travis-tool.sh 39 | - ./travis-tool.sh bootstrap 40 | 41 | install: 42 | - ./travis-tool.sh install_deps 43 | 44 | before_script: 45 | - ./travis-tool.sh dump_sysinfo 46 | 47 | script: 48 | - ./travis-tool.sh run_tests 49 | 50 | after_failure: 51 | - ./travis-tool.sh dump_logs 52 | 53 | after_success: 54 | - ./travis-tool.sh dump_logs 55 | 56 | notifications: 57 | email: 58 | on_success: change 59 | on_failure: change 60 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rustinr 2 | Type: Package 3 | Title: R and Rust Integration 4 | Version: 0.1.0 5 | Author: Qin Wenfeng 6 | Maintainer: Qin Wenfeng 7 | Description: rustr is a Rust library that provides a Rust API to work with R. Write pure Rust code with rustr, and then use rustinr R package to generate Rust interface in R. 8 | License: Apache License 2.0 9 | LazyData: TRUE 10 | SystemRequirements: The Rust package manager Cargo (http://doc.crates.io/) , The Rust compiler rustc (https://www.rust-lang.org/). 11 | RoxygenNote: 5.0.1 12 | Suggests: testthat 13 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(RUSTR_TEMP) 4 | export(headr) 5 | export(rust) 6 | export(rustr_check) 7 | export(rustr_clean_build) 8 | export(rustr_init) 9 | export(rustrize) 10 | importFrom(tools,file_path_sans_ext) 11 | importFrom(utils,package.skeleton) 12 | importFrom(utils,sessionInfo) 13 | -------------------------------------------------------------------------------- /R/clean_build.R: -------------------------------------------------------------------------------- 1 | #' Clean Rust build target 2 | #' 3 | #' \code{rustr_clean_build} cleans the build target of \code{cargo build}. 4 | #' 5 | #' @param pkgdir package path 6 | #' @param lib_only only remove builded static library 7 | #' @export 8 | rustr_clean_build <- function(pkgdir = ".", lib_only = FALSE) { 9 | if(lib_only){ 10 | 11 | static_lib = file.path(pkgdir, "src", "rustlib", "target", "release", "librustlib.a") 12 | if(!dir.exists(static_lib)) { 13 | warning(paste(as.character(static_lib), "does not exist.")) 14 | } 15 | unlink(static_lib) 16 | 17 | } else{ 18 | 19 | target_dir = file.path(pkgdir, "src", "rustlib", "target") 20 | if(!dir.exists(target_dir)) { 21 | warning(paste(as.character(target_dir), "does not exist.")) 22 | } 23 | unlink(target_dir, recursive = T) 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /R/headr.R: -------------------------------------------------------------------------------- 1 | #' Header of Rust file 2 | #' 3 | #' These lines should be in the head of lib.rs file. 4 | #' 5 | #' @examples 6 | #' \dontrun{ 7 | #' 8 | #' #[macro_use] 9 | #' extern crate rustr; 10 | #' pub mod export; 11 | #' pub use rustr::*; 12 | #' 13 | #' // #[rustr_export] 14 | #' pub fn say_hi()->RResult{ 15 | #' Ok("hello world".into()) 16 | #' } 17 | #' } 18 | #' @export 19 | headr = function() { 20 | cat( 21 | '#[macro_use]\nextern crate rustr;\npub mod export;\npub use rustr::*;\n\n// #[rustr_export]\npub fn say_hi()->RResult{\n Ok("hello world".into())\n}' 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /R/init.R: -------------------------------------------------------------------------------- 1 | #' Create a skeleton for a new source package with Rust support 2 | #' 3 | #' Create a skeleton for a new source package with Rust support. 4 | #' 5 | #' There is a folder name \code{rustlib} in the created package \code{src} path. This folder is a rust library which will be compiled and linked by R. Just write Rust code in \code{./src/rustlib/src/} . 6 | #' 7 | #' \code{./src/rustlib/src/export.rs}, \code{./src/REXPORT.c} and \code{./R/REXPORT.R} are generated by \code{rustrize()}, and they should not be edited by hand. Before building the package, run \code{rustrize()} to generate these three files. 8 | #' 9 | #' @param name A character string: the package name and directory name for your package. 10 | #' @param path A path to put the package directory in. 11 | #' @param force See \link[utils]{package.skeleton} 12 | #' @param code_files See \link[utils]{package.skeleton} 13 | #' @param rust_files A character vector with the paths to Rust source files to add to the package. 14 | #' @param author Author of the package. 15 | #' @param maintainer Maintainer of the package. 16 | #' @param email Email of the package maintainer. 17 | #' @param license License of the package. 18 | #' @references 19 | #' Read the \emph{Writing R Extensions} manual for more details. 20 | #' @seealso https://book.rustr.org/create-a-rust-r-package.html 21 | #' @examples 22 | #' \dontrun{ 23 | #' 24 | #' rustr_init("test-pkg") 25 | #' } 26 | #' @export 27 | rustr_init <- function(name, 28 | path = ".", 29 | force = FALSE, 30 | code_files = character(), 31 | rust_files = character(), 32 | author = "Your Name", 33 | maintainer = if (missing(author)) 34 | "Your Name" 35 | else 36 | author, 37 | email = "your@email.com", 38 | license = "MIT") { 39 | if (!is.character(rust_files)) 40 | stop("'rust_files' must be a character vector") 41 | 42 | hienv = new.env(parent = emptyenv()) 43 | hienv$hi = function() { 44 | "hi" 45 | } 46 | tryCatch( 47 | package.skeleton( 48 | name = name, 49 | environment = hienv, 50 | path = path, 51 | force = force, 52 | code_files = code_files 53 | ), 54 | error = function(e) { 55 | stop(sprintf( 56 | "error while calling `package.skeleton` : %s", 57 | conditionMessage(e) 58 | )) 59 | } 60 | ) 61 | 62 | root <- file.path(path, name) 63 | 64 | DESCRIPTION <- file.path(root, "DESCRIPTION") 65 | if (file.exists(DESCRIPTION)) { 66 | x <- read.dcf(DESCRIPTION) 67 | x[, "Author"] <- author 68 | x[, "Maintainer"] <- sprintf("%s <%s>", maintainer, email) 69 | x[, "License"] <- license 70 | x = cbind(x, SystemRequirements = "cargo, rustc") 71 | write.dcf(x, file = DESCRIPTION) 72 | } 73 | 74 | NAMESPACE <- file.path(root, "NAMESPACE") 75 | lines <- readLines(NAMESPACE) 76 | ns <- file(NAMESPACE, open = "w") 77 | if (!grepl("useDynLib", lines)) { 78 | lines <- c(sprintf("useDynLib(%s)", name), lines) 79 | writeLines(lines, con = ns) 80 | message("added useDynLib to NAMESPACE") 81 | } 82 | close(ns) 83 | 84 | 85 | package_help_page <- 86 | file.path(root, "man", sprintf("%s-package.Rd", name)) 87 | if (file.exists(package_help_page)) { 88 | lines <- readLines(package_help_page) 89 | lines <- 90 | gsub("What license is it under?", license, lines, fixed = TRUE) 91 | lines <- 92 | gsub( 93 | "Who to complain to ", 94 | sprintf("%s <%s>", maintainer, email), 95 | lines, 96 | fixed = TRUE 97 | ) 98 | lines <- gsub("Who wrote it", author, lines, fixed = TRUE) 99 | writeLines(lines, package_help_page) 100 | } 101 | 102 | src <- file.path(root, "src") 103 | if (!file.exists(src)) { 104 | dir.create(src) 105 | } 106 | rustlib_src = file.path(root, "src", "rustlib") 107 | if (!file.exists(rustlib_src)) { 108 | dir.create(rustlib_src) 109 | } 110 | rust_src_src = file.path(rustlib_src, "src") 111 | if (!file.exists(rust_src_src)) { 112 | dir.create(rust_src_src) 113 | } 114 | skeleton <- system.file("init", package = "rustinr") 115 | 116 | if (length(rust_files) > 0L) { 117 | for (file in rust_files) { 118 | file.copy(file, src) 119 | message(" >> copied ", file, " to src directory") 120 | } 121 | } 122 | 123 | file.copy(file.path(skeleton, "Makevars.win"), 124 | file.path(src, "Makevars.win")) 125 | if(Sys.info()["sysname"] == "FreeBSD"){ 126 | file.copy(file.path(skeleton, "Makevars.bsd"), 127 | file.path(src, "Makevars")) 128 | } else{ 129 | file.copy(file.path(skeleton, "Makevars"), 130 | file.path(src, "Makevars")) 131 | } 132 | 133 | 134 | file.copy(file.path(skeleton, "Cargo.toml"), 135 | file.path(rustlib_src, "Cargo.toml")) 136 | 137 | file.copy(file.path(skeleton, "lib.rs"), 138 | file.path(rust_src_src, "lib.rs")) 139 | 140 | manu <- file.path(root, "man") 141 | suppressWarnings(file.remove(file.path(manu, "hi.Rd"))) 142 | suppressWarnings(file.remove(file.path(manu, paste0(name,"-package.Rd")))) 143 | rustrize(root) 144 | message("\n") 145 | message("It is recommended to change the rustr version in ./src/rustlib/Cargo.toml from \"*\" to a specific version number to get reproducibility.") 146 | invisible(NULL) 147 | } 148 | -------------------------------------------------------------------------------- /R/rust.R: -------------------------------------------------------------------------------- 1 | #' Source Rust file 2 | #' 3 | #' @param path Rust file path 4 | #' @param code Rust code 5 | #' @param depend Rust dependencies 6 | #' @param rebuild Force rebuild cache 7 | #' @param header Add default Rust header 8 | #' @param env An environment, determining where the export R functions are evaluated 9 | #' 10 | #' @examples 11 | #' \dontrun{ 12 | #' rust(' 13 | #' // #[rustr_export] 14 | #' pub fn say_hi() -> String{ 15 | #' "Hello World".into() 16 | #' } 17 | #' ') 18 | #' 19 | #' rust(code = 'some code', 20 | #' depend = ' 21 | #' [dependencies] 22 | #' rustr = {path = "local/rustr"} 23 | #' extra_dep_on_crates_io = 0.1.1 24 | #' ') 25 | #' 26 | #' } 27 | #' @export 28 | rust <- function(code, path = NULL, depend = NULL, header = TRUE, rebuild = FALSE, env = globalenv()) { 29 | if (!missing(code)) { 30 | file <- tempfile(fileext = ".rs") 31 | con <- file(file, open = "w") 32 | if (header){ 33 | writeLines(c("#[macro_use]", 34 | "extern crate rustr;", 35 | "pub mod export;", 36 | "pub use rustr::*;" 37 | ), con) 38 | } 39 | writeLines(code, con) 40 | close(con) 41 | path2 = normalizePath(file) 42 | if (getOption("verbose")){ 43 | cat(paste0("tempfile : ", path2, "\n")) 44 | } 45 | } else { 46 | stopifnot(!is.null(path)) 47 | path2 = normalizePath(path) 48 | } 49 | 50 | cwd <- getwd() 51 | 52 | if (is.null(RUSTR_TEMP$obj) || rebuild == TRUE) { 53 | rss = random_string() 54 | pathdir = suppressWarnings(normalizePath(file.path(tempdir(), rss))) 55 | RUSTR_TEMP$obj = pathdir 56 | RUSTR_TEMP$rss = rss 57 | reboot = T 58 | rss2 = rss 59 | } else{ 60 | pathdir = RUSTR_TEMP$obj 61 | reboot = F 62 | rss = RUSTR_TEMP$rss 63 | rss2 = random_string() 64 | } 65 | 66 | envRestore = suppressWarnings(setup(file.path(pathdir, "src", "REXPORT.c"))) 67 | tryCatch({ 68 | succeeded <- FALSE 69 | 70 | if (reboot == T) { 71 | if (dir.exists(pathdir)) { 72 | stop(paste("can not create tempdir : ", pathdir)) 73 | } 74 | suppressMessages(rustr_init(rss, path = tempdir())) 75 | suppressWarnings(file.remove(file.path(pathdir, "src", "Makevars.win"))) 76 | suppressWarnings(file.remove(file.path(pathdir, "man", paste0(rss, "-package.Rd")))) 77 | } else{ 78 | x = readLines(file.path(pathdir, "DESCRIPTION")) 79 | x[1] = paste0("Package: ", rss2) 80 | writeLines(x, file.path(pathdir, "DESCRIPTION")) 81 | } 82 | 83 | if (!file.exists(path2)) { 84 | stop(paste("can not find : ", path2)) 85 | } 86 | 87 | file.copy(path2, 88 | file.path(pathdir, "src", "rustlib", "src", "lib.rs"), 89 | overwrite = T) 90 | 91 | if (!is.null(depend)) { 92 | dest = file.path(pathdir, "src", "rustlib", "Cargo.toml") 93 | file.copy( 94 | file.path(system.file("init", package = "rustinr"),"Cargo_deps.toml"), 95 | dest, 96 | overwrite = T) 97 | message("updating cached Cargo.toml.") 98 | destfile = file(dest,"a") 99 | writeLines(depend,destfile) 100 | close(destfile) 101 | if (getOption("verbose")) { 102 | cat(readLines(dest),sep = "\n") 103 | } 104 | } 105 | 106 | rustrize(pathdir) 107 | 108 | setwd(file.path(pathdir, "src", "rustlib")) 109 | cargo = ifelse(Sys.getenv("CARGO_HOME") != "", 110 | Sys.getenv("CARGO_HOME"), 111 | "cargo") 112 | if(.Platform$OS.type == "windows"){ 113 | cmd = paste(cargo, "build --release ") 114 | } else{ 115 | cmd = paste(cargo, "build --release 2>&1") 116 | } 117 | 118 | result <- 119 | suppressWarnings(system(cmd, intern = !getOption("verbose"))) 120 | status <- attr(result, "status") 121 | if (!is.null(status)) { 122 | cat(result, sep = "\n") 123 | succeeded <- FALSE 124 | stop("Error ", 125 | status, 126 | " occurred building shared library.") 127 | } 128 | setwd(file.path(pathdir, "src")) 129 | 130 | linklib = ifelse( 131 | .Platform$OS.type == "windows", 132 | "-lrustlib -lws2_32 -luserenv -lshell32 -ladvapi32", 133 | "-lrustlib" 134 | ) 135 | linksearch = "-Lrustlib/target/release/" 136 | 137 | if (!.callBuildHook(path2, FALSE, 138 | getOption("verbose"))) { 139 | return(invisible(NULL)) 140 | } 141 | 142 | cmd <- paste( 143 | R.home(component = "bin"), 144 | .Platform$file.sep, 145 | "R ", 146 | "CMD SHLIB ", 147 | shQuote(linksearch), 148 | " ", 149 | shQuote(linklib), 150 | " ", 151 | "-o ", 152 | shQuote(paste0(rss2, .Platform$dynlib.ext)), 153 | " ", 154 | 155 | " ", 156 | "", 157 | shQuote("REXPORT.c"), 158 | " ", 159 | sep = "" 160 | ) 161 | 162 | result <- 163 | suppressWarnings(system(cmd, intern = !getOption("verbose"))) 164 | status <- attr(result, "status") 165 | if (!is.null(status)) { 166 | cat(result, sep = "\n") 167 | succeeded <- FALSE 168 | stop("Error ", 169 | status, 170 | " occurred building shared library.") 171 | } 172 | dyn.load(file.path(paste0(rss2, .Platform$dynlib.ext))) 173 | source("../R/REXPORT.R", local = env) 174 | succeeded <- TRUE 175 | }, finally = { 176 | if (!succeeded) 177 | .showBuildFailureDiagnostics() 178 | if(!is.null(cwd)){ 179 | setwd(cwd) 180 | } 181 | restore(envRestore) 182 | if(!is.null(cwd)){ 183 | setwd(cwd) 184 | } 185 | }) 186 | } 187 | 188 | random_string <- function(n = 1, lenght = 12) 189 | { 190 | randomString <- c(1:n) 191 | for (i in 1:n) 192 | { 193 | randomString[i] <- paste(sample(c(letters, LETTERS), 194 | lenght, replace = TRUE), 195 | collapse = "") 196 | } 197 | return(randomString) 198 | } 199 | 200 | ## Learn from Rcpp and Rcpp11 201 | 202 | restore = function(env) { 203 | setVars <- env[!is.na(env)] 204 | if (length(setVars)) 205 | do.call(Sys.setenv, setVars) 206 | removeVars <- names(env[is.na(env)]) 207 | if (length(removeVars)) 208 | Sys.unsetenv(removeVars) 209 | } 210 | 211 | setup = function(sourceFile) { 212 | buildEnv <- list() 213 | 214 | mergeEnv <- function(name, value) { 215 | if (is.null(value) || !nzchar(value)) 216 | return 217 | 218 | if (is.null(buildEnv[[name]])) { 219 | buildEnv[[name]] <<- value 220 | } 221 | else if (!identical(buildEnv[[name]], value)) { 222 | buildEnv[[name]] <<- paste(buildEnv[[name]], value) 223 | 224 | } 225 | 226 | } 227 | 228 | if (length(buildEnv) == 0) { 229 | buildEnv <- list(PKG_LIBS = "") 230 | } 231 | 232 | for (name in names(buildEnv)) 233 | mergeEnv(name, Sys.getenv(name)) 234 | 235 | buildEnv$CYGWIN = "nodosfilewarning" 236 | 237 | if (.Platform$OS.type == "windows" && 238 | !nzchar(Sys.getenv("RSTUDIO"))) { 239 | env <- EnvRtools() 240 | for (var in names(env)) 241 | buildEnv[[var]] <- env[[var]] 242 | } 243 | 244 | restore <- list() 245 | 246 | for (name in names(buildEnv)) 247 | restore[[name]] <- Sys.getenv(name, unset = NA) 248 | 249 | do.call(Sys.setenv, buildEnv) 250 | 251 | return(restore) 252 | } 253 | 254 | build_path = function(path) { 255 | if (.Platform$OS.type == "windows") { 256 | path <- normalizePath(path) 257 | if (grepl(" ", path, fixed = TRUE)) 258 | path <- utils::shortPathName(path) 259 | path <- gsub("\\\\", "/", path) 260 | } 261 | return(path) 262 | } 263 | 264 | 265 | 266 | EnvRtools <- function() { 267 | hasRtools <- 268 | nzchar(Sys.which("ls.exe")) && nzchar(Sys.which("gcc.exe")) 269 | if (!hasRtools) { 270 | key <- NULL 271 | try(key <- utils::readRegistry("SOFTWARE\\R-core\\Rtools", 272 | hive = "HLM", 273 | view = "32-bit"), 274 | silent = TRUE) 275 | 276 | if (!is.null(key)) { 277 | ver <- key$`Current Version` 278 | if (as.numeric(ver) >=3.3) { 279 | rToolsPath <- key$`InstallPath` 280 | if (!is.null(rToolsPath)) { 281 | path <- file.path(rToolsPath, "bin", fsep = "\\") 282 | 283 | if (all(file.exists(path))) { 284 | env <- list() 285 | path <- 286 | paste(path, collapse = .Platform$path.sep) 287 | env$PATH <- 288 | paste(path, 289 | Sys.getenv("PATH"), 290 | sep = .Platform$path.sep) 291 | env$RTOOLS <- .rtoolsPath(rToolsPath) 292 | env$BINPREF <- file.path(env$RTOOLS,"mingw_$(WIN)/bin//",fsep = "/") 293 | return(env) 294 | } 295 | } 296 | } 297 | } 298 | } 299 | 300 | return(NULL) 301 | } 302 | 303 | 304 | .rtoolsPath <- function(path) { 305 | path <- gsub("\\\\", "/", path) 306 | .localsub <- function(re, x) 307 | sub(re, "", x, perl = TRUE) 308 | path <- .localsub("[ \t\r\n]+$", .localsub("^[ \t\r\n]+", path)) 309 | if (substring(path, nchar(path)) != "/") 310 | path <- paste(path, "/", sep = "") 311 | path 312 | } 313 | 314 | .showBuildFailureDiagnostics <- function() { 315 | # RStudio does it's own diagnostics so only do this for other environments 316 | if (nzchar(Sys.getenv("RSTUDIO"))) 317 | return() 318 | 319 | 320 | # if we can't call R CMD SHLIB then notify the user they should 321 | # install the appropriate development tools 322 | if (!.checkDevelTools()) { 323 | msg <- 324 | paste( 325 | "\nWARNING: The tools required to build C++ code for R ", 326 | "were not found.\n\n", 327 | sep = "" 328 | ) 329 | sysName <- Sys.info()[['sysname']] 330 | if (identical(sysName, "Windows")) { 331 | msg <- paste( 332 | msg, 333 | "Please download and install the appropriate ", 334 | "version of Rtools:\n\n", 335 | "http://cran.r-project.org/bin/windows/Rtools/\n", 336 | sep = "" 337 | ) 338 | 339 | 340 | } else if (identical(sysName, "Darwin")) { 341 | msg <- paste( 342 | msg, 343 | "Please install Command Line Tools for XCode ", 344 | "(or equivalent).\n", 345 | sep = "" 346 | ) 347 | } else { 348 | msg <- paste( 349 | msg, 350 | "Please install GNU development tools ", 351 | "including a C++ compiler.\n", 352 | sep = "" 353 | ) 354 | } 355 | message(msg) 356 | } 357 | } 358 | 359 | # check if R development tools are installed (cache successful result) 360 | .hasDevelTools <- FALSE 361 | .checkDevelTools <- function() { 362 | if (!.hasDevelTools) { 363 | # create temp source file 364 | tempFile <- file.path(tempdir(), "foo.c") 365 | cat("void foo() {}\n", file = tempFile) 366 | on.exit(unlink(tempFile)) 367 | 368 | # set working directory to tempdir (revert on exit) 369 | oldDir <- setwd(tempdir()) 370 | on.exit(setwd(oldDir), add = TRUE) 371 | 372 | # attempt the compilation and note whether we succeed 373 | cmd <- 374 | paste(R.home(component = "bin"), 375 | .Platform$file.sep, 376 | "R ", 377 | "CMD SHLIB foo.c", 378 | sep = "") 379 | result <- suppressWarnings(system(cmd, 380 | ignore.stderr = TRUE, 381 | intern = TRUE)) 382 | utils::assignInMyNamespace(".hasDevelTools", is.null(attr(result, "status"))) 383 | 384 | if (.hasDevelTools) { 385 | lib <- file.path(tempdir(), 386 | paste("foo", .Platform$dynlib.ext, sep = '')) 387 | unlink(lib) 388 | } 389 | } 390 | .hasDevelTools 391 | } 392 | 393 | .getHooksList <- function(name) { 394 | hooks <- getHook(name) 395 | if (!is.list(hooks)) 396 | hooks <- list(hooks) 397 | hooks 398 | } 399 | 400 | .callBuildHook <- function(file, fromCode, showOutput) { 401 | for (fun in .getHooksList("sourceCpp.onBuild")) { 402 | if (is.character(fun)) 403 | fun <- get(fun) 404 | 405 | # allow the hook to cancel the build (errors in the hook explicitly 406 | # do not cancel the build since they are unexpected bugs) 407 | continue <- tryCatch( 408 | fun(file, fromCode, showOutput), 409 | error = function(e) 410 | TRUE 411 | ) 412 | 413 | if (!continue) 414 | return (FALSE) 415 | } 416 | 417 | return (TRUE) 418 | } 419 | 420 | 421 | #' Temporary folder for \code{rust()} function 422 | #' 423 | #' \code{rust()} function creates a temporary folder for Rust code generation. Users can get the path to this folder with \code{RUSTR_TEMP$obj}, and get the ramdon string which is the prefix of generated Rust functions with \code{RUSTR_TEMP$rss}. 424 | #' 425 | #' @examples 426 | #' \dontrun{ 427 | #' 428 | #' rustr_check() 429 | #' 430 | #' RUSTR_TEMP$obj 431 | #' 432 | #' list.dirs(RUSTR_TEMP$obj) 433 | #' 434 | #' RUSTR_TEMP$rss 435 | #' 436 | #' readLines(file.path(RUSTR_TEMP$obj, "src", "REXPORT.c")) 437 | #' } 438 | #' @export 439 | RUSTR_TEMP = new.env(parent = emptyenv()) 440 | 441 | RUSTR_TEMP$obj = NULL 442 | -------------------------------------------------------------------------------- /R/rustinr-package.R: -------------------------------------------------------------------------------- 1 | #' Package rustinr 2 | #' 3 | #' \code{rustinr} helps user source Rust script in R and generate the basic struture of a Rust-R package. 4 | #' 5 | #' \code{rust()} creates Rust functions in R console interatively. 6 | #' 7 | #' \code{rustr_init()} creates an R package with Rust support. 8 | #' 9 | #' \code{rustrize()} generates R bindings for Rust functions in a R package. It is similar to \code{compileAttributes()} in Rcpp. 10 | #' 11 | #' \code{headr()} gives you the header of the \code{lib.rs} for the Rust code. 12 | #' 13 | #' \code{rustr_check} checks the status of rustinr, rustc, and cargo installation. 14 | #' 15 | #' @name rustinr-package 16 | NULL 17 | -------------------------------------------------------------------------------- /R/rustr_check.R: -------------------------------------------------------------------------------- 1 | #' @title Check rustinr status 2 | #' @description \code{rustr_check} checks the status of rustinr, rustc, and cargo installation. 3 | #' 4 | #' @param detail print detail info, FALSE by default. 5 | #' 6 | #' @export 7 | rustr_check = function(detail = FALSE) { 8 | origin_verbose = getOption("verbose") 9 | checked = FALSE 10 | res = NULL 11 | tryCatch({ 12 | if(origin_verbose){ 13 | message("Running: library(rustinr)\n") 14 | message('Running: 15 | rust(code = \' 16 | // #[rustr_export] 17 | pub fn say_hi() -> String{ 18 | "Hello World".into() 19 | } 20 | \')\n') 21 | } 22 | 23 | 24 | rust(code = ' 25 | // #[rustr_export] 26 | pub fn say_hi() -> String{ 27 | "Hello World".into() 28 | } 29 | ') 30 | res = eval(quote(say_hi())) 31 | if (is.null(res) || res != "Hello World") 32 | { 33 | checked = TRUE 34 | find_info(res, OK = F) 35 | return(invisible(FALSE)) 36 | } else 37 | { 38 | checked = TRUE 39 | if (detail == T) { 40 | message("\nMore info:\n") 41 | find_info(res) 42 | } 43 | message("\nGreat! It works!") 44 | return(invisible(TRUE)) 45 | } 46 | 47 | }, 48 | finally = { 49 | if (!checked) find_info(res,OK=F) 50 | options(verbose = origin_verbose) 51 | }) 52 | } 53 | 54 | find_info = function(output, OK = T) { 55 | if (is.null(output) || output != "Hello World") { 56 | message("\nSomething is not working correctly. Getting more detail.\n") 57 | } 58 | options(verbose = TRUE) 59 | message('Running:\nlibrary(rustinr)\n') 60 | try(library(rustinr)) 61 | message( 62 | 'Running: 63 | rust(code = \' 64 | // #[rustr_export] 65 | pub fn say_hi() -> String{ 66 | "Hello World".into() 67 | } 68 | \')\n') 69 | message("Running: Rust code parsing") 70 | try(rust(code = ' 71 | // #[rustr_export] 72 | pub fn say_hi() -> String{ 73 | "Hello World".into() 74 | } 75 | ')) 76 | # find cargo 77 | message("\nFind cargo:") 78 | info = try(system("which cargo")) 79 | if (info != 0) { 80 | message("cargo is not in PATH\n") 81 | message("Find CARGO_HOME:") 82 | info_2 = Sys.getenv('CARGO_HOME', unset = NA) 83 | 84 | 85 | if (is.na(info_2)){ 86 | message("CARGO_HOME is not set.\n") 87 | } else{ 88 | message(paste("CARGO_HOME =", info_2,"\n")) 89 | } 90 | } else{ 91 | try(system("cargo --version")) 92 | try(system("rustc --version")) 93 | message("\n") 94 | } 95 | 96 | # R session info 97 | message("sessionInfo:\n") 98 | print(sessionInfo()) 99 | message("\n") 100 | 101 | # check loaded dlls 102 | message("Loaded DLLs:\n") 103 | print(getLoadedDLLs()) 104 | message("\n") 105 | 106 | # Rust temporary source 107 | message("Rust temporary source:") 108 | message(RUSTR_TEMP$obj) 109 | message("\n") 110 | 111 | # Rust build result 112 | message("Rust build result:") 113 | try(print(list.files( 114 | file.path(RUSTR_TEMP$obj, "src/rustlib/target/release") 115 | ))) 116 | message("\n") 117 | 118 | # check lib.rs 119 | message("Check lib.rs:") 120 | try(cat(readLines( 121 | file.path(RUSTR_TEMP$obj, "src/rustlib/src/lib.rs") 122 | ), sep = "\n")) 123 | message("\n") 124 | 125 | # R build result 126 | message("R build result") 127 | try(print(list.files(file.path(RUSTR_TEMP$obj, "src/")))) 128 | if (!OK) { 129 | message("\n") 130 | message("Something is not working correctly. rustr is not ready.") 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /R/rustrize.R: -------------------------------------------------------------------------------- 1 | #' Generate Rust bindings 2 | #' 3 | #' @param pkgdir package path 4 | #' @export 5 | rustrize <- function(pkgdir = ".") { 6 | libpath = "src/rustlib" 7 | pkgdir <- normalizePath(pkgdir, winslash = "/") 8 | descfile <- file.path(pkgdir, "DESCRIPTION") 9 | if (!file.exists(descfile)) 10 | stop("Can not find DESCRIPTION.") 11 | pkgdesc <- read.dcf(descfile)[1,] 12 | rustdir <- file.path(pkgdir, libpath, "src") 13 | cdir <- file.path(pkgdir, "src") 14 | if (!file.exists(rustdir)) 15 | stop("Can not find Rust source path") 16 | if (!file.exists(cdir)) 17 | dir.create(cdir) 18 | rdir <- file.path(pkgdir, "R") 19 | if (!file.exists(rdir)) 20 | dir.create(rdir) 21 | rustfiles1 <- list.files(rustdir, pattern = "\\.rs$") 22 | rustfiles11 <- rustfiles1[rustfiles1 != "export.rs"] 23 | rustfiles2 <- file.path(rustdir, rustfiles11) 24 | rustfiles <- normalizePath(rustfiles2, winslash = "/") 25 | # we now get all the rust files 26 | info = list( 27 | rustfiles = rustfiles, 28 | rdir = rdir, 29 | rustdir = rustdir, 30 | cdir = cdir, 31 | pkgdesc = pkgdesc 32 | ) 33 | 34 | # init function info enviroment 35 | res = new.env(parent = emptyenv()) 36 | res$incomment = F 37 | res$sym = NULL 38 | res$roxlist = list() # simple roxygen comment 39 | res$roxbuffer = character() 40 | 41 | res$funclist = list() 42 | 43 | # get all the information after this line 44 | lapply(info$rustfiles, rtag_one_file_block, res) 45 | 46 | # return(invisible(list(info = info, res = res))) 47 | 48 | # code gen and write 49 | write_rc_file(info, res) 50 | 51 | return(invisible(TRUE)) 52 | } 53 | 54 | write_rc_file = function(info, res) { 55 | cpath = file.path(info$cdir, "REXPORT.c") 56 | rpath = file.path(info$rdir, "REXPORT.R") 57 | rustpath = file.path(info$rustdir, "export.rs") 58 | if (!file.exists(cpath)) 59 | file.create(cpath) 60 | if (!file.exists(rpath)) 61 | file.create(rpath) 62 | if (!file.exists(rustpath)) 63 | file.create(rustpath) 64 | pkgname = info$pkgdesc[names(info$pkgdesc) == "Package"] 65 | funres = new.env(parent = emptyenv()) 66 | funres$c_all = character() 67 | funres$r_all = character() 68 | funres$rust_all = character() 69 | 70 | for (xs in res$funclist) { 71 | gen_fun(xs, pkgname, funres) 72 | } 73 | 74 | cfile = file(cpath, "w") 75 | rfile = file(rpath, "w") 76 | rustfile = file(rustpath, "w") 77 | 78 | tryCatch({ 79 | # c 80 | writeLines(c("// Generated by using rustinr::rustrize() -> do not edit by hand\n"), cfile) 81 | writeLines(c("#include \n#include \n"), cfile) 82 | writeLines(funres$c_all, cfile) 83 | # r 84 | writeLines(c("# Generated by using rustinr::rustrize() -> do not edit by hand\n"), rfile) 85 | writeLines(funres$r_all, rfile) 86 | # rust 87 | writeLines(c("// Generated by using rustinr::rustrize() -> do not edit by hand\n"), rustfile) 88 | writeLines(c("use super::*;\n"), rustfile) 89 | writeLines(funres$rust_all, rustfile) 90 | }, finally = { 91 | close(cfile) 92 | close(rfile) 93 | close(rustfile) 94 | }) 95 | 96 | return(funres) 97 | } 98 | 99 | get_typehead = function(input) { 100 | res = character(length(input)) 101 | 102 | for (xs in 1:length(input)) { 103 | set = FALSE 104 | xsx = strsplit(input[xs], "")[[1]] 105 | for (i in 1:length(xsx)) { 106 | if (xsx[i] == "<") { 107 | res[xs] = substr(input[xs], 1, i - 1) 108 | set = TRUE 109 | break 110 | } 111 | } 112 | if (set == TRUE) 113 | next 114 | res[xs] = input[xs] 115 | } 116 | 117 | return(res) 118 | } 119 | 120 | get_ref_info = function(input){ 121 | input = trimws(input) 122 | 123 | res = character(length(input)) 124 | mut = ref = logical(length(input)) 125 | 126 | for (xs in 1:length(input)) { 127 | if (nchar(input[xs]) > 1 && substr(input[xs],0,1) == "&") { 128 | ref[xs] = TRUE 129 | trim_ref = trimws(substr(input[xs], 2, nchar(input[xs]))) 130 | if ( nchar(trim_ref) > 3 && substr(trim_ref,1,3) == "mut" ) { 131 | mut[xs] = TRUE 132 | res[xs] = substr(trim_ref, 4, nchar(trim_ref)) 133 | } else { 134 | mut[xs] = FALSE 135 | res[xs] = trim_ref 136 | } 137 | } else { 138 | mut[xs] = ref[xs] = FALSE 139 | res[xs] = input[xs] 140 | } 141 | } 142 | list(mut = mut, ref = ref, type = res) 143 | } 144 | 145 | gen_fun = function(funclist, pkgname, funres) { 146 | # new info list 147 | # list( name = funcp, param = paramp, type = typep, ret = retp, rettype = rettype, roxchunk = res$roxbuffer) 148 | if (funclist$ret == TRUE) { 149 | rets = "SEXP" 150 | unwrapr = "unwrapr!( " 151 | rust_tail = "return res_sexp;\n}\n" 152 | } else{ 153 | rets = "SEXP" 154 | unwrapr = "unwrapr_void!( " 155 | rust_tail = "}\n" 156 | } 157 | 158 | if (length(funclist$rettype) != 0 && grepl("(RResult<)|(RR<)",funclist$rettype)){ 159 | throw = TRUE 160 | } else{ 161 | throw = FALSE 162 | } 163 | 164 | # head of c file extern rustr_func_name( 165 | extern_head = sprintf("extern %s rustr_%s(", rets, funclist$name) 166 | 167 | # head of c file call rustr_func_name( 168 | extern_call = sprintf("rustr_%s(", funclist$name) 169 | if (is.null(funclist$param) || length(funclist$param) ==0 ) { 170 | extern_param = "" 171 | rust_param = "" 172 | rust_param_call = "" 173 | rust_let = "" 174 | } 175 | else { 176 | extern_param = paste(paste("SEXP", funclist$param), 177 | collapse = ", ", 178 | sep = " ") 179 | # rustr_name(a : SEXP, b : SEXP) -> SEXP or void 180 | rust_param = paste(paste(paste(funclist$param, "SEXP", sep = " : "), collapse = ", ")) 181 | 182 | # [1] "let a_ : NumVec = unwrapr!( a.fromr() );\n" 183 | # [2] "let b_ : Int = unwrapr!( b.fromr() );\n" 184 | ref_info = get_ref_info(funclist$type) 185 | 186 | # name(a_,b_) 187 | param_ = paste(funclist$param, "_", sep = "") 188 | mut_param = funclist$param 189 | 190 | for (xs in 1:length(param_)) { 191 | if (ref_info[["ref"]][xs] == TRUE){ 192 | if (ref_info[["mut"]][xs] == TRUE){ 193 | param_[xs] = paste("&mut", param_[xs]) 194 | mut_param[xs] = paste("mut", mut_param[xs]) 195 | } else { 196 | param_[xs] = paste("&", param_[xs]) 197 | } 198 | } 199 | } 200 | 201 | rust_param_call = paste(param_, sep = " ", collapse = ",") 202 | 203 | typehead = get_typehead(ref_info$type) 204 | rust_let = paste( 205 | "let ", 206 | mut_param, 207 | "_ : ", 208 | ref_info$type, 209 | " = ", 210 | unwrapr 211 | , 212 | typehead, 213 | "::rnew(", 214 | funclist$param, 215 | ") );\n", 216 | sep = "" 217 | ) 218 | } 219 | 220 | # handle rust code gen 221 | 222 | if (funclist$ret == FALSE) { 223 | # name(a_,b_); call 224 | rust_res = paste0(funclist$name, "(", rust_param_call, ");\n\n") 225 | rust_head = sprintf( 226 | "#[no_mangle]\npub extern \"C\" fn rustr_%s(%s){\n\n", 227 | funclist$name, 228 | rust_param 229 | ) 230 | } else{ 231 | if (throw == TRUE){ 232 | rust_res = sprintf( 233 | "let res = %s%s(%s));\n\n let res_sexp : SEXP = unwrapr!(res.intor());\n\n", 234 | unwrapr, 235 | funclist$name, 236 | rust_param_call 237 | ) 238 | } else{ # not throw 239 | rust_res = sprintf( 240 | "let res = %s(%s);\n\n let res_sexp : SEXP = unwrapr!(res.intor());\n\n", 241 | funclist$name, 242 | rust_param_call 243 | ) 244 | } 245 | rust_head = sprintf( 246 | "#[no_mangle]\npub extern \"C\" fn rustr_%s(%s)->SEXP{\n\n", 247 | funclist$name, 248 | rust_param 249 | ) 250 | } 251 | 252 | rust_all = paste(rust_head, 253 | paste(rust_let, collapse = "\n"), 254 | rust_res, 255 | rust_tail, 256 | collapse = "\n\n") 257 | if (getOption("verbose")) { 258 | cat(rust_all) 259 | } 260 | # c code gen 261 | 262 | extern = sprintf("%s%s);", extern_head, extern_param) 263 | 264 | cfun_head = sprintf("%s %s_%s(", rets, pkgname, funclist$name) 265 | c_param = paste(funclist$param, collapse = ",") 266 | 267 | # c r code gen 268 | 269 | if (is.null(funclist$param) || length(funclist$param) == 0) { 270 | c_param = "" 271 | if (funclist$ret == TRUE && 272 | funclist$rettype != "RResult<()>") { 273 | r_head = sprintf( 274 | "%s = function(%s){ .Call('%s_%s',PACKAGE = '%s')}", 275 | funclist$name, 276 | c_param, 277 | pkgname, 278 | funclist$name, 279 | pkgname 280 | ) 281 | } else{ 282 | r_head = sprintf( 283 | "%s = function(%s){ invisible(.Call('%s_%s',PACKAGE = '%s'))}", 284 | funclist$name, 285 | c_param, 286 | pkgname, 287 | funclist$name, 288 | pkgname 289 | ) 290 | 291 | } 292 | } 293 | else { 294 | c_param = paste(funclist$param, collapse = ",") 295 | if (funclist$ret == TRUE && 296 | funclist$rettype != "RResult<()>") { 297 | r_head = sprintf( 298 | "%s = function(%s){ .Call('%s_%s',PACKAGE = '%s', %s)}", 299 | funclist$name, 300 | c_param, 301 | pkgname, 302 | funclist$name, 303 | pkgname, 304 | c_param 305 | ) 306 | } else{ 307 | r_head = sprintf( 308 | "%s = function(%s){ invisible(.Call('%s_%s',PACKAGE = '%s', %s))}", 309 | funclist$name, 310 | c_param, 311 | pkgname, 312 | funclist$name, 313 | pkgname, 314 | c_param 315 | ) 316 | } 317 | } 318 | 319 | if (is.null(funclist$roxchunk)) 320 | r_all = r_head 321 | else 322 | r_all = paste(paste(funclist$roxchunk,collapse = "\n"), 323 | r_head, 324 | sep = "\n", 325 | collapse = "\n") 326 | 327 | if (funclist$ret == TRUE) { 328 | c_fun = sprintf("%s%s){ return(%s%s));}", 329 | cfun_head, 330 | extern_param, 331 | extern_call, 332 | c_param) 333 | c_all = paste(extern, c_fun, sep = "\n", collapse = "\n") 334 | } else{ 335 | c_fun = sprintf( 336 | "%s%s){ %s%s);return(R_NilValue);}", 337 | cfun_head, 338 | extern_param, 339 | extern_call, 340 | c_param 341 | ) 342 | c_all = paste(extern, c_fun, sep = "\n", collapse = "\n") 343 | } 344 | 345 | 346 | 347 | 348 | funres$r_all = append(funres$r_all, r_all) 349 | funres$c_all = append(funres$c_all, c_all) 350 | funres$rust_all = append(funres$rust_all, rust_all) 351 | } 352 | 353 | isroxygen = function(strings) { 354 | len = nchar(strings[1]) 355 | if (len < 3) 356 | return(FALSE) 357 | idx = as.numeric(regexec("\\S", strings[1])[[1]]) 358 | if (idx == -1) 359 | return(FALSE) 360 | spstr = strsplit(strings[1], "")[[1]] 361 | if (length(spstr) < idx + 3) 362 | return(FALSE) 363 | if (substr(strings[1], idx, idx + 3) == "// \'") { 364 | return(TRUE) 365 | 366 | } 367 | return(FALSE) 368 | } 369 | 370 | # /**// // 371 | # strip_trailing_comments(c("// sdsdd // sdsd","hj", "//@ ")) 372 | strip_trailing_comments = function(strings, trail = TRUE) { 373 | res = vector("character", length(strings)) 374 | for (x in 1:length(strings)) { 375 | tmp = strings[x] 376 | if (tmp == "") { 377 | res[x] = tmp 378 | next 379 | 380 | } 381 | if (isroxygen(tmp)) { 382 | res[x] = tmp 383 | next 384 | 385 | } 386 | instring = FALSE 387 | idx = as.numeric(regexec("\\S", tmp)[[1]]) 388 | if (idx == -1) { 389 | res[x] = tmp 390 | next 391 | 392 | } 393 | len = nchar(tmp) 394 | spstr = strsplit(tmp, "")[[1]] 395 | 396 | if (trail == TRUE) { 397 | if (idx + 1 < len && 398 | spstr[idx] == '/' && spstr[idx + 1] == '/') { 399 | idx = idx + 2 400 | 401 | } 402 | } 403 | 404 | set = FALSE 405 | lastch = 0 406 | while (idx < len) { 407 | if (instring) { 408 | if (spstr[idx] == '"' && spstr[idx - 1] != '\\') { 409 | instring = FALSE 410 | 411 | } 412 | } else { 413 | if (spstr[idx] == '"') { 414 | instring = TRUE 415 | 416 | } 417 | } 418 | 419 | if (!instring && 420 | spstr[idx] == '/' && 421 | spstr[idx + 1] == '/' && lastch != "*") { 422 | res[x] = substr(tmp, 1, idx - 1) 423 | 424 | set = TRUE 425 | break 426 | 427 | } 428 | idx = idx + 1 429 | 430 | lastch = spstr[idx] 431 | } # end while 432 | if (set == TRUE) { 433 | next 434 | } else { 435 | res[x] = tmp 436 | 437 | next 438 | 439 | } 440 | } 441 | return(res) 442 | } 443 | 444 | check_comment = function(line, res) { 445 | pos = 1 446 | nc = nchar(line[1]) 447 | while (pos < nc && pos != -1) { 448 | # check for a // 449 | linecommentpos = regexec("//", line)[[1]] 450 | 451 | # look for the next token 452 | token = ifelse(res$incomment, "\\*/", "/\\*") 453 | pp = regexec(token, substr(line, pos, nchar(line)))[[1]] 454 | pos = pos + pp - 1 455 | 456 | 457 | # process the comment token 458 | if (pp != -1) { 459 | # break if the line comment precedes the comment token 460 | if (linecommentpos != -1 && linecommentpos < pos) 461 | break 462 | 463 | res$incomment = !res$incomment 464 | pos = pos + nchar(token) 465 | 466 | } 467 | } 468 | } 469 | 470 | parse_def = function(linenum, res, content) { 471 | # Look for the signature termination ({ or ; not inside quotes) 472 | # on this line and then subsequent lines if necessary 473 | signature = character() 474 | for (i in linenum:length(content)) { 475 | line = content[i] 476 | 477 | if (line == "") 478 | next 479 | insidequotes = FALSE 480 | 481 | prevchar = 0 482 | 483 | nc = nchar(line) 484 | spstr = strsplit(line, "")[[1]] 485 | # scan for { or ; not inside quotes 486 | for (xs in 1:nc) { 487 | ch = spstr[xs] 488 | # update quotes state 489 | if (ch == '"' && prevchar != '\\') 490 | insidequotes = !insidequotes 491 | 492 | # found signature termination, append and return 493 | if (!insidequotes && ((ch == '{') || (ch == ';'))) { 494 | signature = append(signature, substr(line, 0, xs - 1)) 495 | 496 | return(signature) 497 | } 498 | # record prev char (used to check for escaped quote i.e. \") 499 | prevchar = ch 500 | 501 | } 502 | # if we didn't find a terminator on this line then just append the line 503 | # and move on to the next line 504 | signature = append(signature, line) 505 | 506 | } 507 | 508 | return(character()) 509 | 510 | } 511 | 512 | parse_param_with_comma = function(string) { 513 | # string = "HashMap,b" 514 | nc = nchar(string) 515 | split_string = strsplit(string, split = "")[[1]] 516 | xs = nc + 1 517 | for (x in nc:1) { 518 | if (split_string[x] == ",") { 519 | xs = x 520 | break 521 | 522 | } 523 | } 524 | 525 | if (xs == nc + 1) { 526 | warning(paste("parsing parameter fail: ", string)) 527 | } 528 | 529 | c( 530 | type_ = substr(string, start = 1, stop = xs - 1), 531 | name_ = substr(string, start = xs + 1, stop = nc) 532 | ) 533 | } 534 | 535 | parse_func_inner = function(linenum, res, content) { 536 | def = strip_trailing_comments(parse_def(linenum, res, content), FALSE) 537 | if (is.null(def)) { 538 | warning(paste("no function found1: line ", linenum, content[linenum])) 539 | return(NULL) 540 | } 541 | # remove block comment 542 | defs = gsub("\\/\\*.*\\*\\/", "", paste(trimws(def), sep = " ", collapse = "")) 543 | 544 | endparenloc = regexec('\\)', defs)[[1]] 545 | beginparenloc = regexec('\\(', defs)[[1]] 546 | 547 | if (endparenloc == -1 || 548 | beginparenloc == -1 || 549 | endparenloc < beginparenloc) { 550 | warning(paste("no function found2: line ", linenum, content[linenum])) 551 | return(NULL) 552 | } 553 | 554 | # func name 555 | mfuncp = regexpr("pub\\s*fn\\s*(?\\w*)\\(", defs, perl = TRUE) 556 | funcp = parse.one(defs, mfuncp)[[1]] 557 | 558 | 559 | if (is.null(funcp) || is.na(funcp) || funcp == "") { 560 | warning( 561 | paste( 562 | "function mark as exported, but not included in export.rs", 563 | defs, 564 | sep = "\n" 565 | ) 566 | ) 567 | return(NULL) 568 | } 569 | 570 | # namep = re2_match("(\\s*\\w+\\s*:\\s*\\w+\\s*)",defs, value = TRUE, all= TRUE)[[2]] 571 | 572 | # defs = "pub fn map64(a:HashMap,b:HashMap)->RResult>" 573 | # 574 | # "a:HashMap,b:HashMap" 575 | # parampart = re2_match("\((.*)\)(?!>)*", defs , value = TRUE)[[1]] 576 | m = regexpr("\\((.*)\\)(?!>)", defs, perl = TRUE) 577 | parampart = regmatches(defs, m)[[1]][1] 578 | parampart = substr(parampart, 2, nchar(parampart) - 1) 579 | 580 | if (getOption("verbose")) { 581 | cat(paste0("def : ", defs, "\nparameter : \n")) 582 | print(parampart) 583 | cat("\n\n") 584 | } 585 | # paramp and typep 586 | if (is.na(parampart) || parampart == "") { 587 | paramp = NULL 588 | typep = NULL 589 | } 590 | else{ 591 | # "a","HashMap,b","HashMap" 592 | namep = strsplit(parampart, ":")[[1]] 593 | 594 | if (length(namep) <= 2) { 595 | if (length(namep) < 2) { 596 | warning(paste("failed to parse parameter: ", is.na(parampart))) 597 | } else{ 598 | split_namep = list(namep) 599 | } 600 | 601 | } else{ 602 | split_namep = vector("list", length = length(namep) - 1) 603 | part1 = parse_param_with_comma(namep[2]) 604 | split_namep[[1]] = c(namep[1], part1[1]) 605 | 606 | # handle middle of param list 607 | if (length(namep) >= 4) { 608 | for (ii in 2:(length(namep) - 2)) { 609 | part_front = parse_param_with_comma(namep[ii]) 610 | part_back = parse_param_with_comma(namep[ii + 1]) 611 | split_namep[[ii]] = c(part_front[2], part_back[1]) 612 | } # end for 613 | } # end length(namep)>4 614 | 615 | # handle end of param list 616 | partend = parse_param_with_comma(namep[length(namep) - 1]) 617 | split_namep[[length(namep) - 1]] = 618 | c(partend[2], 619 | namep[length(namep)]) 620 | } 621 | 622 | 623 | # [[1]] 624 | # [1] "sd" "aa" 625 | 626 | # [[2]] 627 | # [1] "sd" "ws" 628 | #split_namep = lapply(strsplit(namep,":"), trimws) 629 | 630 | paramp = character(length = length(split_namep)) 631 | typep = character(length = length(split_namep)) 632 | for (xs in 1:length(split_namep)) { 633 | paramp[xs] = split_namep[[xs]][1] 634 | typep[xs] = split_namep[[xs]][2] 635 | } 636 | } 637 | 638 | if (grepl("\\)\\s*->\\s*(.*)", defs)) { 639 | mrettype = regexpr("\\)\\s*->\\s*(?.*)", defs, perl = T) 640 | rettype = parse.one(defs, mrettype)[[1]] 641 | retp = TRUE 642 | } else if (grepl("\\)\\s*$", defs)) { 643 | rettype = NULL 644 | retp = FALSE 645 | } else{ 646 | warning(paste("can not match any pattern : ", defs)) 647 | return(NULL) 648 | } 649 | # if(is.na(namep[1])){ 650 | # paramp = NULL 651 | # } 652 | 653 | func = list( 654 | name = trimws(funcp), 655 | param = trimws(paramp), 656 | type = trimws(typep), 657 | ret = trimws(retp), 658 | rettype = trimws(rettype), 659 | roxchunk = res$roxbuffer 660 | ) 661 | res$roxbuffer = character() 662 | return(func) 663 | # list( name = "func_name", param = c("param1","param2"), ret = TRUE) 664 | } 665 | 666 | parse_func = function(linenum, res, content) { 667 | if ((linenum + 1) <= length(content)) { 668 | func = parse_func_inner(linenum + 1, res, content) 669 | if (getOption("verbose")) { 670 | cat("funcion information :\n") 671 | print(func) 672 | cat("\n") 673 | } 674 | if (!is.null(func)) 675 | res$funclist = append(res$funclist, list(func)) 676 | } 677 | else { 678 | warning(paste("no function found3: line ", linenum, content[linenum])) 679 | } 680 | } 681 | 682 | rtag_one_file_block = function(filename, res) { 683 | # export pattern 684 | export_tag = "^\\s*\\/\\/\\s*\\#\\[rustr_export\\]\\s*" 685 | content = suppressWarnings(strip_trailing_comments(readLines(filename))) 686 | # get all no mangle start line 687 | # start_line = (1:length(content))[re2_match(no_mangle_tag, content)] 688 | # sizes = length(start_line) 689 | 690 | # res = new.env(parent = emptyenv()) 691 | res$incomment = F 692 | res$sym = NULL 693 | # res$roxlist = list() # simple roxygen comment 694 | # res$roxbuffer = character() 695 | 696 | # res$funclist = list() # func with roxygen comment 697 | # list( name = "func_name", param = c("param1","param2"), ret = TRUE) 698 | 699 | for (xs in 1:length(content)) { 700 | line = content[xs] 701 | 702 | check_comment(line, res) 703 | 704 | if (res$incomment) 705 | next 706 | 707 | if (grepl(export_tag, content[xs])) { 708 | parse_func(xs, res, content) 709 | } else { 710 | # a rox comment 711 | if ((regexec('// \'', line)[[1]] == 1) == TRUE) { 712 | roxline = paste("#'" , substr(line, 5, nchar(line)), sep = "") 713 | 714 | res$roxbuffer = append(res$roxbuffer, roxline) 715 | } else { 716 | # a non-roxygen line causes us to clear the roxygen buffer 717 | #print(res$roxbuffer) 718 | #print(is.null(res$roxbuffer)) 719 | if (!is.null(res$roxbuffer)) { 720 | # push back a chunck of roxygen comment 721 | res$roxlist = append(res$roxlist, list(res$roxbuffer)) 722 | # reset buffer 723 | res$roxbuffer = character() 724 | } 725 | } 726 | } 727 | } 728 | return(res) 729 | } 730 | 731 | parse.one <- function(res, result) { 732 | m <- do.call(rbind, lapply(seq_along(res), function(i) { 733 | if (result[i] == -1) 734 | return("") 735 | st <- attr(result, "capture.start")[i,] 736 | substring(res[i], st, st + attr(result, "capture.length")[i,] - 1) 737 | })) 738 | colnames(m) <- attr(result, "capture.names") 739 | m 740 | } 741 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | #' @importFrom tools file_path_sans_ext 2 | #' @importFrom utils package.skeleton 3 | #' @importFrom utils sessionInfo 4 | NULL -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | Here are two new projects for Rust and R integration: 4 | 5 | ### extendr 6 | 7 | https://github.com/extendr 8 | 9 | ### r-rust 10 | 11 | https://github.com/r-rust 12 | 13 | https://jeroen.github.io/erum2018/ 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.old.md: -------------------------------------------------------------------------------- 1 | ## Rust and R Integration 2 | 3 | [![Build Status](https://travis-ci.org/rustr/rustinr.svg?branch=master)](https://travis-ci.org/rustr/rustinr) 4 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/re2r)](http://cran.r-project.org/package=rustinr) 5 | 6 | Will try CRANing when R 3.3.0 release. 7 | 8 | `rustr` is a Rust library that provides a Rust API to work with R. 9 | 10 | Write pure Rust code with `rustr`, and then use `rustinr` R package to generate Rust interfaces to R. 11 | 12 | More info: https://rustr.github.io https://rustr.github.io/book 13 | 14 | This project is now under construction. Issues and Pull requests are welcome! 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # DO NOT CHANGE the "init" and "install" sections below 2 | 3 | # Download script file from GitHub 4 | init: 5 | ps: | 6 | $ErrorActionPreference = "Stop" 7 | Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" 8 | Import-Module '..\appveyor-tool.ps1' 9 | 10 | install: 11 | - ps: Bootstrap 12 | - ps: InstallRtools 13 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe" 14 | - rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Rust32" 15 | - md C:\Rust\lib\rustlib\i686-pc-windows-gnu 16 | - xcopy C:\Rust32\lib\rustlib\i686-pc-windows-gnu C:\Rust\lib\rustlib\i686-pc-windows-gnu /s /e /h 17 | - rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Rust32" 18 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-x86_64-pc-windows-gnu.exe" 19 | - rust-nightly-x86_64-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Rust" 20 | - del rust-nightly-i686-pc-windows-gnu.exe 21 | - del rust-nightly-x86_64-pc-windows-gnu.exe 22 | - SET PATH=%PATH%;C:\Rust\bin 23 | # Adapt as necessary starting from here 24 | 25 | environment: 26 | matrix: 27 | - R_VERSION: devel 28 | R_ARCH: i386 29 | GCC_PATH: mingw_32 30 | channel: nightly 31 | NOT_CRAN: true 32 | 33 | - R_VERSION: devel 34 | R_ARCH: x64 35 | GCC_PATH: mingw_64 36 | channel: nightly 37 | NOT_CRAN: true 38 | 39 | build_script: 40 | - travis-tool.sh install_deps 41 | 42 | test_script: 43 | - travis-tool.sh run_tests 44 | 45 | on_failure: 46 | - travis-tool.sh dump_logs 47 | - 7z a failure.zip *.Rcheck\* 48 | - appveyor PushArtifact failure.zip 49 | 50 | artifacts: 51 | - path: '*.Rcheck\**\*.log' 52 | name: Logs 53 | 54 | - path: '*.Rcheck\**\*.out' 55 | name: Logs 56 | 57 | - path: '*.Rcheck\**\*.fail' 58 | name: Logs 59 | 60 | - path: '*.Rcheck\**\*.Rout' 61 | name: Logs 62 | 63 | - path: '\*_*.tar.gz' 64 | name: Bits 65 | 66 | - path: '\*_*.zip' 67 | name: Bits 68 | -------------------------------------------------------------------------------- /inst/init/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustlib" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | rustr = "*" 7 | 8 | [lib] 9 | name = "rustlib" 10 | crate-type = ["staticlib","rlib"] 11 | -------------------------------------------------------------------------------- /inst/init/Cargo_deps.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustlib" 3 | version = "0.1.0" 4 | 5 | [lib] 6 | name = "rustlib" 7 | crate-type = ["staticlib","rlib"] 8 | -------------------------------------------------------------------------------- /inst/init/Makevars: -------------------------------------------------------------------------------- 1 | ifneq "$(strip $(CARGO_HOME))" "" 2 | CG="$(CARGO_HOME)" 3 | else 4 | CG=cargo 5 | endif 6 | 7 | .PHONY: all 8 | 9 | # to make things 10 | all: $(SHLIB) 11 | 12 | # cargo build --release folder 13 | $(SHLIB): rustlib/target/release/librustlib.rlib 14 | 15 | # run cargo 16 | rustlib/target/release/librustlib.rlib: rustlib/src/*.rs 17 | cd rustlib && $(CG) build --release 18 | 19 | # -lws2_32 -luserenv -ladvapi32 is needed for linking on windows 20 | PKG_LIBS= $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -L. -Lrustlib/target/release/ -lrustlib 21 | -------------------------------------------------------------------------------- /inst/init/Makevars.bsd: -------------------------------------------------------------------------------- 1 | CG=cargo 2 | 3 | .PHONY: all 4 | 5 | # to make things 6 | all: $(SHLIB) 7 | 8 | # cargo build --release folder 9 | $(SHLIB): rustlib/target/release/librustlib.rlib 10 | 11 | # run cargo 12 | rustlib/target/release/librustlib.rlib: rustlib/src/*.rs 13 | cd rustlib && $(CG) build --release 14 | 15 | # -lws2_32 -luserenv -ladvapi32 is needed for linking on windows 16 | PKG_LIBS= $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -L. -Lrustlib/target/release/ -lrustlib 17 | -------------------------------------------------------------------------------- /inst/init/Makevars.win: -------------------------------------------------------------------------------- 1 | ifneq "$(strip $(CARGO_HOME))" "" 2 | CG=$(CARGO_HOME) 3 | else 4 | CG=cargo 5 | endif 6 | 7 | ifeq "$(WIN)" "64" 8 | RUST_ARCH=x86_64-pc-windows-gnu 9 | else 10 | RUST_ARCH=i686-pc-windows-gnu 11 | endif 12 | 13 | .PHONY: all 14 | 15 | # to make things 16 | all: $(SHLIB) 17 | 18 | # cargo build --release folder 19 | $(SHLIB): rustlib/target/$(RUST_ARCH)/release/librustlib.rlib 20 | 21 | # run cargo 22 | rustlib/target/$(RUST_ARCH)/release/librustlib.rlib: rustlib/src/*.rs 23 | cd rustlib && $(CG) build --release --target $(RUST_ARCH) 24 | 25 | # -lws2_32 -lshell32 -luserenv -ladvapi32 is needed for linking on windows 26 | PKG_LIBS= $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -L. -Lrustlib/target/$(RUST_ARCH)/release/ -lrustlib -lws2_32 -luserenv -lshell32 -ladvapi32 27 | -------------------------------------------------------------------------------- /inst/init/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate rustr; 3 | pub mod export; 4 | pub use rustr::*; 5 | 6 | // #[rustr_export] 7 | pub fn say_hi()->RResult{ 8 | Ok("hello world".into()) 9 | } 10 | -------------------------------------------------------------------------------- /man/RUSTR_TEMP.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rust.R 3 | \docType{data} 4 | \name{RUSTR_TEMP} 5 | \alias{RUSTR_TEMP} 6 | \title{Temporary folder for \code{rust()} function} 7 | \format{An object of class \code{environment} of length 1.} 8 | \usage{ 9 | RUSTR_TEMP 10 | } 11 | \description{ 12 | \code{rust()} function creates a temporary folder for Rust code generation. Users can get the path to this folder with \code{RUSTR_TEMP$obj}, and get the ramdon string which is the prefix of generated Rust functions with \code{RUSTR_TEMP$rss}. 13 | } 14 | \examples{ 15 | \dontrun{ 16 | 17 | rustr_check() 18 | 19 | RUSTR_TEMP$obj 20 | 21 | list.dirs(RUSTR_TEMP$obj) 22 | 23 | RUSTR_TEMP$rss 24 | 25 | readLines(file.path(RUSTR_TEMP$obj, "src", "REXPORT.c")) 26 | } 27 | } 28 | \keyword{datasets} 29 | 30 | -------------------------------------------------------------------------------- /man/headr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/headr.R 3 | \name{headr} 4 | \alias{headr} 5 | \title{Header of Rust file} 6 | \usage{ 7 | headr() 8 | } 9 | \description{ 10 | These lines should be in the head of lib.rs file. 11 | } 12 | \examples{ 13 | \dontrun{ 14 | 15 | #[macro_use] 16 | extern crate rustr; 17 | pub mod export; 18 | pub use rustr::*; 19 | 20 | // #[rustr_export] 21 | pub fn say_hi()->RResult{ 22 | Ok("hello world".into()) 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /man/rust.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rust.R 3 | \name{rust} 4 | \alias{rust} 5 | \title{Source Rust file} 6 | \usage{ 7 | rust(code, path = NULL, depend = NULL, header = TRUE, rebuild = FALSE, 8 | env = globalenv()) 9 | } 10 | \arguments{ 11 | \item{code}{Rust code} 12 | 13 | \item{path}{Rust file path} 14 | 15 | \item{depend}{Rust dependencies} 16 | 17 | \item{header}{Add default Rust header} 18 | 19 | \item{rebuild}{Force rebuild cache} 20 | 21 | \item{env}{An environment, determining where the export R functions are evaluated} 22 | } 23 | \description{ 24 | Source Rust file 25 | } 26 | \examples{ 27 | \dontrun{ 28 | rust(' 29 | // #[rustr_export] 30 | pub fn say_hi() -> String{ 31 | "Hello World".into() 32 | } 33 | ') 34 | 35 | rust(code = 'some code', 36 | depend = ' 37 | [dependencies] 38 | rustr = {path = "local/rustr"} 39 | extra_dep_on_crates_io = 0.1.1 40 | ') 41 | 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /man/rustinr-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rustinr-package.R 3 | \name{rustinr-package} 4 | \alias{rustinr-package} 5 | \title{Package rustinr} 6 | \description{ 7 | \code{rustinr} helps user source Rust script in R and generate the basic struture of a Rust-R package. 8 | } 9 | \details{ 10 | \code{rust()} creates Rust functions in R console interatively. 11 | 12 | \code{rustr_init()} creates an R package with Rust support. 13 | 14 | \code{rustrize()} generates R bindings for Rust functions in a R package. It is similar to \code{compileAttributes()} in Rcpp. 15 | 16 | \code{headr()} gives you the header of the \code{lib.rs} for the Rust code. 17 | 18 | \code{rustr_check} checks the status of rustinr, rustc, and cargo installation. 19 | } 20 | 21 | -------------------------------------------------------------------------------- /man/rustr_check.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rustr_check.R 3 | \name{rustr_check} 4 | \alias{rustr_check} 5 | \title{Check rustinr status} 6 | \usage{ 7 | rustr_check(detail = FALSE) 8 | } 9 | \arguments{ 10 | \item{detail}{print detail info, FALSE by default.} 11 | } 12 | \description{ 13 | \code{rustr_check} checks the status of rustinr, rustc, and cargo installation. 14 | } 15 | 16 | -------------------------------------------------------------------------------- /man/rustr_clean_build.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_build.R 3 | \name{rustr_clean_build} 4 | \alias{rustr_clean_build} 5 | \title{Clean Rust build target} 6 | \usage{ 7 | rustr_clean_build(pkgdir = ".", lib_only = FALSE) 8 | } 9 | \arguments{ 10 | \item{pkgdir}{package path} 11 | 12 | \item{lib_only}{only remove builded static library} 13 | } 14 | \description{ 15 | \code{rustr_clean_build} cleans the build target of \code{cargo build}. 16 | } 17 | 18 | -------------------------------------------------------------------------------- /man/rustr_init.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/init.R 3 | \name{rustr_init} 4 | \alias{rustr_init} 5 | \title{Create a skeleton for a new source package with Rust support} 6 | \usage{ 7 | rustr_init(name, path = ".", force = FALSE, code_files = character(), 8 | rust_files = character(), author = "Your Name", maintainer = if 9 | (missing(author)) "Your Name" else author, email = "your@email.com", 10 | license = "MIT") 11 | } 12 | \arguments{ 13 | \item{name}{A character string: the package name and directory name for your package.} 14 | 15 | \item{path}{A path to put the package directory in.} 16 | 17 | \item{force}{See \link[utils]{package.skeleton}} 18 | 19 | \item{code_files}{See \link[utils]{package.skeleton}} 20 | 21 | \item{rust_files}{A character vector with the paths to Rust source files to add to the package.} 22 | 23 | \item{author}{Author of the package.} 24 | 25 | \item{maintainer}{Maintainer of the package.} 26 | 27 | \item{email}{Email of the package maintainer.} 28 | 29 | \item{license}{License of the package.} 30 | } 31 | \description{ 32 | Create a skeleton for a new source package with Rust support. 33 | } 34 | \details{ 35 | There is a folder name \code{rustlib} in the created package \code{src} path. This folder is a rust library which will be compiled and linked by R. Just write Rust code in \code{./src/rustlib/src/} . 36 | 37 | \code{./src/rustlib/src/export.rs}, \code{./src/REXPORT.c} and \code{./R/REXPORT.R} are generated by \code{rustrize()}, and they should not be edited by hand. Before building the package, run \code{rustrize()} to generate these three files. 38 | } 39 | \examples{ 40 | \dontrun{ 41 | 42 | rustr_init("test-pkg") 43 | } 44 | } 45 | \references{ 46 | Read the \emph{Writing R Extensions} manual for more details. 47 | } 48 | \seealso{ 49 | https://book.rustr.org/create-a-rust-r-package.html 50 | } 51 | 52 | -------------------------------------------------------------------------------- /man/rustrize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rustrize.R 3 | \name{rustrize} 4 | \alias{rustrize} 5 | \title{Generate Rust bindings} 6 | \usage{ 7 | rustrize(pkgdir = ".") 8 | } 9 | \arguments{ 10 | \item{pkgdir}{package path} 11 | } 12 | \description{ 13 | Generate Rust bindings 14 | } 15 | 16 | -------------------------------------------------------------------------------- /rustinr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | StripTrailingWhitespace: Yes 16 | LineEndingConversion: Posix 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageBuildBinaryArgs: --no-multiarch 22 | PackageCheckArgs: --no-multiarch 23 | PackageRoxygenize: rd,collate,namespace,vignette 24 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(rustinr) 3 | 4 | test_check("rustinr") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-init.r: -------------------------------------------------------------------------------- 1 | context("init") 2 | 3 | test_that("init",{ 4 | expect_equal(1 + 1, 2) 5 | }) --------------------------------------------------------------------------------