rust-embed v8.13.0
git clone https://git.pyrossh.dev/rust-embed
rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.
89b83a8
— pyros2097
2018-03-18T22:02:46+05:30
add dev version of custom derive macro
Generated files not shown: Cargo.lock
- .vscode/settings.json +82 -0
- Cargo.toml +7 -2
- examples/basic.rs +28 -0
- examples/rocket.rs +14 -14
- examples/rouille.rs +0 -1
- readme.md +20 -22
- src/lib.rs +134 -94
- 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.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rust-embed"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.0"
|
|
4
|
-
description = "Rust
|
|
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 <[email protected]>"]
|
|
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
|
-
|
|
19
|
+
struct Asset;
|
|
15
|
-
use rust_embed::*;
|
|
16
20
|
|
|
17
21
|
#[get("/")]
|
|
18
|
-
fn index<'r>(
|
|
22
|
+
fn index<'r>() -> response::Result<'r> {
|
|
19
|
-
|
|
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>(
|
|
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
|
-
|
|
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
|
-
|
|
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 [](https://travis-ci.org/pyros2097/rust-embed) [](https://crates.io/crates/rust-embed)
|
|
2
|
-
Rust
|
|
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.
|
|
14
|
+
rust-embed="1.0.0"
|
|
23
15
|
```
|
|
24
16
|
|
|
25
17
|
## Documentation
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37
|
+
struct Asset;
|
|
39
38
|
|
|
40
39
|
fn main() {
|
|
41
|
-
let asset = embed!("examples/public/".to_owned());
|
|
42
|
-
let index_html =
|
|
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
|
|
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
|
|
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
|
|
5
|
+
extern crate syn;
|
|
6
|
+
|
|
3
7
|
extern crate walkdir;
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
use proc_macro::TokenStream;
|
|
10
|
+
use syn::*;
|
|
11
|
+
use quote::Tokens;
|
|
6
12
|
|
|
7
13
|
#[cfg(debug_assertions)]
|
|
8
|
-
|
|
14
|
+
fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
|
|
15
|
+
quote!{
|
|
9
|
-
|
|
16
|
+
use std::fs::File;
|
|
17
|
+
use std::io::Read;
|
|
10
|
-
|
|
18
|
+
use std::path::Path;
|
|
19
|
+
|
|
11
|
-
|
|
20
|
+
impl #ident {
|
|
12
|
-
|
|
21
|
+
pub fn get(file_path: &str) -> Option<Vec<u8>> {
|
|
13
|
-
|
|
22
|
+
let folder_path = #folder_path;
|
|
14
|
-
|
|
23
|
+
let name = &format!("{}{}", folder_path, file_path);
|
|
15
|
-
|
|
24
|
+
let path = &Path::new(name);
|
|
16
|
-
|
|
25
|
+
let key = String::from(path.to_str().expect("Path does not have a string representation"));
|
|
17
|
-
|
|
26
|
+
info!("file: {}", key);
|
|
18
|
-
|
|
27
|
+
let mut file = match File::open(path) {
|
|
19
|
-
|
|
28
|
+
Ok(mut file) => file,
|
|
20
|
-
|
|
29
|
+
Err(e) => {
|
|
21
|
-
|
|
30
|
+
error!("file: {} {}", key, e);
|
|
22
|
-
|
|
31
|
+
return None
|
|
23
|
-
|
|
32
|
+
}
|
|
24
|
-
|
|
33
|
+
};
|
|
25
|
-
|
|
34
|
+
let mut data: Vec<u8> = Vec::new();
|
|
26
|
-
|
|
35
|
+
match file.read_to_end(&mut data) {
|
|
27
|
-
|
|
36
|
+
Ok(_) => Some(data),
|
|
28
|
-
|
|
37
|
+
Err(e) => {
|
|
29
|
-
|
|
38
|
+
error!("file: {} {}", key, e);
|
|
30
|
-
|
|
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
|
|
48
|
+
pub fn generate_assets(folder_path: String) -> Asset {
|
|
38
|
-
|
|
49
|
+
use std::fs::File;
|
|
39
|
-
|
|
50
|
+
use std::io::Read;
|
|
40
|
-
|
|
51
|
+
use std::path::Path;
|
|
41
|
-
|
|
52
|
+
use walkdir::WalkDir;
|
|
42
|
-
|
|
53
|
+
use std::collections::HashMap;
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
info!("loading folder -> {}", parent_path);
|
|
45
|
-
|
|
55
|
+
let mut map = HashMap::new();
|
|
46
|
-
|
|
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
|
-
|
|
61
|
+
info!("asset from file -> {}", entry.path().display());
|
|
48
|
-
|
|
62
|
+
let base = &parent_path.clone();
|
|
63
|
+
let key = String::from(
|
|
64
|
+
entry
|
|
65
|
+
.path()
|
|
66
|
+
.to_str()
|
|
49
|
-
|
|
67
|
+
.expect("Path does not have a string representation"),
|
|
68
|
+
).replace(base, "");
|
|
50
|
-
|
|
69
|
+
let mut file = File::open(&Path::new(&entry.path())).unwrap_or_else(|e| {
|
|
51
|
-
|
|
70
|
+
panic!("could not open file -> {} {}", key, e);
|
|
52
|
-
|
|
71
|
+
});
|
|
53
|
-
|
|
72
|
+
let mut data: Vec<u8> = Vec::new();
|
|
54
|
-
|
|
73
|
+
file.read_to_end(&mut data).unwrap_or_else(|e| {
|
|
55
|
-
|
|
74
|
+
panic!("could not read file -> {} {}", key, e);
|
|
56
|
-
|
|
75
|
+
});
|
|
57
|
-
|
|
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
|
-
|
|
86
|
+
impl #ident {
|
|
60
|
-
|
|
87
|
+
pub fn get(file_path: &str) -> Option<Vec<u8>> {
|
|
61
|
-
|
|
88
|
+
match file_path {
|
|
62
|
-
|
|
89
|
+
"index.html" => Some(vec![]),
|
|
63
|
-
|
|
90
|
+
_ => None,
|
|
64
|
-
|
|
91
|
+
}
|
|
65
|
-
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
66
95
|
}
|
|
67
96
|
|
|
68
|
-
|
|
97
|
+
fn help() {
|
|
69
|
-
|
|
98
|
+
panic!(
|
|
70
|
-
(
|
|
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
|
-
|
|
104
|
+
match ast.body {
|
|
105
|
+
Body::Enum(_) => help(),
|
|
106
|
+
Body::Struct(ref data) => match data {
|
|
107
|
+
&VariantData::Struct(_) => help(),
|
|
74
|
-
|
|
108
|
+
_ => {}
|
|
75
|
-
|
|
109
|
+
},
|
|
110
|
+
};
|
|
76
|
-
|
|
111
|
+
let ident = &ast.ident;
|
|
77
|
-
fn dev() {
|
|
78
|
-
let asset = embed!("examples/public/".to_owned());
|
|
79
|
-
|
|
112
|
+
if ast.attrs.len() == 0 || ast.attrs.len() > 1 {
|
|
80
|
-
None => assert!(false, "index.html should exist"),
|
|
81
|
-
|
|
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
|
-
|
|
118
|
+
if attr_name == "folder" {
|
|
119
|
+
items
|
|
120
|
+
} else {
|
|
84
|
-
|
|
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
|
-
|
|
124
|
+
_ => {
|
|
96
|
-
let asset = embed!("examples/public/".to_owned());
|
|
97
|
-
match asset("index.html".to_owned()) {
|
|
98
|
-
|
|
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
|
+
}
|