~repos /rust-embed

#rust#proc-macro#http

git clone https://pyrossh.dev/repos/rust-embed.git
Discussions: https://groups.google.com/g/rust-embed-devs

rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.


Files changed (9) hide show
  1. .vscode/settings.json +82 -0
  2. Cargo.lock +35 -1
  3. Cargo.toml +7 -2
  4. examples/basic.rs +28 -0
  5. examples/rocket.rs +14 -14
  6. examples/rouille.rs +0 -1
  7. readme.md +20 -22
  8. src/lib.rs +134 -94
  9. tests/lib.rs +47 -0
.vscode/settings.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "editor.tabSize": 2,
3
+ "editor.fontSize": 15,
4
+ "editor.wordWrapColumn": 160,
5
+ "editor.minimap.enabled": false,
6
+ "search.exclude": {
7
+ "**/node_modules": true,
8
+ "**/bower_components": true,
9
+ "**/.git": true,
10
+ "**/.DS_Store": true,
11
+ "**/__pycache__": true,
12
+ "**/target": true,
13
+ "**/dist": true,
14
+ "**/.expo": true,
15
+ "**/.cache": true,
16
+ "**/.cache-loader": true,
17
+ },
18
+ "files.exclude": {
19
+ "**/node_modules": true,
20
+ "**/.git": true,
21
+ "**/.DS_Store": true,
22
+ "**/__pycache__": true,
23
+ "**/target": true,
24
+ "**/dist": true,
25
+ "**/.expo": true,
26
+ "**/.cache": true,
27
+ "**/.cache-loader": true,
28
+ },
29
+ "files.watcherExclude": {
30
+ "**/node_modules": true,
31
+ "**/dist": true,
32
+ "**/.git/objects/**": true,
33
+ "**/node_modules/**": true,
34
+ "**/__pycache__": true,
35
+ "**/target": true,
36
+ "**/.expo": true,
37
+ "**/.cache": true,
38
+ "**/.cache-loader": true,
39
+ },
40
+ "workbench.editor.showTabs": false,
41
+ "explorer.openEditors.visible": 0,
42
+ "gitProjectManager.baseProjectsFolders": [
43
+ "~/Code",
44
+ ],
45
+ "gitProjectManager.openInNewWindow": true,
46
+ "window.zoomLevel": 0,
47
+ "window.menuBarVisibility": "toggle",
48
+ "workbench.iconTheme": "vscode-icons",
49
+ "git.confirmSync": false,
50
+ "git.enableSmartCommit": true,
51
+ "workbench.startupEditor": "newUntitledFile",
52
+ "gitlens.advanced.messages": {
53
+ "suppressCommitHasNoPreviousCommitWarning": false,
54
+ "suppressCommitNotFoundWarning": false,
55
+ "suppressFileNotUnderSourceControlWarning": false,
56
+ "suppressGitVersionWarning": false,
57
+ "suppressLineUncommittedWarning": false,
58
+ "suppressNoRepositoryWarning": false,
59
+ "suppressResultsExplorerNotice": true,
60
+ "suppressUpdateNotice": true,
61
+ "suppressWelcomeNotice": false
62
+ },
63
+ "workbench.colorTheme": "Dracula Soft",
64
+ "git.autofetch": true,
65
+ "gitlens.keymap": "alternate",
66
+ "sqltools.format": {
67
+ "indentSize": 2
68
+ },
69
+ "sqltools.connections": [
70
+ {
71
+ "name": "Postgres",
72
+ "server": "localhost",
73
+ "dialect": "PostgreSQL",
74
+ "port": 5432,
75
+ "database": "postgres",
76
+ "username": "postgres",
77
+ "askForPassword": false,
78
+ "password": "postgres",
79
+ "connectionTimeout": 300
80
+ }
81
+ ]
82
+ }
Cargo.lock CHANGED
@@ -555,6 +555,11 @@ dependencies = [
555
555
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
556
556
  ]
557
557
 
558
+ [[package]]
559
+ name = "quote"
560
+ version = "0.3.15"
561
+ source = "registry+https://github.com/rust-lang/crates.io-index"
562
+
558
563
  [[package]]
559
564
  name = "quote"
560
565
  version = "0.4.2"
