include_dir-0.7.3/.cargo_vcs_info.json0000644000000001510000000000100133210ustar { "git": { "sha1": "1e76b91179babd98ad9d709ee7af1e39631d7d8d" }, "path_in_vcs": "include_dir" }include_dir-0.7.3/Cargo.toml0000644000000023400000000000100113210ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56" name = "include_dir" version = "0.7.3" authors = ["Michael Bryan "] description = "Embed the contents of a directory in your binary" readme = "README.md" keywords = [ "assets", "include", "embed", "dir", ] categories = [ "development-tools", "web-programming", "game-engines", ] license = "MIT" repository = "https://github.com/Michael-F-Bryan/include_dir" resolver = "1" [package.metadata.docs.rs] all-features = true [dependencies.glob] version = "0.3" optional = true [dependencies.include_dir_macros] version = "^0.7.0" [dev-dependencies.tempfile] version = "3" [features] default = [] metadata = ["include_dir_macros/metadata"] nightly = ["include_dir_macros/nightly"] include_dir-0.7.3/Cargo.toml.orig0000644000000013450000000000100122640ustar [package] authors = ["Michael Bryan "] name = "include_dir" version = "0.7.3" description = "Embed the contents of a directory in your binary" license = "MIT" readme = "README.md" keywords = ["assets", "include", "embed", "dir"] repository = "https://github.com/Michael-F-Bryan/include_dir" categories = ["development-tools", "web-programming", "game-engines"] edition = "2021" rust-version = "1.56" [dependencies] glob = { version = "0.3", optional = true } include_dir_macros = { version = "^0.7.0", path = "../macros" } [dev-dependencies] tempfile = "3" [features] default = [] nightly = ["include_dir_macros/nightly"] metadata = ["include_dir_macros/metadata"] [package.metadata.docs.rs] all-features = true include_dir-0.7.3/Cargo.toml.orig000064400000000000000000000013451046102023000150060ustar 00000000000000[package] authors = ["Michael Bryan "] name = "include_dir" version = "0.7.3" description = "Embed the contents of a directory in your binary" license = "MIT" readme = "README.md" keywords = ["assets", "include", "embed", "dir"] repository = "https://github.com/Michael-F-Bryan/include_dir" categories = ["development-tools", "web-programming", "game-engines"] edition = "2021" rust-version = "1.56" [dependencies] glob = { version = "0.3", optional = true } include_dir_macros = { version = "^0.7.0", path = "../macros" } [dev-dependencies] tempfile = "3" [features] default = [] nightly = ["include_dir_macros/nightly"] metadata = ["include_dir_macros/metadata"] [package.metadata.docs.rs] all-features = true include_dir-0.7.3/README.md000064400000000000000000000034341046102023000133770ustar 00000000000000# include_dir [![Continuous Integration](https://github.com/Michael-F-Bryan/include_dir/actions/workflows/main.yml/badge.svg)](https://github.com/Michael-F-Bryan/include_dir/actions/workflows/main.yml) [![license](https://img.shields.io/github/license/Michael-F-Bryan/include_dir.svg)](LICENSE) [![Crates.io](https://img.shields.io/crates/v/include_dir.svg)](https://crates.io/crates/include_dir) [![Docs.rs](https://docs.rs/include_dir/badge.svg)](https://docs.rs/include_dir/) An evolution of the `include_str!()` and `include_bytes!()` macros for embedding an entire directory tree into your binary. Rendered Documentation: - [master](https://michael-f-bryan.github.io/include_dir) - [Latest Release](https://docs.rs/include_dir/) ## Getting Started The `include_dir!()` macro works very similarly to the normal `include_str!()` and `include_bytes!()` macros. You pass the macro a file path and assign the returned value to some `static` variable. ```rust use include_dir::{include_dir, Dir}; static PROJECT_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR"); // of course, you can retrieve a file by its full path let lib_rs = PROJECT_DIR.get_file("src/lib.rs").unwrap(); // you can also inspect the file's contents let body = lib_rs.contents_utf8().unwrap(); assert!(body.contains("SOME_INTERESTING_STRING")); // you can search for files (and directories) using glob patterns #[cfg(feature = "glob")] { let glob = "**/*.rs"; for entry in PROJECT_DIR.find(glob).unwrap() { println!("Found {}", entry.path().display()); } } ``` ## Features - Embed a directory tree into your binary at compile time - Find a file in the embedded directory - Search for files using a glob pattern (requires the `globs` feature) - File metadata (requires the `metadata` feature) To-Do list: - Compression? include_dir-0.7.3/src/dir.rs000064400000000000000000000055151046102023000140350ustar 00000000000000use crate::{file::File, DirEntry}; use std::fs; use std::path::Path; /// A directory. #[derive(Debug, Clone, PartialEq)] pub struct Dir<'a> { path: &'a str, entries: &'a [DirEntry<'a>], } impl<'a> Dir<'a> { /// Create a new [`Dir`]. pub const fn new(path: &'a str, entries: &'a [DirEntry<'a>]) -> Self { Dir { path, entries } } /// The full path for this [`Dir`], relative to the directory passed to /// [`crate::include_dir!()`]. pub fn path(&self) -> &'a Path { Path::new(self.path) } /// The entries within this [`Dir`]. pub const fn entries(&self) -> &'a [DirEntry<'a>] { self.entries } /// Get a list of the files in this directory. pub fn files(&self) -> impl Iterator> + 'a { self.entries().iter().filter_map(DirEntry::as_file) } /// Get a list of the sub-directories inside this directory. pub fn dirs(&self) -> impl Iterator> + 'a { self.entries().iter().filter_map(DirEntry::as_dir) } /// Recursively search for a [`DirEntry`] with a particular path. pub fn get_entry>(&self, path: S) -> Option<&'a DirEntry<'a>> { let path = path.as_ref(); for entry in self.entries() { if entry.path() == path { return Some(entry); } if let DirEntry::Dir(d) = entry { if let Some(nested) = d.get_entry(path) { return Some(nested); } } } None } /// Look up a file by name. pub fn get_file>(&self, path: S) -> Option<&'a File<'a>> { self.get_entry(path).and_then(DirEntry::as_file) } /// Look up a dir by name. pub fn get_dir>(&self, path: S) -> Option<&'a Dir<'a>> { self.get_entry(path).and_then(DirEntry::as_dir) } /// Does this directory contain `path`? pub fn contains>(&self, path: S) -> bool { self.get_entry(path).is_some() } /// Create directories and extract all files to real filesystem. /// Creates parent directories of `path` if they do not already exist. /// Fails if some files already exist. /// In case of error, partially extracted directory may remain on the filesystem. pub fn extract>(&self, base_path: S) -> std::io::Result<()> { let base_path = base_path.as_ref(); for entry in self.entries() { let path = base_path.join(entry.path()); match entry { DirEntry::Dir(d) => { fs::create_dir_all(&path)?; d.extract(base_path)?; } DirEntry::File(f) => { fs::write(path, f.contents())?; } } } Ok(()) } } include_dir-0.7.3/src/dir_entry.rs000064400000000000000000000021541046102023000152520ustar 00000000000000use crate::{Dir, File}; use std::path::Path; /// A directory entry, roughly analogous to [`std::fs::DirEntry`]. #[derive(Debug, Clone, PartialEq)] pub enum DirEntry<'a> { /// A directory. Dir(Dir<'a>), /// A file. File(File<'a>), } impl<'a> DirEntry<'a> { /// The [`DirEntry`]'s full path. pub fn path(&self) -> &'a Path { match self { DirEntry::Dir(d) => d.path(), DirEntry::File(f) => f.path(), } } /// Try to get this as a [`Dir`], if it is one. pub fn as_dir(&self) -> Option<&Dir<'a>> { match self { DirEntry::Dir(d) => Some(d), DirEntry::File(_) => None, } } /// Try to get this as a [`File`], if it is one. pub fn as_file(&self) -> Option<&File<'a>> { match self { DirEntry::File(f) => Some(f), DirEntry::Dir(_) => None, } } /// Get this item's sub-items, if it has any. pub fn children(&self) -> &'a [DirEntry<'a>] { match self { DirEntry::Dir(d) => d.entries(), DirEntry::File(_) => &[], } } } include_dir-0.7.3/src/file.rs000064400000000000000000000036651046102023000142020ustar 00000000000000use std::{ fmt::{self, Debug, Formatter}, path::Path, }; /// A file with its contents stored in a `&'static [u8]`. #[derive(Clone, PartialEq, Eq)] pub struct File<'a> { path: &'a str, contents: &'a [u8], #[cfg(feature = "metadata")] metadata: Option, } impl<'a> File<'a> { /// Create a new [`File`]. pub const fn new(path: &'a str, contents: &'a [u8]) -> Self { File { path, contents, #[cfg(feature = "metadata")] metadata: None, } } /// The full path for this [`File`], relative to the directory passed to /// [`crate::include_dir!()`]. pub fn path(&self) -> &'a Path { Path::new(self.path) } /// The file's raw contents. pub fn contents(&self) -> &[u8] { self.contents } /// The file's contents interpreted as a string. pub fn contents_utf8(&self) -> Option<&str> { std::str::from_utf8(self.contents()).ok() } } #[cfg(feature = "metadata")] impl<'a> File<'a> { /// Set the [`Metadata`] associated with a [`File`]. pub const fn with_metadata(self, metadata: crate::Metadata) -> Self { let File { path, contents, .. } = self; File { path, contents, metadata: Some(metadata), } } /// Get the [`File`]'s [`Metadata`], if available. pub fn metadata(&self) -> Option<&crate::Metadata> { self.metadata.as_ref() } } impl<'a> Debug for File<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let File { path, contents, #[cfg(feature = "metadata")] metadata, } = self; let mut d = f.debug_struct("File"); d.field("path", path) .field("contents", &format!("<{} bytes>", contents.len())); #[cfg(feature = "metadata")] d.field("metadata", metadata); d.finish() } } include_dir-0.7.3/src/globs.rs000064400000000000000000000017331046102023000143630ustar 00000000000000use crate::{Dir, DirEntry}; use glob::{Pattern, PatternError}; impl<'a> Dir<'a> { /// Search for a file or directory with a glob pattern. pub fn find(&self, glob: &str) -> Result>, PatternError> { let pattern = Pattern::new(glob)?; Ok(Globs::new(pattern, self)) } } #[derive(Debug, Clone, PartialEq)] struct Globs<'a> { stack: Vec<&'a DirEntry<'a>>, pattern: Pattern, } impl<'a> Globs<'a> { pub(crate) fn new(pattern: Pattern, root: &Dir<'a>) -> Globs<'a> { let stack = root.entries().iter().collect(); Globs { stack, pattern } } } impl<'a> Iterator for Globs<'a> { type Item = &'a DirEntry<'a>; fn next(&mut self) -> Option { while let Some(item) = self.stack.pop() { self.stack.extend(item.children()); if self.pattern.matches_path(item.path()) { return Some(item); } } None } } include_dir-0.7.3/src/lib.rs000064400000000000000000000100251046102023000140150ustar 00000000000000//! An extension to the `include_str!()` and `include_bytes!()` macro for //! embedding an entire directory tree into your binary. //! //! # Environment Variables //! //! When invoking the [`include_dir!()`] macro you should try to avoid using //! relative paths because `rustc` makes no guarantees about the current //! directory when it is running a procedural macro. //! //! Environment variable interpolation can be used to remedy this. You might //! want to read the [*Environment Variables*][cargo-vars] section of *The //! Cargo Book* for a list of variables provided by `cargo`. //! //! Most crates will want to use the `$CARGO_MANIFEST_DIR` or `$OUT_DIR` //! variables. For example, to include a folder relative to your crate you might //! use `include_dir!("$CARGO_MANIFEST_DIR/assets")`. //! //! # Examples //! //! Here is an example that embeds the `include_dir` crate's source code in a //! `static` so we can play around with it. //! //! ```rust //! use include_dir::{include_dir, Dir}; //! use std::path::Path; //! //! static PROJECT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR"); //! //! // of course, you can retrieve a file by its full path //! let lib_rs = PROJECT_DIR.get_file("src/lib.rs").unwrap(); //! //! // you can also inspect the file's contents //! let body = lib_rs.contents_utf8().unwrap(); //! assert!(body.contains("SOME_INTERESTING_STRING")); //! //! // if you enable the `glob` feature, you can for files (and directories) using glob patterns //! #[cfg(feature = "glob")] //! { //! let glob = "**/*.rs"; //! for entry in PROJECT_DIR.find(glob).unwrap() { //! println!("Found {}", entry.path().display()); //! } //! } //! ``` //! //! # Features //! //! This library exposes a couple feature flags for enabling and disabling extra //! functionality. These are: //! //! - `glob` - search for files using glob patterns //! - `metadata` - include some basic filesystem metadata like last modified //! time. This is not enabled by default to allow for more reproducible builds //! and to hide potentially identifying information. //! - `nightly` - enables nightly APIs like [`track_path`][track-path] //! and [`proc_macro_tracked_env`][tracked-env]. This gives the compiler //! more information about what is accessed by the procedural macro, enabling //! better caching. **Functionality behind this feature flag is unstable and //! may change or stop compiling at any time.** //! //! # Compile Time Considerations //! //! While the `include_dir!()` macro executes relatively quickly, it expands //! to a fairly large amount of code (all your files are essentially embedded //! as Rust byte strings) and this may have a flow-on effect on the build //! process. //! //! In particular, including a large number or files or files which are //! particularly big may cause the compiler to use large amounts of RAM or spend //! a long time parsing your crate. //! //! As one data point, this crate's `target/` directory contained 620 files with //! a total of 64 MB, with a full build taking about 1.5 seconds and 200MB of //! RAM to generate a 7MB binary. //! //! Using `include_dir!("target/")` increased the compile time to 5 seconds //! and used 730MB of RAM, generating a 72MB binary. //! //! [tracked-env]: https://github.com/rust-lang/rust/issues/74690 //! [track-path]: https://github.com/rust-lang/rust/issues/73921 //! [cargo-vars]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates #![deny( elided_lifetimes_in_paths, future_incompatible, missing_copy_implementations, missing_debug_implementations, missing_docs, rust_2018_idioms )] #![cfg_attr(feature = "nightly", feature(doc_cfg))] mod dir; mod dir_entry; mod file; #[cfg(feature = "metadata")] mod metadata; #[cfg(feature = "glob")] mod globs; #[cfg(feature = "metadata")] pub use crate::metadata::Metadata; pub use crate::{dir::Dir, dir_entry::DirEntry, file::File}; pub use include_dir_macros::include_dir; #[doc = include_str!("../README.md")] #[allow(dead_code)] fn check_readme_examples() {} include_dir-0.7.3/src/metadata.rs000064400000000000000000000021671046102023000150370ustar 00000000000000use std::time::{Duration, SystemTime}; /// Basic metadata for a file. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Metadata { accessed: Duration, created: Duration, modified: Duration, } impl Metadata { /// Create a new [`Metadata`] using the number of seconds since the /// [`SystemTime::UNIX_EPOCH`]. pub const fn new(accessed: Duration, created: Duration, modified: Duration) -> Self { Metadata { accessed, created, modified, } } /// Get the time this file was last accessed. /// /// See also: [`std::fs::Metadata::accessed()`]. pub fn accessed(&self) -> SystemTime { SystemTime::UNIX_EPOCH + self.accessed } /// Get the time this file was created. /// /// See also: [`std::fs::Metadata::created()`]. pub fn created(&self) -> SystemTime { SystemTime::UNIX_EPOCH + self.created } /// Get the time this file was last modified. /// /// See also: [`std::fs::Metadata::modified()`]. pub fn modified(&self) -> SystemTime { SystemTime::UNIX_EPOCH + self.modified } }