diff --git a/src/bin/cook.rs b/src/bin/cook.rs
index 0908b4653c9c32a013700b585a357ab59155a3ba..335d021450a39c3207b774f1be75b4ad9fe5478a 100644
--- a/src/bin/cook.rs
+++ b/src/bin/cook.rs
@@ -1,9 +1,8 @@
 use cookbook::blake3::blake3_progress;
-use cookbook::recipe::{Recipe, SourceRecipe, BuildKind, BuildRecipe, PackageRecipe};
+use cookbook::recipe::{BuildKind, BuildRecipe, PackageRecipe, Recipe, SourceRecipe};
 use cookbook::recipe_find::recipe_find;
 use std::{
-    env,
-    fs,
+    env, fs,
     io::{self, Write},
     path::{Path, PathBuf},
     process::{self, Command, Stdio},
@@ -17,21 +16,13 @@ fn remove_all(path: &Path) -> Result<(), String> {
         fs::remove_dir_all(path)
     } else {
         fs::remove_file(path)
-    }.map_err(|err| format!(
-        "failed to remove '{}': {}\n{:?}",
-        path.display(),
-        err,
-        err
-    ))
+    }
+    .map_err(|err| format!("failed to remove '{}': {}\n{:?}", path.display(), err, err))
 }
 
 fn create_dir(dir: &Path) -> Result<(), String> {
-    fs::create_dir(dir).map_err(|err| format!(
-        "failed to create '{}': {}\n{:?}",
-        dir.display(),
-        err,
-        err
-    ))
+    fs::create_dir(dir)
+        .map_err(|err| format!("failed to create '{}': {}\n{:?}", dir.display(), err, err))
 }
 
 fn create_dir_clean(dir: &Path) -> Result<(), String> {
@@ -41,22 +32,43 @@ fn create_dir_clean(dir: &Path) -> Result<(), String> {
     create_dir(dir)
 }
 
+fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
+    fs::create_dir_all(&dst)?;
+    for entry in fs::read_dir(src)? {
+        let entry = entry?;
+        let ty = entry.file_type()?;
+        if ty.is_dir() {
+            copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
+        } else {
+            fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
+        }
+    }
+    Ok(())
+}
+
 fn modified(path: &Path) -> Result<SystemTime, String> {
-    let metadata = fs::metadata(path).map_err(|err| format!(
-        "failed to get metadata of '{}': {}\n{:#?}",
-        path.display(),
-        err,
-        err
-    ))?;
-    metadata.modified().map_err(|err| format!(
-        "failed to get modified time of '{}': {}\n{:#?}",
-        path.display(),
-        err,
-        err
-    ))
+    let metadata = fs::metadata(path).map_err(|err| {
+        format!(
+            "failed to get metadata of '{}': {}\n{:#?}",
+            path.display(),
+            err,
+            err
+        )
+    })?;
+    metadata.modified().map_err(|err| {
+        format!(
+            "failed to get modified time of '{}': {}\n{:#?}",
+            path.display(),
+            err,
+            err
+        )
+    })
 }
 
-fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(dir: &Path, filter: F) -> io::Result<SystemTime> {
+fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(
+    dir: &Path,
+    filter: F,
+) -> io::Result<SystemTime> {
     let mut newest = fs::metadata(dir)?.modified()?;
     for entry_res in WalkDir::new(dir).into_iter().filter_entry(filter) {
         let entry = entry_res?;
@@ -69,48 +81,55 @@ fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(dir: &Path, filter: F) -> io:
 }
 
 fn modified_dir(dir: &Path) -> Result<SystemTime, String> {
-    modified_dir_inner(dir, |_| true).map_err(|err| format!(
-        "failed to get modified time of '{}': {}\n{:#?}",
-        dir.display(),
-        err,
-        err
-    ))
+    modified_dir_inner(dir, |_| true).map_err(|err| {
+        format!(
+            "failed to get modified time of '{}': {}\n{:#?}",
+            dir.display(),
+            err,
+            err
+        )
+    })
 }
 
 fn modified_dir_ignore_git(dir: &Path) -> Result<SystemTime, String> {
     modified_dir_inner(dir, |entry| {
-        entry.file_name().to_str().map(|s| s != ".git").unwrap_or(true)
-    }).map_err(|err| format!(
-        "failed to get modified time of '{}': {}\n{:#?}",
-        dir.display(),
-        err,
-        err
-    ))
+        entry
+            .file_name()
+            .to_str()
+            .map(|s| s != ".git")
+            .unwrap_or(true)
+    })
+    .map_err(|err| {
+        format!(
+            "failed to get modified time of '{}': {}\n{:#?}",
+            dir.display(),
+            err,
+            err
+        )
+    })
 }
 
 fn rename(src: &Path, dst: &Path) -> Result<(), String> {
-    fs::rename(src, dst).map_err(|err| format!(
-        "failed to rename '{}' to '{}': {}\n{:?}",
-        src.display(),
-        dst.display(),
-        err,
-        err
-    ))
+    fs::rename(src, dst).map_err(|err| {
+        format!(
+            "failed to rename '{}' to '{}': {}\n{:?}",
+            src.display(),
+            dst.display(),
+            err,
+            err
+        )
+    })
 }
 
 fn run_command(mut command: process::Command) -> Result<(), String> {
-    let status = command.status().map_err(|err| format!(
-        "failed to run {:?}: {}\n{:#?}",
-        command,
-        err,
-        err
-    ))?;
-
-    if ! status.success() {
+    let status = command
+        .status()
+        .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?;
+
+    if !status.success() {
         return Err(format!(
             "failed to run {:?}: exited with status {}",
-            command,
-            status
+            command, status
         ));
     }
 
@@ -120,39 +139,29 @@ fn run_command(mut command: process::Command) -> Result<(), String> {
 fn run_command_stdin(mut command: process::Command, stdin_data: &[u8]) -> Result<(), String> {
     command.stdin(Stdio::piped());
 
-    let mut child = command.spawn().map_err(|err| format!(
-        "failed to spawn {:?}: {}\n{:#?}",
-        command,
-        err,
-        err
-    ))?;
+    let mut child = command
+        .spawn()
+        .map_err(|err| format!("failed to spawn {:?}: {}\n{:#?}", command, err, err))?;
 
     if let Some(ref mut stdin) = child.stdin {
-        stdin.write_all(stdin_data).map_err(|err| format!(
-            "failed to write stdin of {:?}: {}\n{:#?}",
-            command,
-            err,
-            err
-        ))?;
+        stdin.write_all(stdin_data).map_err(|err| {
+            format!(
+                "failed to write stdin of {:?}: {}\n{:#?}",
+                command, err, err
+            )
+        })?;
     } else {
-        return Err(format!(
-            "failed to find stdin of {:?}",
-            command
-        ));
+        return Err(format!("failed to find stdin of {:?}", command));
     }
 
-    let status = child.wait().map_err(|err| format!(
-        "failed to run {:?}: {}\n{:#?}",
-        command,
-        err,
-        err
-    ))?;
+    let status = child
+        .wait()
+        .map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?;
 
-    if ! status.success() {
+    if !status.success() {
         return Err(format!(
             "failed to run {:?}: exited with status {}",
-            command,
-            status
+            command, status
         ));
     }
 
@@ -163,27 +172,44 @@ fn fetch(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf, St
     let source_dir = recipe_dir.join("source");
     match source {
         Some(SourceRecipe::SameAs { same_as }) => {
-            if ! source_dir.is_symlink() {
+            if !source_dir.is_symlink() {
                 if source_dir.is_dir() {
                     return Err(format!(
                         "'{dir}' is a directory, but recipe indicated a symlink. \n\
                         try removing '{dir}' if you haven't made any changes that would be lost",
-                        dir=source_dir.display(),
+                        dir = source_dir.display(),
                     ));
                 }
                 let original = Path::new(same_as).join("source");
-                std::os::unix::fs::symlink(&original, &source_dir).map_err(|err| format!(
-                    "failed to symlink '{}' to '{}': {}\n{:?}",
-                    original.display(),
-                    source_dir.display(),
-                    err,
-                    err
-                ))?;
+                std::os::unix::fs::symlink(&original, &source_dir).map_err(|err| {
+                    format!(
+                        "failed to symlink '{}' to '{}': {}\n{:?}",
+                        original.display(),
+                        source_dir.display(),
+                        err,
+                        err
+                    )
+                })?;
             }
         }
-        Some(SourceRecipe::Git { git, upstream, branch, rev }) => {
+        Some(SourceRecipe::Path { path }) => {
+            copy_dir_all(path, &source_dir).map_err(|e| {
+                format!(
+                    "Couldn't copy source from {} to {}: {}",
+                    path,
+                    source_dir.display(),
+                    e
+                )
+            })?;
+        }
+        Some(SourceRecipe::Git {
+            git,
+            upstream,
+            branch,
+            rev,
+        }) => {
             //TODO: use libgit?
-            if ! source_dir.is_dir() {
+            if !source_dir.is_dir() {
                 // Create source.tmp
                 let source_dir_tmp = recipe_dir.join("source.tmp");
                 create_dir_clean(&source_dir_tmp)?;
@@ -202,7 +228,7 @@ fn fetch(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf, St
             } else {
                 // Don't let this code reset the origin for the cookbook repo
                 let source_git_dir = source_dir.join(".git");
-                if ! source_git_dir.is_dir() {
+                if !source_git_dir.is_dir() {
                     return Err(format!(
                         "'{}' is not a git repository, but recipe indicated git source",
                         source_dir.display(),
@@ -238,7 +264,8 @@ fn fetch(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf, St
             } else {
                 //TODO: complicated stuff to check and reset branch to origin
                 let mut command = Command::new("bash");
-                command.arg("-c").arg(r#"
+                command.arg("-c").arg(
+                    r#"
 ORIGIN_BRANCH="$(git branch --remotes | grep '^  origin/HEAD -> ' | cut -d ' ' -f 5-)"
 if [ -n "$BRANCH" ]
 then
@@ -248,7 +275,8 @@ fi
 if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ]
 then
     git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH"
-fi"#);
+fi"#,
+                );
                 if let Some(branch) = branch {
                     command.env("BRANCH", branch);
                 }
@@ -265,15 +293,24 @@ fi"#);
             // Update submodules
             let mut command = Command::new("git");
             command.arg("-C").arg(&source_dir);
-            command.arg("submodule").arg("update").arg("--init").arg("--recursive");
+            command
+                .arg("submodule")
+                .arg("update")
+                .arg("--init")
+                .arg("--recursive");
             run_command(command)?;
-        },
-        Some(SourceRecipe::Tar { tar, blake3, patches, script }) => {
-            if ! source_dir.is_dir() {
+        }
+        Some(SourceRecipe::Tar {
+            tar,
+            blake3,
+            patches,
+            script,
+        }) => {
+            if !source_dir.is_dir() {
                 // Download tar
                 //TODO: replace wget
                 let source_tar = recipe_dir.join("source.tar");
-                if ! source_tar.is_file() {
+                if !source_tar.is_file() {
                     let source_tar_tmp = recipe_dir.join("source.tar.tmp");
 
                     let mut command = Command::new("wget");
@@ -286,19 +323,20 @@ fi"#);
                 }
 
                 // Calculate blake3
-                let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| format!(
-                    "failed to calculate blake3 of '{}': {}\n{:?}",
-                    source_tar.display(),
-                    err,
-                    err
-                ))?;
+                let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| {
+                    format!(
+                        "failed to calculate blake3 of '{}': {}\n{:?}",
+                        source_tar.display(),
+                        err,
+                        err
+                    )
+                })?;
                 if let Some(blake3) = blake3 {
                     // Check if it matches recipe
                     if &source_tar_blake3 != blake3 {
                         return Err(format!(
                             "calculated blake3 '{}' does not match recipe blake3 '{}'",
-                            source_tar_blake3,
-                            blake3
+                            source_tar_blake3, blake3
                         ));
                     }
                 } else {
@@ -327,19 +365,21 @@ fi"#);
                 // Apply patches
                 for patch_name in patches {
                     let patch_file = recipe_dir.join(patch_name);
-                    if ! patch_file.is_file() {
+                    if !patch_file.is_file() {
                         return Err(format!(
                             "failed to find patch file '{}'",
                             patch_file.display()
                         ));
                     }
 
-                    let patch = fs::read_to_string(&patch_file).map_err(|err| format!(
-                        "failed to read patch file '{}': {}\n{:#?}",
-                        patch_file.display(),
-                        err,
-                        err
-                    ))?;
+                    let patch = fs::read_to_string(&patch_file).map_err(|err| {
+                        format!(
+                            "failed to read patch file '{}': {}\n{:#?}",
+                            patch_file.display(),
+                            err,
+                            err
+                        )
+                    })?;
 
                     let mut command = Command::new("patch");
                     command.arg("--directory").arg(&source_dir_tmp);
@@ -358,47 +398,66 @@ fi"#);
                 // Move source.tmp to source atomically
                 rename(&source_dir_tmp, &source_dir)?;
             }
-        },
+        }
         // Local Sources
         None => {
-            if ! source_dir.is_dir() {
+            if !source_dir.is_dir() {
                 eprintln!(
                     "WARNING: Recipe without source section expected source dir at '{}'",
                     source_dir.display(),
                 );
                 create_dir(&source_dir)?;
             }
-        },
+        }
     }
 
     Ok(source_dir)
 }
 
-fn build(recipe_dir: &Path, source_dir: &Path, target_dir: &Path, build: &BuildRecipe) -> Result<PathBuf, String> {
+fn build(
+    recipe_dir: &Path,
+    source_dir: &Path,
+    target_dir: &Path,
+    build: &BuildRecipe,
+) -> Result<PathBuf, String> {
     let mut dep_pkgars = vec![];
     for dependency in build.dependencies.iter() {
         //TODO: sanitize name
         let dependency_dir = recipe_find(dependency, Path::new("recipes"))?;
         if dependency_dir.is_none() {
-            return Err(format!(
-                "failed to find recipe directory '{}'",
-                dependency
-            ));
+            return Err(format!("failed to find recipe directory '{}'", dependency));
         }
-        dep_pkgars.push(dependency_dir.unwrap().join("target").join(redoxer::target()).join("stage.pkgar"));
+        dep_pkgars.push(
+            dependency_dir
+                .unwrap()
+                .join("target")
+                .join(redoxer::target())
+                .join("stage.pkgar"),
+        );
     }
 
     let source_modified = modified_dir_ignore_git(source_dir)?;
-    let deps_modified = dep_pkgars.iter().map(|pkgar| modified(pkgar)).max().unwrap_or(Ok(SystemTime::UNIX_EPOCH))?;
+    let deps_modified = dep_pkgars
+        .iter()
+        .map(|pkgar| modified(pkgar))
+        .max()
+        .unwrap_or(Ok(SystemTime::UNIX_EPOCH))?;
 
     let sysroot_dir = target_dir.join("sysroot");
     // Rebuild sysroot if source is newer
     //TODO: rebuild on recipe changes
-    if sysroot_dir.is_dir() && (modified_dir(&sysroot_dir)? < source_modified || modified_dir(&sysroot_dir)? < deps_modified) {
-        eprintln!("DEBUG: '{}' newer than '{}'", source_dir.display(), sysroot_dir.display());
+    if sysroot_dir.is_dir()
+        && (modified_dir(&sysroot_dir)? < source_modified
+            || modified_dir(&sysroot_dir)? < deps_modified)
+    {
+        eprintln!(
+            "DEBUG: '{}' newer than '{}'",
+            source_dir.display(),
+            sysroot_dir.display()
+        );
         remove_all(&sysroot_dir)?;
     }
-    if ! sysroot_dir.is_dir() {
+    if !sysroot_dir.is_dir() {
         // Create sysroot.tmp
         let sysroot_dir_tmp = target_dir.join("sysroot.tmp");
         create_dir_clean(&sysroot_dir_tmp)?;
@@ -413,13 +472,16 @@ fn build(recipe_dir: &Path, source_dir: &Path, target_dir: &Path, build: &BuildR
             pkgar::extract(
                 public_path,
                 &archive_path,
-                sysroot_dir_tmp.to_str().unwrap()
-            ).map_err(|err| format!(
-                "failed to install '{}' in '{}': {:?}",
-                archive_path.display(),
-                sysroot_dir_tmp.display(),
-                err
-            ))?;
+                sysroot_dir_tmp.to_str().unwrap(),
+            )
+            .map_err(|err| {
+                format!(
+                    "failed to install '{}' in '{}': {:?}",
+                    archive_path.display(),
+                    sysroot_dir_tmp.display(),
+                    err
+                )
+            })?;
         }
 
         // Move sysroot.tmp to sysroot atomically
@@ -429,12 +491,19 @@ fn build(recipe_dir: &Path, source_dir: &Path, target_dir: &Path, build: &BuildR
     let stage_dir = target_dir.join("stage");
     // Rebuild stage if source is newer
     //TODO: rebuild on recipe changes
-    if stage_dir.is_dir() && (modified_dir(&stage_dir)? < source_modified || modified_dir(&stage_dir)? < deps_modified) {
-        eprintln!("DEBUG: '{}' newer than '{}'", source_dir.display(), stage_dir.display());
+    if stage_dir.is_dir()
+        && (modified_dir(&stage_dir)? < source_modified
+            || modified_dir(&stage_dir)? < deps_modified)
+    {
+        eprintln!(
+            "DEBUG: '{}' newer than '{}'",
+            source_dir.display(),
+            stage_dir.display()
+        );
         remove_all(&stage_dir)?;
     }
 
-    if ! stage_dir.is_dir() {
+    if !stage_dir.is_dir() {
         // Create stage.tmp
         let stage_dir_tmp = target_dir.join("stage.tmp");
         create_dir_clean(&stage_dir_tmp)?;
@@ -442,7 +511,7 @@ fn build(recipe_dir: &Path, source_dir: &Path, target_dir: &Path, build: &BuildR
         // Create build, if it does not exist
         //TODO: flag for clean builds where build is wiped out
         let build_dir = target_dir.join("build");
-        if ! build_dir.is_dir() {
+        if !build_dir.is_dir() {
             create_dir_clean(&build_dir)?;
         }
 
@@ -579,8 +648,14 @@ done
         //TODO: configurable target
         //TODO: Add more configurability, convert scripts to Rust?
         let script = match &build.kind {
-            BuildKind::Cargo { package_path, cargoflags } => {
-                format!("PACKAGE_PATH={} cookbook_cargo {cargoflags}", package_path.as_deref().unwrap_or("."))
+            BuildKind::Cargo {
+                package_path,
+                cargoflags,
+            } => {
+                format!(
+                    "PACKAGE_PATH={} cookbook_cargo {cargoflags}",
+                    package_path.as_deref().unwrap_or(".")
+                )
             }
             BuildKind::Configure => "cookbook_configure".to_owned(),
             BuildKind::Custom { script } => script.clone(),
@@ -590,7 +665,9 @@ done
             //TODO: remove unwraps
             let cookbook_build = build_dir.canonicalize().unwrap();
             let cookbook_recipe = recipe_dir.canonicalize().unwrap();
-            let cookbook_redoxer = Path::new("target/release/cookbook_redoxer").canonicalize().unwrap();
+            let cookbook_redoxer = Path::new("target/release/cookbook_redoxer")
+                .canonicalize()
+                .unwrap();
             let cookbook_root = Path::new(".").canonicalize().unwrap();
             let cookbook_stage = stage_dir_tmp.canonicalize().unwrap();
             let cookbook_source = source_dir.canonicalize().unwrap();
@@ -620,24 +697,27 @@ done
     Ok(stage_dir)
 }
 
-fn package(_recipe_dir: &Path, stage_dir: &Path, target_dir: &Path, _package: &PackageRecipe) -> Result<PathBuf, String> {
+fn package(
+    _recipe_dir: &Path,
+    stage_dir: &Path,
+    target_dir: &Path,
+    _package: &PackageRecipe,
+) -> Result<PathBuf, String> {
     //TODO: metadata like dependencies, name, and version
 
     let secret_path = "build/id_ed25519.toml";
     let public_path = "build/id_ed25519.pub.toml";
-    if ! Path::new(secret_path).is_file() || ! Path::new(public_path).is_file() {
-        if ! Path::new("build").is_dir() {
+    if !Path::new(secret_path).is_file() || !Path::new(public_path).is_file() {
+        if !Path::new("build").is_dir() {
             create_dir(Path::new("build"))?;
         }
         let (public_key, secret_key) = pkgar_keys::SecretKeyFile::new();
-        public_key.save(public_path).map_err(|err| format!(
-            "failed to save pkgar public key: {:?}",
-            err
-        ))?;
-        secret_key.save(secret_path).map_err(|err| format!(
-            "failed to save pkgar secret key: {:?}",
-            err
-        ))?;
+        public_key
+            .save(public_path)
+            .map_err(|err| format!("failed to save pkgar public key: {:?}", err))?;
+        secret_key
+            .save(secret_path)
+            .map_err(|err| format!("failed to save pkgar secret key: {:?}", err))?;
     }
 
     let package_file = target_dir.join("stage.pkgar");
@@ -646,50 +726,48 @@ fn package(_recipe_dir: &Path, stage_dir: &Path, target_dir: &Path, _package: &P
     if package_file.is_file() {
         let stage_modified = modified_dir(stage_dir)?;
         if modified(&package_file)? < stage_modified {
-            eprintln!("DEBUG: '{}' newer than '{}'", stage_dir.display(), package_file.display());
+            eprintln!(
+                "DEBUG: '{}' newer than '{}'",
+                stage_dir.display(),
+                package_file.display()
+            );
             remove_all(&package_file)?;
         }
     }
-    if ! package_file.is_file() {
+    if !package_file.is_file() {
         pkgar::create(
             secret_path,
             package_file.to_str().unwrap(),
-            stage_dir.to_str().unwrap()
-        ).map_err(|err| format!(
-            "failed to create pkgar archive: {:?}",
-            err
-        ))?;
+            stage_dir.to_str().unwrap(),
+        )
+        .map_err(|err| format!("failed to create pkgar archive: {:?}", err))?;
     }
 
     Ok(package_file)
 }
 
 fn cook(recipe_dir: &Path, recipe: &Recipe, fetch_only: bool) -> Result<(), String> {
-    let source_dir = fetch(recipe_dir, &recipe.source).map_err(|err| format!(
-        "failed to fetch: {}",
-        err
-    ))?;
+    let source_dir =
+        fetch(recipe_dir, &recipe.source).map_err(|err| format!("failed to fetch: {}", err))?;
 
-    if fetch_only { return Ok(()); }
+    if fetch_only {
+        return Ok(());
+    }
 
     let target_parent_dir = recipe_dir.join("target");
-    if ! target_parent_dir.is_dir() {
+    if !target_parent_dir.is_dir() {
         create_dir(&target_parent_dir)?;
     }
     let target_dir = target_parent_dir.join(redoxer::target());
-    if ! target_dir.is_dir() {
+    if !target_dir.is_dir() {
         create_dir(&target_dir)?;
     }
 
-    let stage_dir = build(recipe_dir, &source_dir, &target_dir, &recipe.build).map_err(|err| format!(
-        "failed to build: {}",
-        err
-    ))?;
+    let stage_dir = build(recipe_dir, &source_dir, &target_dir, &recipe.build)
+        .map_err(|err| format!("failed to build: {}", err))?;
 
-    let _package_file = package(recipe_dir, &stage_dir, &target_dir, &recipe.package).map_err(|err| format!(
-        "failed to package: {}",
-        err
-    ))?;
+    let _package_file = package(recipe_dir, &stage_dir, &target_dir, &recipe.package)
+        .map_err(|err| format!("failed to package: {}", err))?;
 
     Ok(())
 }
@@ -705,39 +783,33 @@ impl CookRecipe {
         //TODO: sanitize recipe name?
         let dir = recipe_find(&name, Path::new("recipes"))?;
         if dir.is_none() {
-            return Err(format!(
-                "failed to find recipe directory '{}'",
-                name
-            ));
+            return Err(format!("failed to find recipe directory '{}'", name));
         }
         let dir = dir.unwrap();
         let file = dir.join("recipe.toml");
-        if ! file.is_file() {
-            return Err(format!(
-                "failed to find recipe file '{}'",
-                file.display()
-            ));
+        if !file.is_file() {
+            return Err(format!("failed to find recipe file '{}'", file.display()));
         }
 
-        let toml = fs::read_to_string(&file).map_err(|err| format!(
-            "failed to read recipe file '{}': {}\n{:#?}",
-            file.display(),
-            err,
-            err
-        ))?;
+        let toml = fs::read_to_string(&file).map_err(|err| {
+            format!(
+                "failed to read recipe file '{}': {}\n{:#?}",
+                file.display(),
+                err,
+                err
+            )
+        })?;
 
-        let recipe: Recipe = toml::from_str(&toml).map_err(|err| format!(
-            "failed to parse recipe file '{}': {}\n{:#?}",
-            file.display(),
-            err,
-            err
-        ))?;
+        let recipe: Recipe = toml::from_str(&toml).map_err(|err| {
+            format!(
+                "failed to parse recipe file '{}': {}\n{:#?}",
+                file.display(),
+                err,
+                err
+            )
+        })?;
 
-        Ok(Self {
-            name,
-            dir,
-            recipe
-        })
+        Ok(Self { name, dir, recipe })
     }
 
     //TODO: make this more efficient, smarter, and not return duplicates
@@ -753,14 +825,10 @@ impl CookRecipe {
         for name in names {
             let recipe = Self::new(name.clone())?;
 
-            let dependencies = Self::new_recursive(
-                &recipe.recipe.build.dependencies,
-                recursion - 1
-            ).map_err(|err| format!(
-                "{}: failed on loading build dependencies:\n{}",
-                name,
-                err
-            ))?;
+            let dependencies =
+                Self::new_recursive(&recipe.recipe.build.dependencies, recursion - 1).map_err(
+                    |err| format!("{}: failed on loading build dependencies:\n{}", name, err),
+                )?;
 
             for dependency in dependencies {
                 recipes.push(dependency);
@@ -805,7 +873,7 @@ fn main() {
     };
 
     for recipe in recipes {
-        if ! quiet {
+        if !quiet {
             eprintln!(
                 "{}{}cook - {}{}{}",
                 style::Bold,
@@ -817,7 +885,7 @@ fn main() {
         }
 
         let res = if dry_run {
-            if ! quiet {
+            if !quiet {
                 eprintln!("DRY RUN: {:#?}", recipe.recipe);
             }
             Ok(())
@@ -827,7 +895,7 @@ fn main() {
 
         match res {
             Ok(()) => {
-                if ! quiet {
+                if !quiet {
                     eprintln!(
                         "{}{}cook - {} - successful{}{}",
                         style::Bold,
@@ -837,7 +905,7 @@ fn main() {
                         style::Reset,
                     );
                 }
-            },
+            }
             Err(err) => {
                 eprintln!(
                     "{}{}cook - {} - error:{}{} {}",
diff --git a/src/recipe.rs b/src/recipe.rs
index e69742a85efe0fb947cb268a1ce7f2c670b6f731..2f1566e196dd8ae39048fbd8169688bbecf6715e 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -12,6 +12,11 @@ pub enum SourceRecipe {
         /// Relative path to the package for which to reuse the source dir
         same_as: String,
     },
+    /// Path source
+    Path {
+        /// The path to the source
+        path: String,
+    },
     /// A git repository source
     Git {
         /// The URL for the git repository, such as https://gitlab.redox-os.org/redox-os/ion.git
@@ -58,9 +63,7 @@ pub enum BuildKind {
     Configure,
     /// Will build and install using custom commands
     #[serde(rename = "custom")]
-    Custom {
-        script: String,
-    },
+    Custom { script: String },
 }
 
 #[derive(Debug, Deserialize, PartialEq, Serialize)]
@@ -89,14 +92,14 @@ pub struct Recipe {
     pub package: PackageRecipe,
 }
 
-
 #[cfg(test)]
 mod tests {
     #[test]
     fn git_cargo_recipe() {
-        use crate::recipe::{Recipe, SourceRecipe, BuildKind, BuildRecipe, PackageRecipe};
+        use crate::recipe::{BuildKind, BuildRecipe, PackageRecipe, Recipe, SourceRecipe};
 
-        let recipe: Recipe = toml::from_str(r#"
+        let recipe: Recipe = toml::from_str(
+            r#"
             [source]
             git = "https://gitlab.redox-os.org/redox-os/acid.git"
             branch = "master"
@@ -104,30 +107,39 @@ mod tests {
 
             [build]
             template = "cargo"
-        "#).unwrap();
+        "#,
+        )
+        .unwrap();
 
-        assert_eq!(recipe, Recipe {
-            source: Some(SourceRecipe::Git {
-                git: "https://gitlab.redox-os.org/redox-os/acid.git".to_string(),
-                upstream: None,
-                branch: Some("master".to_string()),
-                rev: Some("06344744d3d55a5ac9a62a6059cb363d40699bbc".to_string()),
-            }),
-            build: BuildRecipe {
-                kind: BuildKind::Cargo,
-                dependencies: Vec::new(),
-            },
-            package: PackageRecipe {
-                dependencies: Vec::new(),
-            },
-        });
+        assert_eq!(
+            recipe,
+            Recipe {
+                source: Some(SourceRecipe::Git {
+                    git: "https://gitlab.redox-os.org/redox-os/acid.git".to_string(),
+                    upstream: None,
+                    branch: Some("master".to_string()),
+                    rev: Some("06344744d3d55a5ac9a62a6059cb363d40699bbc".to_string()),
+                }),
+                build: BuildRecipe {
+                    kind: BuildKind::Cargo {
+                        package_path: None,
+                        cargoflags: String::new(),
+                    },
+                    dependencies: Vec::new(),
+                },
+                package: PackageRecipe {
+                    dependencies: Vec::new(),
+                },
+            }
+        );
     }
 
     #[test]
     fn tar_custom_recipe() {
-        use crate::recipe::{Recipe, SourceRecipe, BuildKind, BuildRecipe, PackageRecipe};
+        use crate::recipe::{BuildKind, BuildRecipe, PackageRecipe, Recipe, SourceRecipe};
 
-        let recipe: Recipe = toml::from_str(r#"
+        let recipe: Recipe = toml::from_str(
+            r#"
             [source]
             tar = "http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz"
             sha256 = "8220c0e4082fa26c07b10bfe31f641d2e33ebe1d1bb0b20221b7016bc8b78a3a"
@@ -135,24 +147,32 @@ mod tests {
             [build]
             template = "custom"
             script = "make"
-        "#).unwrap();
+        "#,
+        )
+        .unwrap();
 
-        assert_eq!(recipe, Recipe {
-            source: Some(SourceRecipe::Tar {
-                tar: "http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz".to_string(),
-                blake3: Some("8220c0e4082fa26c07b10bfe31f641d2e33ebe1d1bb0b20221b7016bc8b78a3a".to_string()),
-                patches: Vec::new(),
-                script: None,
-            }),
-            build: BuildRecipe {
-                kind: BuildKind::Custom {
-                    script: "make".to_string()
+        assert_eq!(
+            recipe,
+            Recipe {
+                source: Some(SourceRecipe::Tar {
+                    tar: "http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz".to_string(),
+                    blake3: Some(
+                        "8220c0e4082fa26c07b10bfe31f641d2e33ebe1d1bb0b20221b7016bc8b78a3a"
+                            .to_string()
+                    ),
+                    patches: Vec::new(),
+                    script: None,
+                }),
+                build: BuildRecipe {
+                    kind: BuildKind::Custom {
+                        script: "make".to_string()
+                    },
+                    dependencies: Vec::new(),
+                },
+                package: PackageRecipe {
+                    dependencies: Vec::new(),
                 },
-                dependencies: Vec::new(),
-            },
-            package: PackageRecipe {
-                dependencies: Vec::new(),
-            },
-        });
+            }
+        );
     }
 }