diff --git a/Cargo.lock b/Cargo.lock
index c82ab83..0b4154a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -297,8 +297,6 @@ dependencies = [
  "mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "phf_macros 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -546,15 +544,6 @@ dependencies = [
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "phf_macros"
-version = "0.7.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "phf_shared"
 version = "0.7.22"
@@ -1126,7 +1115,6 @@ dependencies = [
 "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2"
 "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b"
 "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998"
-"checksum phf_macros 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6cfe846605e58552e1324c566bfb5bd5ac032245c9ae05c859f042e81c03b5"
 "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930"
 "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
 "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
diff --git a/Cargo.toml b/Cargo.toml
index 45c5d95..0f88f8b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,9 +17,6 @@ url = "*"
 reqwest = "0.8.6"
 number_prefix = "0.2.7"
 
-phf = "0.7.22"
-phf_macros = "0.7.22"
-
 serde = "1.0.27"
 serde_derive = "1.0.27"
 serde_json = "1.0.9"
diff --git a/README.md b/README.md
index 8a6cbc1..669eb72 100644
--- a/README.md
+++ b/README.md
@@ -20,10 +20,10 @@ Building
 Add your logo to `static/img/logo.png`, modify the configuration file, then run:
 
 ```bash
-cargo build
+cargo build --release
 ```
 
-LiftInstall should build on both ~~Stable and~~ Nightly Rust.
+LiftInstall should build on both Stable and Nightly Rust.
 
 Contributing
 ------------
diff --git a/src/assets.rs b/src/assets.rs
index 3aa6946..81b514b 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -3,33 +3,21 @@ extern crate mime_guess;
 
 use assets::mime_guess::{get_mime_type, octet_stream};
 
-use phf;
-
 macro_rules! include_files_as_assets {
-    ( $field_name:ident, $( $file_name:expr ),* ) => {
-        static $field_name: phf::Map<&'static str, &'static [u8]> = phf_map!(
+    ( $target_match:expr, $( $file_name:expr ),* ) => {
+        match $target_match {
             $(
-                $file_name => include_bytes!(concat!("../static/", $file_name)),
+                $file_name => Some(include_bytes!(concat!("../static/", $file_name)).as_ref()),
             )*
-        );
+            _ => None
+        }
     }
 }
 
-include_files_as_assets!(
-    ASSETS,
-    "/index.html",
-    "/css/bulma.css",
-    "/css/main.css",
-    "/img/logo.png",
-    "/js/helpers.js",
-    "/js/vue.js",
-    "/js/vue.min.js"
-);
-
 /// Returns a static file based upon a given String as a Path.
 ///
 /// file_path: String path, beginning with a /
-pub fn file_from_string(file_path: &str) -> Option<(String, Vec<u8>)> {
+pub fn file_from_string(file_path: &str) -> Option<(String, &'static [u8])> {
     let guessed_mime = match file_path.rfind(".") {
         Some(ext_ptr) => {
             let ext = &file_path[ext_ptr + 1..];
@@ -41,5 +29,16 @@ pub fn file_from_string(file_path: &str) -> Option<(String, Vec<u8>)> {
 
     let string_mime = guessed_mime.to_string();
 
-    Some((string_mime, (*ASSETS.get(file_path)?).to_owned()))
+    let contents = include_files_as_assets!(
+        file_path,
+        "/index.html",
+        "/css/bulma.css",
+        "/css/main.css",
+        "/img/logo.png",
+        "/js/helpers.js",
+        "/js/vue.js",
+        "/js/vue.min.js"
+    )?;
+
+    Some((string_mime, contents)) //(*ASSETS.get(file_path)?).to_owned()))
 }
diff --git a/src/main.rs b/src/main.rs
index c95ae4b..7180baf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,17 +1,13 @@
 #![windows_subsystem = "windows"]
-#![feature(plugin)]
-#![plugin(phf_macros)]
 
 extern crate web_view;
 
-extern crate hyper;
 extern crate futures;
+extern crate hyper;
 
 extern crate number_prefix;
 extern crate reqwest;
 
-extern crate phf;
-
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
@@ -29,6 +25,7 @@ mod http;
 mod installer;
 mod rest;
 mod sources;
+mod tasks;
 
 use web_view::*;
 
diff --git a/src/sources/github/mod.rs b/src/sources/github/mod.rs
index ec6853f..7d5a005 100644
--- a/src/sources/github/mod.rs
+++ b/src/sources/github/mod.rs
@@ -24,7 +24,7 @@ impl GithubReleases {
 impl ReleaseSource for GithubReleases {
     fn get_current_releases(&self, config: &TomlValue) -> Result<Vec<Release>, String> {
         // Reparse our Config as strongly typed
-        let config : GithubConfig = match config.clone().try_into() {
+        let config: GithubConfig = match config.clone().try_into() {
             Ok(v) => v,
             Err(v) => return Err(format!("Failed to parse release config: {:?}", v)),
         };
@@ -33,7 +33,11 @@ impl ReleaseSource for GithubReleases {
 
         // Build the HTTP client up
         let client = reqwest::Client::new();
-        let mut response = client.get(&format!("https://api.github.com/repos/{}/releases", config.repo))
+        let mut response = client
+            .get(&format!(
+                "https://api.github.com/repos/{}/releases",
+                config.repo
+            ))
             .header(UserAgent::new("liftinstall (j-selby)"))
             .send()
             .map_err(|x| format!("Error while sending HTTP request: {:?}", x))?;
@@ -42,16 +46,16 @@ impl ReleaseSource for GithubReleases {
             return Err(format!("Bad status code: {:?}", response.status()));
         }
 
-        let body = response.text()
+        let body = response
+            .text()
             .map_err(|x| format!("Failed to decode HTTP response body: {:?}", x))?;
 
-        let result: serde_json::Value = serde_json::from_str(&body)
-            .map_err(|x| format!("Failed to parse response: {:?}", x))?;
+        let result: serde_json::Value =
+            serde_json::from_str(&body).map_err(|x| format!("Failed to parse response: {:?}", x))?;
 
-        let result: &Vec<serde_json::Value> =
-            result
-                .as_array()
-                .ok_or(format!("Response was not an array!"))?;
+        let result: &Vec<serde_json::Value> = result
+            .as_array()
+            .ok_or(format!("Response was not an array!"))?;
 
         // Parse JSON from server
         for entry in result.into_iter() {
diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs
new file mode 100644
index 0000000..4846140
--- /dev/null
+++ b/src/tasks/mod.rs
@@ -0,0 +1,106 @@
+use std::any::Any;
+
+use std::fmt;
+use std::fmt::Display;
+
+/// Allows for any time to be used as an Input to a Task.
+type AnyInput = Box<Any>;
+
+/// A Task is a small, async task conforming to a fixed set of inputs/outputs.
+pub trait Task {
+    type Input;
+    type Output;
+    type Error;
+
+    /// Executes this individual task, evaluating to the given Output result.
+    ///
+    /// Each dependency is given an indice in the inputted vector.
+    fn execute(
+        &mut self,
+        input: Vec<Self::Input>,
+        messenger: &Fn(&str, f32),
+    ) -> Result<Self::Output, Self::Error>;
+
+    /// Returns a vector containing all dependencies that need to be executed
+    /// before this task can function.
+    fn dependencies(
+        &self,
+    ) -> Vec<Box<Task<Input = AnyInput, Output = Self::Input, Error = Self::Error>>>;
+
+    /// Returns a short name used for formatting the dependency tree.
+    fn name(&self) -> String;
+}
+
+/// The dependency tree allows for smart iteration on a Task struct.
+pub struct DependencyTree<I, O, E> {
+    task: Box<Task<Input = I, Output = O, Error = E>>,
+    dependencies: Vec<DependencyTree<AnyInput, I, E>>,
+}
+
+impl<I, O, E> DependencyTree<I, O, E> {
+    /// Renders the dependency tree into a user-presentable string.
+    fn render(&self) -> String {
+        let mut buf = self.task.name();
+
+        buf += "\n";
+
+        for i in 0..self.dependencies.len() {
+            let dependencies = self.dependencies[i].render();
+            let dependencies = dependencies.trim();
+
+            if i + 1 == self.dependencies.len() {
+                buf += "└── ";
+                buf += &dependencies.replace("\n", "\n    ");
+            } else {
+                buf += "├── ";
+                buf += &dependencies.replace("\n", "\n│   ");
+                buf += "\n";
+            }
+        }
+
+        buf
+    }
+
+    /// Executes this pipeline.
+    pub fn execute(&mut self, messenger: &Fn(&str, f32)) -> Result<O, E> {
+        let total_tasks = (self.dependencies.len() + 1) as f32;
+
+        let mut inputs = Vec::<I>::with_capacity(self.dependencies.len());
+
+        let mut count = 0;
+
+        for i in &mut self.dependencies {
+            inputs.push(i.execute(&|msg: &str, progress: f32| {
+                messenger(
+                    msg,
+                    progress / total_tasks + (1.0 / total_tasks) * count as f32,
+                )
+            })?);
+            count += 1;
+        }
+
+        self.task.execute(inputs, &|msg: &str, progress: f32| {
+            messenger(
+                msg,
+                progress / total_tasks + (1.0 / total_tasks) * count as f32,
+            )
+        })
+    }
+
+    /// Builds a new pipeline from the specified task, iterating on dependencies.
+    pub fn build(task: Box<Task<Input = I, Output = O, Error = E>>) -> DependencyTree<I, O, E> {
+        let dependencies = task
+            .dependencies()
+            .into_iter()
+            .map(|x| DependencyTree::build(x))
+            .collect();
+
+        DependencyTree { task, dependencies }
+    }
+}
+
+impl<I, O, E> Display for DependencyTree<I, O, E> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "{}", self.render())
+    }
+}