mirror of
https://github.com/RGBCube/embd-rs
synced 2025-07-26 21:17:44 +00:00
Initial commit
This commit is contained in:
commit
a76d9199c0
8 changed files with 353 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
*
|
||||||
|
|
||||||
|
!src/
|
||||||
|
|
||||||
|
!.gitignore
|
||||||
|
|
||||||
|
!Cargo.lock
|
||||||
|
!Cargo.toml
|
||||||
|
|
||||||
|
!*.md
|
||||||
|
!*.rs
|
53
Cargo.lock
generated
Normal file
53
Cargo.lock
generated
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embed"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir_macros"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "embed"
|
||||||
|
description = "Read files or directories from the filesystem at runtime on debug, embed on release."
|
||||||
|
repository = "https://github.com/RGBCube/embed-rs"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = [ "embedding", "files", "debug-optimization", "bundling" ]
|
||||||
|
cateories = [ "filesystem" ]
|
||||||
|
authors = [ "RGBCube" ]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
include_dir = { version = "^0.7", features = [ "metadata" ] }
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023-present RGBCube
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
45
README.md
Normal file
45
README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# embed-rs
|
||||||
|
|
||||||
|
A super simple file and directory embedding crate,
|
||||||
|
that loads files from the filesystem in debug mode,
|
||||||
|
allowing for quick edit-and-test cycles without compilation.
|
||||||
|
|
||||||
|
On release mode it falls back to `include_str!`, `include_bytes!` and `include_dir!`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
After running `cargo add embed`, you can do:
|
||||||
|
|
||||||
|
```rs
|
||||||
|
let contents: String = embed::string!("path/to/file.txt");
|
||||||
|
let bytes: Vec<u8> = embed::bytes!("path/to/image.png");
|
||||||
|
|
||||||
|
let dir: embed::Dir = embed::dir!("path/to/dir");
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
```
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023-present RGBCube
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
138
src/dir.rs
Normal file
138
src/dir.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
time::SystemTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
use include_dir::{
|
||||||
|
Dir,
|
||||||
|
DirEntry,
|
||||||
|
File,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn read_dir(dir: &PathBuf) -> Vec<PathBuf> {
|
||||||
|
if !dir.is_dir() {
|
||||||
|
panic!("embed: {path} is not a directory", path = dir.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
|
||||||
|
for entry in dir.read_dir().unwrap_or_else(|error| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to read directory {dir}: {error}",
|
||||||
|
dir = dir.display()
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
paths.push(
|
||||||
|
entry
|
||||||
|
.unwrap_or_else(|error| panic!("embed: failed to resolve entry: {error}"))
|
||||||
|
.path(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.sort();
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_to_entry<'a>(path: &'a PathBuf) -> DirEntry<'a> {
|
||||||
|
let abs = path.canonicalize().unwrap_or_else(|error| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to resolve path {path}: {error}",
|
||||||
|
path = path.display()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let contents = fs::read(&path).unwrap_or_else(|error| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to read file {path}: {error}",
|
||||||
|
path = path.display()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut entry = File::new(abs.to_str().unwrap(), contents.as_slice());
|
||||||
|
|
||||||
|
if let Ok(metadata) = path.metadata() {
|
||||||
|
entry = entry.with_metadata(include_dir::Metadata::new(
|
||||||
|
metadata
|
||||||
|
.accessed()
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to read metadata.accessed of {metadata:?}: {error}");
|
||||||
|
})
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to calculate time difference: {error}")
|
||||||
|
}),
|
||||||
|
metadata
|
||||||
|
.created()
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to read metadata.created of {metadata:?}: {error}");
|
||||||
|
})
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to calculate time difference: {error}")
|
||||||
|
}),
|
||||||
|
metadata
|
||||||
|
.modified()
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to read metadata.modified of {metadata:?}: {error}");
|
||||||
|
})
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
panic!("embed: failed to calculate time difference: {error}")
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry::File(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dir_to_entries<'a>(root: &'a PathBuf, path: &'a PathBuf) -> Vec<DirEntry<'a>> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
|
||||||
|
for child in read_dir(path) {
|
||||||
|
if child.is_dir() {
|
||||||
|
entries.push(DirEntry::Dir(Dir::new(
|
||||||
|
root.as_os_str().to_str().unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to convert {lossy} to &str as it was not valid unicode",
|
||||||
|
lossy = root.to_string_lossy()
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
&dir_to_entries(root, &child),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
else if child.is_file() {
|
||||||
|
entries.push(file_to_entry(&child))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!(
|
||||||
|
"{child} is neither a file or directory",
|
||||||
|
child = child.display()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn include_dir<'a>(path_str: &'a str) -> Dir<'a> {
|
||||||
|
let path = PathBuf::from(path_str);
|
||||||
|
|
||||||
|
let entries = dir_to_entries(&path, &path);
|
||||||
|
|
||||||
|
Dir::new(path_str, entries.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! dir {
|
||||||
|
($path:literal) => {{
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
include_dir($path)
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
include_dir!($path)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
67
src/file.rs
Normal file
67
src/file.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! string {
|
||||||
|
($path:literal) => {{
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.expect("embed: file has no parent")
|
||||||
|
.join($path);
|
||||||
|
|
||||||
|
Cow::<'static, str>::Owned(fs::read_to_string(&file).unwrap_or_else(|error| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to read file {file}: {error}",
|
||||||
|
file = file.display()
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
Cow::Borrowed(include_str!($path))
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bytes {
|
||||||
|
($path:literal) => {{
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.expect("embed: file has no parent")
|
||||||
|
.join($path);
|
||||||
|
|
||||||
|
Cow::<'static, str>::Owned(fs::read_to_string(&file).unwrap_or_else(|error| {
|
||||||
|
panic!(
|
||||||
|
"embed: failed to read file {file}: {error}",
|
||||||
|
file = file.display()
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
Cow::Borrowed(include_bytes!($path))
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn check_validity() {
|
||||||
|
string!("test.txt");
|
||||||
|
bytes!("test.txt");
|
||||||
|
}
|
5
src/lib.rs
Normal file
5
src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod dir;
|
||||||
|
pub use dir::*;
|
||||||
|
|
||||||
|
mod file;
|
||||||
|
pub use file::*;
|
Loading…
Add table
Add a link
Reference in a new issue