Skip to content

Commit

Permalink
rustic-babel: support wrap BODY in a fn main if none exists
Browse files Browse the repository at this point in the history
close brotzeit#267

related: brotzeit#266
  • Loading branch information
Eric Wang authored and Eric Wang committed Nov 5, 2021
1 parent 985c094 commit 4fa7eed
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [:features](#features)
- [:paths](#paths)
- [:toolchain](#toolchain)
- [:main](#main)
- [Spinner](#spinner)
- [inline-documentation](#inline-documentation)
- [Prequisites](#prequisites)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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::<Vec<_>>();
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

Expand Down
21 changes: 19 additions & 2 deletions rustic-babel.el
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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))
Expand All @@ -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)))
Expand Down
37 changes: 37 additions & 0 deletions test/rustic-babel-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))

0 comments on commit 4fa7eed

Please sign in to comment.