Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: rust
rust:
- stable
- 1.26.0
- 1.31.0
- nightly

cache: cargo
Expand Down
5 changes: 2 additions & 3 deletions include_dir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
authors = ["Michael Bryan <michaelfbryan@gmail.com>"]
name = "include_dir"
version = "0.2.1"
version = "0.2.2"
description = "Embed the contents of a directory in your binary"
license = "MIT"
readme = "../README.md"
Expand All @@ -20,10 +20,9 @@ repository = "Michael-F-Bryan/include_dir"

[dependencies]
glob = "0.2.11"
proc-macro-hack = "0.4"
proc-macro-hack = "0.5"

[dependencies.include_dir_impl]
#version = "0.2.1"
path = "../include_dir_impl"

[features]
Expand Down
123 changes: 55 additions & 68 deletions include_dir/src/dir.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,78 @@
use file::File;
use glob::{Pattern, PatternError};
use globs::{DirEntry, Globs};
use std::path::Path;
use std::path::{Iter as PathIter, Path};
use std::ffi::OsStr;

use crate::file::File;

/// A directory entry.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Dir<'a> {
#[doc(hidden)]
pub path: &'a str,
#[doc(hidden)]
pub files: &'a [File<'a>],
#[doc(hidden)]
pub dirs: &'a [Dir<'a>],
pub enum DirEntry<'a> {
/// A directory.
Dir(Dir<'a>),

/// A file.
File(File<'a>),
}

impl<'a> Dir<'a> {
impl<'a> DirEntry<'a> {
/// Get the directory's path.
pub fn path(&self) -> &'a Path {
Path::new(self.path)
}

/// Get a list of the files in this directory.
pub fn files(&self) -> &'a [File<'a>] {
self.files
}

/// Get a list of the sub-directories inside this directory.
pub fn dirs(&self) -> &'a [Dir<'a>] {
self.dirs
}

/// Does this directory contain `path`?
pub fn contains<S: AsRef<Path>>(&self, path: S) -> bool {
let path = path.as_ref();

self.get_file(path).is_some() || self.get_dir(path).is_some()
match self {
DirEntry::Dir(dir) => dir.path(),
DirEntry::File(file) => file.path(),
}
}

/// Fetch a sub-directory by *exactly* matching its path relative to the
/// directory included with `include_dir!()`.
pub fn get_dir<S: AsRef<Path>>(&self, path: S) -> Option<Dir> {
let path = path.as_ref();

for dir in self.dirs {
if Path::new(dir.path) == path {
return Some(*dir);
}

if let Some(d) = dir.get_dir(path) {
return Some(d);
}
pub(crate) fn file_name(&self) -> &'a OsStr {
match self {
DirEntry::File(file) => file.file_name(),
DirEntry::Dir(dir) => dir.file_name(),
}

None
}

/// Fetch a sub-directory by *exactly* matching its path relative to the
/// directory included with `include_dir!()`.
pub fn get_file<S: AsRef<Path>>(&self, path: S) -> Option<File> {
let path = path.as_ref();

for file in self.files {
if Path::new(file.path) == path {
return Some(*file);
pub(crate) fn get(&self, mut iter: PathIter) -> Option<&DirEntry> {
match (self, iter.next()) {
(entry, None) => {
Some(entry)
}
}

for dir in self.dirs {
if let Some(d) = dir.get_file(path) {
return Some(d);
(DirEntry::File(_), Some(_)) => {
// Can't desend into a file.
None
}
(DirEntry::Dir(dir), Some(name)) => {
if let Ok(pos) = dir.entries.binary_search_by_key(&name, |e| e.file_name()) {
dir.entries[pos].get(iter)
} else {
None
}
}
}

None
}
}

/// Search for a file or directory with a glob pattern.
pub fn find(&self, glob: &str) -> Result<impl Iterator<Item = DirEntry<'a>>, PatternError> {
let pattern = Pattern::new(glob)?;
/// A directory.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Dir<'a> {
#[doc(hidden)]
pub path: &'a str,
#[doc(hidden)]
pub file_name: &'a str,
#[doc(hidden)]
pub entries: &'a [DirEntry<'a>],
}

Ok(Globs::new(pattern, *self))
impl<'a> Dir<'a> {
/// Get the directory's path.
pub fn path(&self) -> &'a Path {
Path::new(self.path)
}

pub(crate) fn dir_entries(&self) -> impl Iterator<Item = DirEntry<'a>> {
let files = self.files().into_iter().map(|f| DirEntry::File(*f));
let dirs = self.dirs().into_iter().map(|d| DirEntry::Dir(*d));
/// The directory's name.
pub fn file_name(&self) -> &'a OsStr {
OsStr::new(self.file_name)
}

files.chain(dirs)
/// Get a list of the entries in this directory.
pub fn entries(&self) -> &'a [DirEntry<'a>] {
self.entries
}
}
9 changes: 9 additions & 0 deletions include_dir/src/file.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::ffi::OsStr;
use std::fmt::{self, Debug, Formatter};
use std::path::Path;
use std::str;
Expand All @@ -8,6 +9,8 @@ pub struct File<'a> {
#[doc(hidden)]
pub path: &'a str,
#[doc(hidden)]
pub file_name: &'a str,
#[doc(hidden)]
pub contents: &'a [u8],
}

Expand All @@ -18,6 +21,11 @@ impl<'a> File<'a> {
Path::new(self.path)
}

/// The file's name.
pub fn file_name(&self) -> &'a OsStr {
OsStr::new(self.file_name)
}

