diff --git a/README.md b/README.md index a8a3f383..9d59d441 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ - [:features](#features) - [:paths](#paths) - [:toolchain](#toolchain) + - [:main](#main) - [Spinner](#spinner) - [inline-documentation](#inline-documentation) - [Prequisites](#prequisites) @@ -79,7 +80,7 @@ The other files provide functionality that is similar to some of the features of rustic, however can be considered light-weight compared to some rustic's functionality. -The shared functions and options exist as aliases in the rust-mode and +The shared functions and options exist as aliases in the rust-mode and rustic namespace for backwards compatability reasons(rustic has been a fork). ## Known issues @@ -499,7 +500,7 @@ then a string, instead of a list, will also be accepted: ``` #+BEGIN_SRC rust :crates '((tokio . 1.0)) :features '((tokio . ("rt-multi-thread" "time"))) extern crate tokio; - + fn main() { tokio::runtime::Runtime::new() .unwrap() @@ -541,6 +542,45 @@ fn main() { : a,b,c ``` +#### :main + +Auto wrap whole block body in a "fn main" function call if none exists. + +Since this is very handy in most code snippets, so the default value is "yes". +"no" if you don't want this feature(for example if you don't want regex search slow things down). + +You can also set a default value by: +``` elisp +;; By setq this default to `nil`, you'll have to explict set params to ":main yes" in each block +(setq rustic-babel-auto-wrap-main nil) +``` + +``` +#+begin_src rust +let x = vec![1, 2, 3].iter().map(|&x| x + 1).collect::>(); +println!("{:?}", x); +#+end_src + +#+results: +: [2, 3, 4] +``` + +NOTE: the regex match would unable to /understand/ `fn main()` within a raw string. + +``` +#+begin_src rust :exports both :main yes +// won't work +let r = " +fn main() { + let x = "within raw"; +} +"; + +// but this works +printlnt!("fn main{{}}") +#+end_src +``` + ## Spinner diff --git a/rustic-babel.el b/rustic-babel.el index f9a5cd33..66d3f3bf 100644 --- a/rustic-babel.el +++ b/rustic-babel.el @@ -26,6 +26,11 @@ :type 'boolean :group 'rustic-babel) +(defcustom rustic-babel-auto-wrap-main t + "Whether to auto wrap BODY in a \"fn main\" function call if none exists." + :type 'boolean + :group 'rustic-babel) + (defcustom rustic-babel-format-src-block t "Whether to format a src block automatically after successful execution." :type 'boolean @@ -259,6 +264,12 @@ directory DIR." (insert s) (insert dependencies)))))) +(defun rustic-babel-ensure-main-wrap (body) + "Wrap BODY in a \"fn main\" function call if none exists." + (if (string-match "^[ \t]*[fn]+[ \t\n\r]*main[ \t]*(.*)" body) + body + (format "fn main() {\n%s\n}\n" body))) + (defun org-babel-execute:rustic (body params) "Execute a block of Rust code with org-babel. @@ -272,7 +283,12 @@ kill the running process." (let* ((default-directory org-babel-temporary-directory) (project (rustic-babel-project)) (dir (setq rustic-babel-dir (expand-file-name project))) - (main (expand-file-name "main.rs" (concat dir "/src")))) + (main-p (cdr (assq :main params))) + (main (expand-file-name "main.rs" (concat dir "/src"))) + (wrap-main (cond ((string= main-p "yes") t) + ((string= main-p "no") nil) + (t rustic-babel-auto-wrap-main)))) + (make-directory (file-name-directory main) t) (rustic-babel-cargo-toml dir params) (setq rustic-info (org-babel-get-src-block-info)) @@ -286,7 +302,8 @@ kill the running process." (let ((default-directory dir) (toolchain (cdr (assq :toolchain params)))) (write-region - (concat "#![allow(non_snake_case)]\n" body) nil main nil 0) + (concat "#![allow(non_snake_case)]\n" + (if wrap-main (rustic-babel-ensure-main-wrap body) body)) nil main nil 0) (rustic-babel-eval dir toolchain) (setq rustic-babel-src-location (set-marker (make-marker) (point) (current-buffer))) diff --git a/test/rustic-babel-test.el b/test/rustic-babel-test.el index 79b24acf..88968fa3 100644 --- a/test/rustic-babel-test.el +++ b/test/rustic-babel-test.el @@ -187,3 +187,40 @@ (with-current-buffer buf (rustic-test-babel-execute-block buf) (should (eq (rustic-test-babel-check-results buf) nil))))) + +(ert-deftest rustic-test-babel-ensure-main-wrap() + (let* ((string "let x = \"fn main(){}\";") + (buf (rustic-test-get-babel-block string))) + (with-current-buffer buf + (rustic-test-babel-execute-block buf) + (should (eq (rustic-test-babel-check-results buf) nil)))) + (let* ((string "let x = \"fn main(){}\";") + (params ":main no") + (buf (rustic-test-get-babel-block string params))) + (with-current-buffer buf + (rustic-test-babel-execute-block buf) + (let ((re (format "error: Could not compile `%s`.\n" + (car (reverse (split-string rustic-babel-dir "/")))))) + (should (string= re (rustic-test-babel-check-results buf))))))) + +(ert-deftest rustic-test-babel-ensure-main-wrap-yes-with-main() + (let* ((string " + fn main() { +let x = \"rustic\"; + }") + (params ":main yes") + (buf (rustic-test-get-babel-block string params))) + (with-current-buffer buf + (rustic-test-babel-execute-block buf) + (should (eq (rustic-test-babel-check-results buf) nil))))) + +(ert-deftest rustic-test-babel-ensure-main-wrap-no() + (let* ((string " + fn main() { +let x = \"rustic\"; + }") + (params ":main no") + (buf (rustic-test-get-babel-block string params))) + (with-current-buffer buf + (rustic-test-babel-execute-block buf) + (should (eq (rustic-test-babel-check-results buf) nil)))))