mirror of
https://github.com/RGBCube/embd-rs
synced 2025-07-26 04:57:44 +00:00
Todo dirs
This commit is contained in:
parent
d262e42422
commit
c031e19e7f
15 changed files with 371 additions and 179 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,11 +1,14 @@
|
|||
*
|
||||
|
||||
!src/
|
||||
!embed/
|
||||
!embed/src/
|
||||
|
||||
!macros/
|
||||
!macros/src/
|
||||
|
||||
!.gitignore
|
||||
|
||||
!Cargo.lock
|
||||
|
||||
!*.lock
|
||||
!*.md
|
||||
!*.rs
|
||||
!*.toml
|
||||
|
|
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -6,46 +6,46 @@ version = 3
|
|||
name = "embed"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"include_dir",
|
||||
"embed_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
|
||||
name = "embed_macros"
|
||||
version = "0.1.0"
|
||||
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",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d27c2c202598d05175a6dd3af46824b7f747f8d8e9b14c623f19fa5069735d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,13 +1,2 @@
|
|||
[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" ] }
|
||||
[workspace]
|
||||
members = [ "embed", "macros" ]
|
||||
|
|
|
@ -4,13 +4,16 @@ 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!`.
|
||||
On release mode it falls back to `include_str!`, `include_bytes!`
|
||||
and our own custom `include_dir!` implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
```rs
|
||||
let contents: Cow<'_, str> = embed::string!("path/to/file.txt");
|
||||
let bytes: Cow<'_, [u8]> = embed::bytes!("path/to/image.png");
|
||||
|
||||
let dir: embed::Dir = embed::dir!("path/to");
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
53
embed/Cargo.lock
generated
Normal file
53
embed/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
embed/Cargo.toml
Normal file
13
embed/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" ]
|
||||
categories = [ "filesystem" ]
|
||||
authors = [ "RGBCube" ]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
embed_macros = { path = "../macros" }
|
43
embed/README.md
Normal file
43
embed/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 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 our own custom `include_dir!` implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
```rs
|
||||
let contents: Cow<'_, str> = embed::string!("path/to/file.txt");
|
||||
let bytes: Cow<'_, [u8]> = embed::bytes!("path/to/image.png");
|
||||
|
||||
let dir: embed::Dir = embed::dir!("path/to");
|
||||
```
|
||||
|
||||
## 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.
|
||||
```
|
86
embed/src/dir.rs
Normal file
86
embed/src/dir.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use std::{
|
||||
fs::{
|
||||
self,
|
||||
},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
pub enum DirEntry {
|
||||
Dir(Dir),
|
||||
File(File),
|
||||
}
|
||||
|
||||
pub struct Dir {
|
||||
pub children: Vec<DirEntry>,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Dir {
|
||||
pub fn flatten(self) -> Vec<File> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for child in self.children {
|
||||
match child {
|
||||
DirEntry::File(file) => entries.push(file),
|
||||
DirEntry::Dir(dir) => entries.append(&mut dir.flatten()),
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
pub content: Vec<u8>,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
fn read_dir(path: &PathBuf) -> Vec<DirEntry> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(path).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 get the canonical path of the DirEntry");
|
||||
|
||||
if filetype.is_dir() {
|
||||
let children = read_dir(&path);
|
||||
|
||||
entries.push(DirEntry::Dir(Dir { children, path }))
|
||||
} else if filetype.is_file() {
|
||||
let content = fs::read(&path).expect("Failed to read file contents");
|
||||
|
||||
entries.push(DirEntry::File(File { content, path }))
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
|
||||
pub fn __include_dir(path: &str) -> Dir {
|
||||
let path = PathBuf::from(path)
|
||||
.canonicalize()
|
||||
.expect("Failed to get the canonical path of the DirEntry");
|
||||
|
||||
let children = read_dir(&path);
|
||||
|
||||
Dir { children, path }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dir {
|
||||
($path:literal) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
::embed::__include_dir($path)
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
::embed_macros::__include_dir!($path)
|
||||
}
|
||||
}};
|
||||
}
|
5
embed/src/lib.rs
Normal file
5
embed/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod dir;
|
||||
pub use dir::*;
|
||||
|
||||
mod file;
|
||||
pub use file::*;
|
17
macros/Cargo.toml
Normal file
17
macros/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "embed_macros"
|
||||
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" ]
|
||||
categories = [ "filesystem" ]
|
||||
authors = [ "RGBCube" ]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc_macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
syn = "2"
|
43
macros/README.md
Normal file
43
macros/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 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 our own custom `include_dir!` implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
```rs
|
||||
let contents: Cow<'_, str> = embed::string!("path/to/file.txt");
|
||||
let bytes: Cow<'_, [u8]> = embed::bytes!("path/to/image.png");
|
||||
|
||||
let dir: embed::Dir = embed::dir!("path/to");
|
||||
```
|
||||
|
||||
## 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.
|
||||
```
|
80
macros/src/lib.rs
Normal file
80
macros/src/lib.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use std::{
|
||||
fs,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
LitStr,
|
||||
};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn __include_dir(tokens: TokenStream) -> TokenStream {
|
||||
let path = parse_macro_input!(tokens as LitStr).value();
|
||||
|
||||
let path = PathBuf::from(path)
|
||||
.canonicalize()
|
||||
.expect("Failed to get the canonical path of the DirEntry");
|
||||
|
||||
let path_str = path
|
||||
.to_str()
|
||||
.expect("Failed to get the string representation of PathBuf");
|
||||
|
||||
let children = read_dir(&path);
|
||||
let children_tokens = quote! {
|
||||
vec![#(#children),*]
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
::embed::Dir {
|
||||
children: #children_tokens,
|
||||
path: ::std::path::PathBuf::from(#path_str),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn read_dir(path: &PathBuf) -> Vec<TokenStream> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(path).expect("Failed to list directory contents") {
|
||||
let entry = entry.expect("Failed to read entry");
|
||||
|
||||
let path = entry
|
||||
.path()
|
||||
.canonicalize()
|
||||
.expect("Failed to get the canonical path of the DirEntry");
|
||||
|
||||
let path_str = path
|
||||
.to_str()
|
||||
.expect("Failed to get the string representation of PathBuf");
|
||||
|
||||
let filetype = fs::metadata(path)
|
||||
.expect("Failed to get file metadata")
|
||||
.file_type();
|
||||
|
||||
if filetype.is_dir() {
|
||||
let children = read_dir(&path);
|
||||
let children_tokens = quote! {
|
||||
vec![#(#children),*]
|
||||
};
|
||||
|
||||
entries.push(quote! {
|
||||
::embed::DirEntry(::embed::Dir {
|
||||
children: #children_tokens,
|
||||
path: ::std::path::PathBuf::from(#path_str),
|
||||
})
|
||||
});
|
||||
} else if filetype.is_file() {
|
||||
entries.push(quote! {
|
||||
::embed::DirEntry(::embed::File {
|
||||
content: ::include_bytes!(#path_str),
|
||||
path: ::std::path::PathBuf::from(#path_str),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
138
src/dir.rs
138
src/dir.rs
|
@ -1,138 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}};
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
// mod dir;
|
||||
// pub use dir::*;
|
||||
|
||||
mod file;
|
||||
pub use file::*;
|
Loading…
Add table
Add a link
Reference in a new issue