/// The file's raw contents.
pub fn contents(&self) -> &'a [u8] {
self.contents
Expand All @@ -33,6 +41,7 @@ impl<'a> Debug for File<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("File")
.field("path", &self.path)
.field("file_name", &self.file_name)
.field("contents", &format!("<{} bytes>", self.contents.len()))
.finish()
}
Expand Down
50 changes: 50 additions & 0 deletions include_dir/src/fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use glob::{Pattern, PatternError};
use std::path::Path;

use crate::file::File;
use crate::dir::{Dir, DirEntry};
use crate::globs::Globs;

/// The file system that includes all the files and directories included with `include_dir!()`.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct FileSystem<'a> {
#[doc(hidden)]
pub root: DirEntry<'a>,
}

impl<'a> FileSystem<'a> {
/// Does this directory contain `path`?
pub fn contains<S: AsRef<Path>>(&self, path: S) -> bool {
self.get(path).is_some()
}

/// Does this file system contain `path`?
pub fn get<S: AsRef<Path>>(&self, path: S) -> Option<&DirEntry> {
self.root.get(path.as_ref().iter())
}

/// Does this file system contain a file `path`?
pub fn get_file<S: AsRef<Path>>(&self, path: S) -> Option<&File> {
if let Some(DirEntry::File(file)) = self.get(path) {
Some(file)
} else {
None
}
}

/// Does this file system contain a directory `path`?
pub fn get_dir<S: AsRef<Path>>(&self, path: S) -> Option<&Dir> {
if let Some(DirEntry::Dir(dir)) = self.get(path) {
Some(dir)
} else {
None
}
}

/// Search for a file or directory with a glob pattern.
pub fn find(&self, glob: &str) -> Result<impl Iterator<Item = DirEntry<'a>>, PatternError> {
let pattern = Pattern::new(glob)?;

Ok(Globs::new(pattern, self.root))
}
}
40 changes: 15 additions & 25 deletions include_dir/src/globs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use dir::Dir;
use file::File;
use glob::Pattern;
use std::path::Path;

use crate::dir::DirEntry;

#[derive(Debug, Clone, PartialEq)]
pub struct Globs<'a> {
Expand All @@ -10,14 +9,20 @@ pub struct Globs<'a> {
}

impl<'a> Globs<'a> {
pub(crate) fn new(pattern: Pattern, root: Dir<'a>) -> Globs<'a> {
let stack = vec![DirEntry::Dir(root)];
Globs { stack, pattern }
pub(crate) fn new(pattern: Pattern, item: DirEntry<'a>) -> Globs<'a> {
let mut globs = Globs {
stack: vec![],
pattern,
};

globs.fill_buffer(item);

globs
}

fn fill_buffer(&mut self, item: &DirEntry<'a>) {
if let DirEntry::Dir(ref dir) = *item {
self.stack.extend(dir.dir_entries());
fn fill_buffer(&mut self, item: DirEntry<'a>) {
if let DirEntry::Dir(dir) = item {
self.stack.extend(dir.entries());
}
}
}
Expand All @@ -27,7 +32,7 @@ impl<'a> Iterator for Globs<'a> {

fn next(&mut self) -> Option<Self::Item> {
while let Some(item) = self.stack.pop() {
self.fill_buffer(&item);
self.fill_buffer(item);

if self.pattern.matches_path(item.path()) {
return Some(item);
Expand All @@ -37,18 +42,3 @@ impl<'a> Iterator for Globs<'a> {
None
}
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum DirEntry<'a> {
File(File<'a>),
Dir(Dir<'a>),
}

impl<'a> DirEntry<'a> {
pub fn path(&self) -> &'a Path {
match *self {
DirEntry::File(f) => f.path(),
DirEntry::Dir(d) => d.path(),
}
}
}
32 changes: 10 additions & 22 deletions include_dir/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! An extension to the `include_str!()` macro for embedding an entire directory
//! tree into your binary.
//! An extension to the `include_str!()` and `include_bytes!()` macro for embedding an entire
//! directory tree into your binary.
//!
//! # Examples
//!
Expand All @@ -11,10 +11,10 @@
//! #[macro_use]
//! extern crate include_dir;
//!
//! use include_dir::Dir;
//! use include_dir::FileSystem;
//! use std::path::Path;
//!
//! const PROJECT_DIR: Dir = include_dir!(".");
//! const PROJECT_DIR: FileSystem = include_dir!(".");
//!
//! # fn main() {
//! // of course, you can retrieve a file by its full path
Expand All @@ -41,35 +41,23 @@

#![deny(missing_docs, missing_copy_implementations, missing_debug_implementations)]

#[allow(unused_imports)]
#[macro_use]
extern crate include_dir_impl;
#[macro_use]
extern crate proc_macro_hack;
extern crate glob;

mod dir;
mod file;
mod fs;
mod globs;

pub use dir::Dir;
pub use file::File;

#[doc(hidden)]
pub use include_dir_impl::*;
pub use crate::dir::{Dir, DirEntry};
pub use crate::file::File;
pub use crate::fs::FileSystem;

#[macro_export]
#[doc(hidden)]
/// Hack used by `include_dir_impl` which can't access `$crate`.
macro_rules! __include_dir_use_everything {
() => {
pub use $crate::*;
};
}

proc_macro_expr_decl! {
include_dir! => include_dir_impl
}
#[proc_macro_hack]
pub use include_dir_impl::include_dir;

/// Example the output generated when running `include_dir!()` on itself.
#[cfg(feature = "example-output")]
Expand Down
Loading