mirror of
https://github.com/RGBCube/embd-rs
synced 2025-07-26 21:17:44 +00:00
Rename to embd
This commit is contained in:
parent
7c2eb614db
commit
53f550c9c0
10 changed files with 86 additions and 91 deletions
13
embd/Cargo.toml
Normal file
13
embd/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "embd"
|
||||
description = "Read files or directories from the filesystem at runtime on debug, embed on release."
|
||||
repository = "https://github.com/RGBCube/embd-rs"
|
||||
license = "MIT"
|
||||
keywords = [ "embedding", "files", "debug-optimization", "bundling" ]
|
||||
categories = [ "filesystem" ]
|
||||
authors = [ "RGBCube" ]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
embd-macros = "0.1"
|
65
embd/README.md
Normal file
65
embd/README.md
Normal file
|
@ -0,0 +1,65 @@
|
|||
# embd-rs
|
||||
|
||||
A super simple file and directory embedding crate,
|
||||
that loads files from the filesystem on debug mode,
|
||||
allowing for quick edit-and-test cycles without compilation.
|
||||
|
||||
It is also super efficient, and does not heap allocate when the
|
||||
files are embedded on release mode by utilizing `std::borrow::Cow`.
|
||||
|
||||
On release mode it falls back to `include_str!`, `include_bytes!`
|
||||
and our own custom `include_dir!`-like implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
embd = "0.1"
|
||||
```
|
||||
|
||||
Then you can use this crate like so:
|
||||
|
||||
```rs
|
||||
let contents: Cow<'_, str> = embd::string!("path/to/file.txt");
|
||||
let bytes: Cow<'_, [u8]> = embd::bytes!("path/to/image.png");
|
||||
|
||||
let dir: embd::Dir = embd::dir!("path/to");
|
||||
let files: Vec<embd::File> = dir.flatten();
|
||||
```
|
||||
|
||||
Note that you will need to enable the `procmacro2_semver_exempt` feature
|
||||
to use this crate, you can enable it like so, by putting this in
|
||||
`.cargo/config.toml` in the project root:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
rustflags = [ "--cfg", "procmacro2_semver_exempt" ]
|
||||
```
|
||||
|
||||
## 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.
|
||||
```
|
225
embd/src/lib.rs
Normal file
225
embd/src/lib.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
#![cfg(feature = "procmacro2_semver_exempt")]
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn __string_runtime(neighbor: &str, path: &str) -> String {
|
||||
let file = Path::new(neighbor)
|
||||
.parent()
|
||||
.expect("Failed to get the parent of file")
|
||||
.join(path);
|
||||
|
||||
fs::read_to_string(file).expect("Failed to read file")
|
||||
}
|
||||
|
||||
/// Embed a files contents as a `&str` on release,
|
||||
/// read from the filesystem as a `String` on debug.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// fn main() {
|
||||
/// let content: Cow<'static, str> = embd::string!("main.rs");
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! string {
|
||||
($path:literal) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
::std::borrow::Cow::Owned::<'static, str>(::embd::__string_runtime(file!(), $path))
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
::std::borrow::Cow::Borrowed(include_str!($path))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn __bytes_runtime(neighbor: &str, path: &str) -> Vec<u8> {
|
||||
let file = Path::new(neighbor)
|
||||
.parent()
|
||||
.expect("Failed to get the parent of file")
|
||||
.join(path);
|
||||
|
||||
fs::read(file).expect("Failed to read file")
|
||||
}
|
||||
|
||||
/// Embed a files contents as a `&[u8]` on release,
|
||||
/// read from the filesystem as a `Vec<u8>` on debug.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// fn main() {
|
||||
/// // `assets/` is in the same directory as `src/`
|
||||
/// let content: Cow<'static, [u8]> = embd::string!("../assets/icon.png");
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! bytes {
|
||||
($path:literal) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
::std::borrow::Cow::Owned::<'static, [u8]>(::embd::__bytes_runtime(file!(), $path))
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
::std::borrow::Cow::Borrowed(include_bytes!($path))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// A directory entry.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DirEntry {
|
||||
/// A directory.
|
||||
Dir(Dir),
|
||||
/// A file.
|
||||
File(File),
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
/// Returns the absolute path of the entry.
|
||||
pub fn path(&self) -> &Cow<'_, str> {
|
||||
match self {
|
||||
DirEntry::File(file) => file.path(),
|
||||
DirEntry::Dir(dir) => dir.path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A directory.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dir {
|
||||
#[doc(hidden)]
|
||||
pub __children: Cow<'static, [DirEntry]>,
|
||||
#[doc(hidden)]
|
||||
pub __path: Cow<'static, str>, /* We are making it a &str because *
|
||||
* include_*! takes a string anyway. */
|
||||
}
|
||||
|
||||
impl Dir {
|
||||
/// Returns the children of the directory.
|
||||
pub fn children(&self) -> &Cow<'_, [DirEntry]> {
|
||||
&self.__children
|
||||
}
|
||||
|
||||
/// Returns the absolute path of the directory.
|
||||
pub fn path(&self) -> &Cow<'_, str> {
|
||||
&self.__path
|
||||
}
|
||||
|
||||
/// Collects all files from the directory into a vector.
|
||||
pub fn flatten(self) -> Vec<File> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for child in self.__children.into_owned() {
|
||||
// TODO: Eliminate allocation.
|
||||
match child {
|
||||
DirEntry::File(file) => entries.push(file),
|
||||
DirEntry::Dir(dir) => entries.append(&mut dir.flatten()),
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
}
|
||||
|
||||
/// A file.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct File {
|
||||
#[doc(hidden)]
|
||||
pub __content: Cow<'static, [u8]>,
|
||||
#[doc(hidden)]
|
||||
pub __path: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Returns the content of the file.
|
||||
pub fn content(&self) -> &Cow<'_, [u8]> {
|
||||
&self.__content
|
||||
}
|
||||
|
||||
/// Returns the absolute path of the file.
|
||||
pub fn path(&self) -> &Cow<'_, str> {
|
||||
&self.__path
|
||||
}
|
||||
}
|
||||
|
||||
fn read_dir(directory: &Path) -> Vec<DirEntry> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(directory).expect("Failed to list directory contents") {
|
||||
let entry = entry.expect("Failed to read entry");
|
||||
|
||||
let filetype = entry.file_type().expect("Failed to read entry filetype");
|
||||
|
||||
let path = entry
|
||||
.path()
|
||||
.canonicalize()
|
||||
.expect("Failed to canonicalize path");
|
||||
|
||||
let path_str = path
|
||||
.to_str()
|
||||
.expect("Failed to convert OsStr to str")
|
||||
.to_string();
|
||||
|
||||
if filetype.is_dir() {
|
||||
let children = read_dir(&path);
|
||||
|
||||
entries.push(DirEntry::Dir(Dir {
|
||||
__children: children.into(),
|
||||
__path: path_str.into(),
|
||||
}))
|
||||
} else if filetype.is_file() {
|
||||
let content = fs::read(&path).expect("Failed to read file contents");
|
||||
|
||||
entries.push(DirEntry::File(File {
|
||||
__content: content.into(),
|
||||
__path: path_str.into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn __dir_runtime(neighbor: &str, path: &str) -> Dir {
|
||||
let directory = Path::new(neighbor)
|
||||
.parent()
|
||||
.expect("Failed to get the parent of file")
|
||||
.join(path)
|
||||
.canonicalize()
|
||||
.expect("Failed to canonicalize path");
|
||||
|
||||
let directory_str = directory
|
||||
.to_str()
|
||||
.expect("Failed to convert OsStr to str")
|
||||
.to_string();
|
||||
|
||||
let children = read_dir(&directory);
|
||||
|
||||
Dir {
|
||||
__children: children.into(),
|
||||
__path: directory_str.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Embed a directories contents.
|
||||
/// The content value of File will be Borrowed on release,
|
||||
/// and Owned on debug.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// fn main() {
|
||||
/// let content: embd::Dir = embd::dir!("../assets");
|
||||
/// }
|
||||
/// ```
|
||||
pub use embd_macros::__dir as dir;
|
Loading…
Add table
Add a link
Reference in a new issue