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