@@ -709,14 +714,16 @@ dependencies = [
709
714
 
710
715
  [[package]]
711
716
  name = "rust-embed"
712
- version = "0.5.2"
717
+ version = "1.0.0"
713
718
  dependencies = [
714
719
  "fern 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
715
720
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
721
+ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
716
722
  "rocket 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
717
723
  "rocket_codegen 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
718
724
  "rocket_contrib 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
719
725
  "rouille 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
726
+ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
720
727
  "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
721
728
  ]
722
729
 
@@ -799,6 +806,16 @@ name = "state"
799
806
  version = "0.3.3"
800
807
  source = "registry+https://github.com/rust-lang/crates.io-index"
801
808
 
809
+ [[package]]
810
+ name = "syn"
811
+ version = "0.11.11"
812
+ source = "registry+https://github.com/rust-lang/crates.io-index"
813
+ dependencies = [
814
+ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
815
+ "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
816
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
817
+ ]
818
+
802
819
  [[package]]
803
820
  name = "syn"
804
821
  version = "0.12.14"
@@ -809,6 +826,14 @@ dependencies = [
809
826
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
810
827
  ]
811
828
 
829
+ [[package]]
830
+ name = "synom"
831
+ version = "0.11.3"
832
+ source = "registry+https://github.com/rust-lang/crates.io-index"
833
+ dependencies = [
834
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
835
+ ]
836
+
812
837
  [[package]]
813
838
  name = "tempdir"
814
839
  version = "0.3.6"
@@ -915,6 +940,11 @@ name = "unicode-normalization"
915
940
  version = "0.1.5"
916
941
  source = "registry+https://github.com/rust-lang/crates.io-index"
917
942
 
943
+ [[package]]
944
+ name = "unicode-xid"
945
+ version = "0.0.4"
946
+ source = "registry+https://github.com/rust-lang/crates.io-index"
947
+
918
948
  [[package]]
919
949
  name = "unicode-xid"
920
950
  version = "0.1.0"
@@ -1074,6 +1104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1074
1104
  "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
1075
1105
  "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
1076
1106
  "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
1107
+ "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
1077
1108
  "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
1078
1109
  "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
1079
1110
  "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
@@ -1099,7 +1130,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1099
1130
  "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
1100
1131
  "checksum smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4f357e8cd37bf8822e1b964e96fd39e2cb5a0424f8aaa284ccaccc2162411c"
1101
1132
  "checksum state 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e2fe297055568778ddc83eb1d4292bcdab36bf9e5e7adf4d0ce4ee59caf778d9"
1133
+ "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
1102
1134
  "checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd"
1135
+ "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
1103
1136
  "checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e"
1104
1137
  "checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281"
1105
1138
  "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
@@ -1113,6 +1146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1113
1146
  "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
1114
1147
  "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
1115
1148
  "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
1149
+ "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
1116
1150
  "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
1117
1151
  "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
1118
1152
  "checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068"
Cargo.toml CHANGED
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "rust-embed"
3
- version = "0.5.2"
3
+ version = "1.0.0"
4
- description = "Rust Marco which loads files into the rust binary at compile time during release and loads the file from the fs during dev"
4
+ description = "Rust Custom Derive Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev"
5
5
  readme = "readme.md"
6
6
  documentation = "https://docs.rs/rust-embed"
7
7
  repository = "https://github.com/pyros2097/rust-embed"
@@ -10,7 +10,12 @@ keywords = ["http", "rocket", "static", "web", "server"]
10
10
  categories = ["web-programming::http-server"]
11
11
  authors = ["pyros2097 <pyros2097@gmail.com>"]
12
12
 
13
+ [lib]
14
+ proc-macro = true
15
+
13
16
  [dependencies]
17
+ syn = "0.11"
18
+ quote = "0.3"
14
19
  log = "0.4"
15
20
  walkdir = "2.1.4"
16
21
 
examples/basic.rs ADDED
@@ -0,0 +1,28 @@
1
+ #![feature(attr_literals)]
2
+ #[macro_use]
3
+ extern crate log;
4
+ #[macro_use]
5
+ extern crate rust_embed;
6
+ extern crate fern;
7
+
8
+ #[derive(RustEmbed)]
9
+ #[folder("examples/public/")]
10
+ struct Asset;
11
+
12
+ fn main() {
13
+ fern::Dispatch::new()
14
+ .format(move |out, message, record| {
15
+ out.finish(format_args!(
16
+ "[{}][{}] {}",
17
+ record.target(),
18
+ record.level(),
19
+ message
20
+ ))
21
+ })
22
+ .level(log::LevelFilter::Info)
23
+ .chain(std::io::stdout())
24
+ .apply()
25
+ .expect("Could not initialize logger");
26
+ let index_html = Asset::get("index.html").unwrap();
27
+ println!("{:?}", std::str::from_utf8(&index_html));
28
+ }
examples/rocket.rs CHANGED
@@ -1,22 +1,26 @@
1
- #![feature(test, plugin, decl_macro)]
1
+ #![feature(test, plugin, decl_macro, attr_literals)]
2
2
  #![plugin(rocket_codegen)]
3
+ extern crate fern;
4
+ #[macro_use]
5
+ extern crate log;
3
6
  extern crate rocket;
4
7
  extern crate rocket_contrib;
8
+ #[macro_use]
5
9
  extern crate rust_embed;
6
- extern crate fern;
7
- extern crate log;
8
10
 
9
11
  use std::path::PathBuf;
10
12
  use std::ffi::OsStr;
11
13
  use std::io::Cursor;
12
14
  use rocket::response;
13
15
  use rocket::http::{ContentType, Status};
16
+
17
+ #[derive(RustEmbed)]
18
+ #[folder("examples/public/")]
14
- use rocket::State;
19
+ struct Asset;
15
- use rust_embed::*;
16
20
 
17
21
  #[get("/")]
18
- fn index<'r>(asset: State<Asset>) -> response::Result<'r> {
22
+ fn index<'r>() -> response::Result<'r> {
19
- asset("index.html".to_owned()).map_or_else(
23
+ Asset::get("index.html").map_or_else(
20
24
  || Err(Status::NotFound),
21
25
  |d| {
22
26
  response::Response::build()
@@ -28,7 +32,7 @@ fn index<'r>(asset: State<Asset>) -> response::Result<'r> {
28
32
  }
29
33
 
30
34
  #[get("/dist/<file..>")]
31
- fn dist<'r>(asset: State<Asset>, file: PathBuf) -> response::Result<'r> {
35
+ fn dist<'r>(file: PathBuf) -> response::Result<'r> {
32
36
  let filename = file.display().to_string();
33
37
  let ext = file
34
38
  .as_path()
@@ -36,7 +40,7 @@ fn dist<'r>(asset: State<Asset>, file: PathBuf) -> response::Result<'r> {
36
40
  .and_then(OsStr::to_str)
37
41
  .expect("Could not get file extension");
38
42
  let content_type = ContentType::from_extension(ext).expect("Could not get file content type");
39
- asset(filename.clone()).map_or_else(
43
+ Asset::get(&filename.clone()).map_or_else(
40
44
  || Err(Status::NotFound),
41
45
  |d| {
42
46
  response::Response::build()
@@ -61,9 +65,5 @@ fn main() {
61
65
  .chain(std::io::stdout())
62
66
  .apply()
63
67
  .expect("Could not initialize logger");
64
- let asset = embed!("examples/public/".to_owned());
65
- rocket::ignite()
66
- .manage(asset)
67
- .mount("/", routes![index, dist])
68
+ rocket::ignite().mount("/", routes![index, dist]).launch();
68
- .launch();
69
69
  }
examples/rouille.rs CHANGED
@@ -1,4 +1,3 @@
1
- #[macro_use]
2
1
  extern crate rouille;
3
2
 
4
3
  use rouille::Response;
readme.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ## Rust Embed [![Build Status](https://travis-ci.org/pyros2097/rust-embed.svg?branch=master)](https://travis-ci.org/pyros2097/rust-embed) [![crates.io](http://meritbadge.herokuapp.com/rust-embed)](https://crates.io/crates/rust-embed)
2
- Rust Marco which loads files into the rust binary at compile time during release and loads the file from the fs during dev.
2
+ Rust Custom Derive Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.
3
3
 
4
4
  You can use this to embed your css, js and images into a single executable.
5
5
 
@@ -7,55 +7,53 @@ This is similar to [go-embed](https://github.com/pyros2097/go-embed).
7
7
 
8
8
  This is similar to [pony-embed](https://github.com/pyros2097/pony-embed).
9
9
 
10
- Note:
11
-
12
- This is not the same as std macros,
13
- `include_bytes!`
14
- `include_str!`
15
- these are macros which generate code at compile time for only files.
16
- `embed!("examples/public/")` accepts folders and returns a function to get the contents using the file path
17
-
18
10
  ## Installation
19
11
 
20
12
  ```
21
13
  [dependencies]
22
- rust-embed="0.5.2"
14
+ rust-embed="1.0.0"
23
15
  ```
24
16
 
25
17
  ## Documentation
26
- The `embed!` macro takes a folder path and returns a function which allows you to get the file by passing the file path within the folder. So now you can statically compile all your assets i.e. your /static/ or /public/ folders into the rust executable and serve them during release and in development it will load the file from the file
27
- system so that it doesn't take to much time to compile;]
28
-
18
+ Declare a struct name it Asset or something and add an attribute `folder` to it which has the path to your static folder.
29
19
  ```rust
20
+ #![feature(attr_literals)]
21
+
22
+ #[derive(RustEmbed)]
30
- asset(path: String) -> Option<Vec<u8>>
23
+ #[folder("examples/public/")]
24
+ struct Asset;
31
25
  ```
32
26
 
33
27
  ## Usage
34
28
  ```rust
29
+ #![feature(attr_literals)]
35
30
  #[macro_use]
36
31
  extern crate rust_embed;
32
+ #[macro_use]
33
+ extern crate log;
37
34
 
35
+ #[derive(RustEmbed)]
36
+ #[folder("examples/public/")]
38
- use rust_embed::*;
37
+ struct Asset;
39
38
 
40
39
  fn main() {
41
- let asset = embed!("examples/public/".to_owned());
42
- let index_html = asset("index.html".to_owned()).unwrap();
40
+ let index_html = Asset::get("index.html").unwrap();
43
- println!("{}", index_html);
41
+ println!("{:?}", std::str::from_utf8(&index_html));
44
42
  }
45
43
  ```
46
44
 
47
45
  ## Examples
48
46
  To run the example in dev mode where it reads from the fs,
49
47
 
50
- `cargo run --example rocket`
48
+ `cargo run --example basic`
51
49
 
52
50
  To run the example in release mode where it reads from binary,
53
51
 
54
- `cargo run --release --example rocket`
52
+ `cargo run --release --example basic`
55
53
  ## Testing
56
- debug: `cargo test --lib`
54
+ debug: `cargo test --tests --lib`
57
55
 
58
- release: `cargo test --lib --release`
56
+ release: `cargo test --tests --lib --release`
59
57
 
60
58
  Go Rusketeers!
61
59
  The power is yours!
src/lib.rs CHANGED
@@ -1,110 +1,150 @@
1
+ #![recursion_limit = "1024"]
2
+ extern crate proc_macro;
1
3
  #[macro_use]
4
+ extern crate quote;
2
- extern crate log;
5
+ extern crate syn;
6
+
3
7
  extern crate walkdir;
4
8
 
5
- pub type Asset = Box<Fn(String) -> Option<Vec<u8>> + std::marker::Sync + std::marker::Send>;
9
+ use proc_macro::TokenStream;
10
+ use syn::*;
11
+ use quote::Tokens;
6
12
 
7
13
  #[cfg(debug_assertions)]
8
- pub fn generate_assets(parent_path: String) -> Asset {
14
+ fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
15
+ quote!{
9
- use std::fs::File;
16
+ use std::fs::File;
17
+ use std::io::Read;
10
- use std::path::Path;
18
+ use std::path::Path;
19
+
11
- use std::io::Read;
20
+ impl #ident {
12
- info!("loading folder -> {}", parent_path);
21
+ pub fn get(file_path: &str) -> Option<Vec<u8>> {
13
- Box::new(move |file_path| {
22
+ let folder_path = #folder_path;
14
- let name = &format!("{}{}", parent_path, file_path);
23
+ let name = &format!("{}{}", folder_path, file_path);
15
- let path = &Path::new(name);
24
+ let path = &Path::new(name);
16
- let key = String::from(path.to_str().expect("Path does not have a string representation"));
25
+ let key = String::from(path.to_str().expect("Path does not have a string representation"));
17
- info!("asset from file -> {}", key);
26
+ info!("file: {}", key);
18
- let mut file = match File::open(path) {
27
+ let mut file = match File::open(path) {
19
- Ok(mut file) => file,
28
+ Ok(mut file) => file,
20
- Err(e) => {
29
+ Err(e) => {
21
- error!("could not open file -> {} {}", key, e);
30
+ error!("file: {} {}", key, e);
22
- return None
31
+ return None
23
- }
32
+ }
24
- };
33
+ };
25
- let mut data: Vec<u8> = Vec::new();
34
+ let mut data: Vec<u8> = Vec::new();
26
- match file.read_to_end(&mut data) {
35
+ match file.read_to_end(&mut data) {
27
- Ok(_) => Some(data),
36
+ Ok(_) => Some(data),
28
- Err(e) => {
37
+ Err(e) => {
29
- error!("could not open file -> {} {}", key, e);
38
+ error!("file: {} {}", key, e);
30
- return None
39
+ return None
31
- }
40
+ }
32
- }
41
+ }
33
- })
42
+ }
43
+ }
44
+ }
34
45
  }
35
46
 
36
47
  #[cfg(not(debug_assertions))]
37
- pub fn generate_assets<'a>(parent_path: String) -> Asset {
48
+ pub fn generate_assets(folder_path: String) -> Asset {
38
- use std::fs::File;
49
+ use std::fs::File;
39
- use std::io::Read;
50
+ use std::io::Read;
40
- use std::path::Path;
51
+ use std::path::Path;
41
- use walkdir::WalkDir;
52
+ use walkdir::WalkDir;
42
- use std::collections::HashMap;
53
+ use std::collections::HashMap;
43
-
44
- info!("loading folder -> {}", parent_path);
54
+ info!("loading folder -> {}", parent_path);
45
- let mut map = HashMap::new();
55
+ let mut map = HashMap::new();
46
- for entry in WalkDir::new(parent_path.clone()).into_iter().filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) {
56
+ for entry in WalkDir::new(parent_path.clone())
57
+ .into_iter()
58
+ .filter_map(|e| e.ok())
59
+ .filter(|e| e.file_type().is_file())
60
+ {
47
- info!("asset from file -> {}", entry.path().display());
61
+ info!("asset from file -> {}", entry.path().display());
48
- let base = &parent_path.clone();
62
+ let base = &parent_path.clone();
63
+ let key = String::from(
64
+ entry
65
+ .path()
66
+ .to_str()
49
- let key = String::from(entry.path().to_str().expect("Path does not have a string representation")).replace(base, "");
67
+ .expect("Path does not have a string representation"),
68
+ ).replace(base, "");
50
- let mut file = File::open(&Path::new(&entry.path())).unwrap_or_else(|e| {
69
+ let mut file = File::open(&Path::new(&entry.path())).unwrap_or_else(|e| {
51
- panic!("could not open file -> {} {}", key, e);
70
+ panic!("could not open file -> {} {}", key, e);
52
- });
71
+ });
53
- let mut data: Vec<u8> = Vec::new();
72
+ let mut data: Vec<u8> = Vec::new();
54
- file.read_to_end(&mut data).unwrap_or_else(|e| {
73
+ file.read_to_end(&mut data).unwrap_or_else(|e| {
55
- panic!("could not read file -> {} {}", key, e);
74
+ panic!("could not read file -> {} {}", key, e);
56
- });
75
+ });
57
- map.insert(key, data);
76
+ map.insert(key, data);
77
+ }
78
+ Box::new(move |file_path| {
79
+ info!("asset from cache -> {}", file_path);
80
+ match map.get(&file_path) {
81
+ Some(s) => Some(s.to_vec()),
82
+ None => None,
58
83
  }
84
+ });
85
+ quote!{
59
- Box::new(move |file_path| {
86
+ impl #ident {
60
- info!("asset from cache -> {}", file_path);
87
+ pub fn get(file_path: &str) -> Option<Vec<u8>> {
61
- match map.get(&file_path) {
88
+ match file_path {
62
- Some(s) => Some(s.to_vec()),
89
+ "index.html" => Some(vec![]),
63
- None => None,
90
+ _ => None,
64
- }
91
+ }
65
- })
92
+ }
93
+ }
94
+ }
66
95
  }
67
96
 
68
- #[macro_export]
97
+ fn help() {
69
- macro_rules! embed {
98
+ panic!(
70
- ($x:expr) => ( $crate::generate_assets($x) )
99
+ "#[derive(RustEmbed)] should contain one attribute like this #[golem(\"examples/public/\")]"
100
+ );
71
101
  }
72
102
 
103
+ fn impl_rust_embed(ast: &syn::DeriveInput) -> Tokens {
73
- #[cfg(test)]
104
+ match ast.body {
105
+ Body::Enum(_) => help(),
106
+ Body::Struct(ref data) => match data {
107
+ &VariantData::Struct(_) => help(),
74
- mod tests {
108
+ _ => {}
75
- #[test]
109
+ },
110
+ };
76
- #[cfg(debug_assertions)]
111
+ let ident = &ast.ident;
77
- fn dev() {
78
- let asset = embed!("examples/public/".to_owned());
79
- match asset("index.html".to_owned()) {
112
+ if ast.attrs.len() == 0 || ast.attrs.len() > 1 {
80
- None => assert!(false, "index.html should exist"),
81
- _ => assert!(true),
113
+ help();
82
- }
114
+ }
115
+ let value = &ast.attrs[0].value;
116
+ let items = match value {
117
+ &MetaItem::List(ref attr_name, ref items) => {
83
- match asset("gg.html".to_owned()) {
118
+ if attr_name == "folder" {
119
+ items
120
+ } else {
84
- Some(_) => assert!(false, "gg.html should not exist"),
121
+ panic!("#[derive(RustEmbed)] attribute name must be folder");
85
- _ => assert!(true),
86
- }
122
+ }
87
- match asset("images/llama.png".to_owned()) {
88
- None => assert!(false, "llama.png should exist"),
89
- _ => assert!(true),
90
- }
91
123
  }
92
-
93
- #[test]
94
- #[cfg(not(debug_assertions))]
95
- fn prod() {
124
+ _ => {
96
- let asset = embed!("examples/public/".to_owned());
97
- match asset("index.html".to_owned()) {
98
- None => assert!(false, "index.html should exist"),
125
+ panic!("#[derive(RustEmbed)] attribute name must be folder");
99
- _ => assert!(true),
100
- }
101
- match asset("gg.html".to_owned()) {
102
- Some(_) => assert!(false, "gg.html should not exist"),
103
- _ => assert!(true),
104
- }
105
- match asset("images/llama.png".to_owned()) {
106
- None => assert!(false, "llama.png should exist"),
107
- _ => assert!(true),
108
- }
109
126
  }
127
+ };
128
+ let item = &items[0];
129
+ let lit = match item {
130
+ &NestedMetaItem::Literal(ref l) => l,
131
+ _ => {
132
+ panic!("Hello");
133
+ }
134
+ };
135
+ let folder_path = match lit {
136
+ &Lit::Str(ref val, _) => val.clone(),
137
+ _ => {
138
+ panic!("#[derive(RustEmbed)] attribute value must be a string literal");
139
+ }
140
+ };
141
+ generate_assets(ident, folder_path)
142
+ }
143
+
144
+ #[proc_macro_derive(RustEmbed, attributes(folder))]
145
+ pub fn derive_input_object(input: TokenStream) -> TokenStream {
146
+ let s = input.to_string();
147
+ let ast = syn::parse_derive_input(&s).unwrap();
148
+ let gen = impl_rust_embed(&ast);
149
+ gen.parse().unwrap()
110
150
  }
tests/lib.rs ADDED
@@ -0,0 +1,47 @@
1
+ #![feature(attr_literals)]
2
+ #[macro_use]
3
+ extern crate log;
4
+ #[macro_use]
5
+ extern crate rust_embed;
6
+
7
+ #[test]
8
+ #[cfg(debug_assertions)]
9
+ fn dev() {
10
+ #[derive(RustEmbed)]
11
+ #[folder("examples/public/")]
12
+ struct Asset;
13
+
14
+ match Asset::get("index.html") {
15
+ None => assert!(false, "index.html should exist"),
16
+ _ => assert!(true),
17
+ }
18
+ match Asset::get("gg.html") {
19
+ Some(_) => assert!(false, "gg.html should not exist"),
20
+ _ => assert!(true),
21
+ }
22
+ match Asset::get("images/llama.png") {
23
+ None => assert!(false, "llama.png should exist"),
24
+ _ => assert!(true),
25
+ }
26
+ }
27
+
28
+ #[test]
29
+ #[cfg(not(debug_assertions))]
30
+ fn prod() {
31
+ #[derive(RustEmbed)]
32
+ #[folder("examples/public/")]
33
+ struct Asset;
34
+
35
+ match Asset::get("index.html") {
36
+ None => assert!(false, "index.html should exist"),
37
+ _ => assert!(true),
38
+ }
39
+ match Asset::get("gg.html") {
40
+ Some(_) => assert!(false, "gg.html should not exist"),
41
+ _ => assert!(true),
42
+ }
43
+ match Asset::get("images/llama.png") {
44
+ None => assert!(false, "llama.png should exist"),
45
+ _ => assert!(true),
46
+ }
47
+ }