2017-10-25 21:37:20 -05:00
|
|
|
extern crate reqwest;
|
|
|
|
|
2017-10-25 20:11:32 -05:00
|
|
|
use std::process::Command;
|
|
|
|
use std::env;
|
|
|
|
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::fs::canonicalize;
|
2017-10-25 21:37:20 -05:00
|
|
|
use std::fs::{File, create_dir_all};
|
|
|
|
use std::io::{Write, copy};
|
2017-10-25 20:11:32 -05:00
|
|
|
|
|
|
|
pub fn yarn_install () {
|
2017-12-28 23:43:47 -06:00
|
|
|
if !Command::new("yarn").status().expect("failed to execute yarn process").success() {
|
|
|
|
panic!("Failed to execute yarn process");
|
|
|
|
}
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn browserify (infile: &str, outfile: &str) -> PathBuf {
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap();
|
|
|
|
let out_dir = Path::new(&out_dir);
|
|
|
|
let full_outfile = out_dir.join(outfile);
|
2017-12-28 00:39:10 -06:00
|
|
|
let status = Command::new("node_modules/.bin/browserify")
|
2017-10-25 20:11:32 -05:00
|
|
|
.args(&[infile, "-o", full_outfile.to_str().expect("failed to build output path")])
|
2017-12-28 00:39:10 -06:00
|
|
|
.status()
|
2017-10-25 20:11:32 -05:00
|
|
|
.expect("failed to build js bundle");
|
2017-12-28 00:39:10 -06:00
|
|
|
|
|
|
|
if !status.success() {
|
|
|
|
panic!("Failed to browserify input file {}", infile);
|
|
|
|
}
|
|
|
|
|
2017-10-25 20:11:32 -05:00
|
|
|
full_outfile
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lessc (infile: &str, outfile: &str) -> PathBuf {
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap();
|
|
|
|
let out_dir = Path::new(&out_dir);
|
|
|
|
let full_outfile = out_dir.join(outfile);
|
2017-12-28 00:39:10 -06:00
|
|
|
let status = Command::new("node_modules/.bin/lessc")
|
2017-10-25 20:11:32 -05:00
|
|
|
.args(&[infile, full_outfile.to_str().expect("failed to build output path")])
|
2017-12-28 00:39:10 -06:00
|
|
|
.status()
|
2017-10-25 20:11:32 -05:00
|
|
|
.expect("failed to build css bundle");
|
2017-12-28 00:39:10 -06:00
|
|
|
|
|
|
|
if !status.success() {
|
|
|
|
panic!("Failed to lessc input file {}", infile);
|
|
|
|
}
|
|
|
|
|
2017-10-25 20:11:32 -05:00
|
|
|
full_outfile
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ResourcesGenerator {
|
|
|
|
resources: Vec<Resource>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ResourcesGenerator {
|
|
|
|
pub fn new () -> ResourcesGenerator {
|
|
|
|
ResourcesGenerator {
|
|
|
|
resources: vec![]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add (&mut self, resource: Resource) -> &mut ResourcesGenerator {
|
|
|
|
self.resources.push(resource);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write (&self) {
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap();
|
|
|
|
let out_dir = Path::new(&out_dir);
|
|
|
|
let mut outfile = File::create(out_dir.join("resources.rs").to_str().expect("failed to build output path")).expect("failed to create outfile");
|
|
|
|
|
|
|
|
let mut consts = String::new();
|
|
|
|
|
|
|
|
let mut routes = String::new();
|
|
|
|
let mut routes_fn = String::from("pub fn routes() -> Vec<Route> {\n\troutes![");
|
|
|
|
|
|
|
|
let mut handlebars = String::new();
|
|
|
|
for resource in &self.resources {
|
2017-12-28 00:32:49 -06:00
|
|
|
let canonical_source = canonicalize(&resource.source).expect(&format!("failed to get canonical path for {:?}", resource.source));
|
2017-10-25 20:11:32 -05:00
|
|
|
let data_type = if resource.raw {
|
|
|
|
"[u8]"
|
|
|
|
} else {
|
|
|
|
"str"
|
|
|
|
};
|
|
|
|
|
|
|
|
let include = format!("include_{}!(\"{}\")", if resource.raw { "bytes" } else { "str" }, canonical_source.display());
|
|
|
|
if let Some(constant) = resource.constant {
|
|
|
|
consts.push_str(&format!(
|
|
|
|
"const {}: &'static {} = {};\n", constant, data_type, include
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2017-12-28 23:43:47 -06:00
|
|
|
if resource.mount_at.is_some() {
|
|
|
|
let (function_name, route_function) = self.create_rocket_route(resource, &include);
|
|
|
|
routes.push_str(&route_function);
|
2017-10-25 20:11:32 -05:00
|
|
|
routes_fn.push_str(&function_name);
|
|
|
|
routes_fn.push(',');
|
|
|
|
}
|
|
|
|
|
2017-12-28 23:43:47 -06:00
|
|
|
if resource.template_name.is_some() {
|
|
|
|
handlebars.push_str(&self.create_handlebars_template(resource, &include));
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !routes.is_empty() {
|
2017-12-28 23:43:47 -06:00
|
|
|
self.write_rocket_routes(&mut outfile, &routes, &routes_fn);
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if !handlebars.is_empty () {
|
2017-12-28 23:43:47 -06:00
|
|
|
self.write_handlebars_templates(&mut outfile, &handlebars);
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
outfile.write(consts.as_bytes());
|
|
|
|
outfile.write(b"\n");
|
2017-12-28 23:43:47 -06:00
|
|
|
}
|
2017-10-25 20:11:32 -05:00
|
|
|
|
2017-12-28 23:43:47 -06:00
|
|
|
fn create_rocket_route(&self, resource: &Resource, include: &str) -> (String, String) {
|
|
|
|
let mount_at = resource.mount_at.as_ref().unwrap();
|
|
|
|
let mut function_name = String::from("route");
|
|
|
|
let mut function_return_type = "&'static str";
|
|
|
|
let mut function_return_value = String::from(resource.constant.unwrap_or(&include));
|
2017-10-25 20:11:32 -05:00
|
|
|
|
2017-12-28 23:43:47 -06:00
|
|
|
if resource.raw {
|
|
|
|
function_return_type = "RawByteResource";
|
|
|
|
function_return_value = format!("RawByteResource({})", function_return_value);
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
2017-12-28 23:43:47 -06:00
|
|
|
|
|
|
|
function_name.push_str(&mount_at.endpoint.replace("/", "_").replace(".", "_").replace("-", "_"));
|
|
|
|
(function_name.clone(), format!(
|
|
|
|
"#[get(\"{}\")]\nfn {}() -> Content<{}> {{\n\tContent(ContentType::{}, {})\n}}\n\n",
|
|
|
|
mount_at.endpoint,
|
|
|
|
function_name,
|
|
|
|
function_return_type,
|
|
|
|
mount_at.content_type,
|
|
|
|
function_return_value
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_rocket_routes(&self, outfile: &mut File, routes: &str, routes_fn: &str) {
|
|
|
|
outfile.write(b"use rocket::{Route, Response, Request};\nuse rocket::response::{Content, Responder};\nuse rocket::http::{ContentType, Status};\n\n");
|
|
|
|
outfile.write(b"use std::io::Cursor;\n\n");
|
|
|
|
outfile.write(b"struct RawByteResource(&'static [u8]);\n");
|
|
|
|
outfile.write(b"impl<'r> Responder<'r> for RawByteResource {
|
|
|
|
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
|
|
|
Response::build()
|
|
|
|
.header(ContentType::Binary)
|
|
|
|
.sized_body(Cursor::new(self.0))
|
|
|
|
.ok()
|
|
|
|
}
|
|
|
|
}");
|
|
|
|
outfile.write(routes.as_bytes());
|
|
|
|
outfile.write(routes_fn.as_bytes());
|
|
|
|
outfile.write(b"]\n}");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_handlebars_template(&self, resource: &Resource, include: &str) -> String {
|
|
|
|
let template_name = resource.template_name.unwrap();
|
|
|
|
format!(
|
|
|
|
"\n\thbs.register_template_string(\"{}\", {}).expect(\"Failed to register template: {}\");",
|
|
|
|
template_name,
|
|
|
|
resource.constant.unwrap_or(&include),
|
|
|
|
template_name
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_handlebars_templates(&self, outfile: &mut File, handlebars: &str) {
|
|
|
|
outfile.write(b"use handlebars::Handlebars;\n\n");
|
|
|
|
outfile.write(b"pub fn handlebars() -> Handlebars {\n");
|
|
|
|
outfile.write(b"\tlet mut hbs = Handlebars::new();");
|
|
|
|
outfile.write(handlebars.as_bytes());
|
|
|
|
outfile.write(b"hbs\n}");
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-25 22:08:23 -05:00
|
|
|
#[derive(Default)]
|
2017-10-25 20:11:32 -05:00
|
|
|
pub struct Resource {
|
2017-10-25 22:17:56 -05:00
|
|
|
pub source: PathBuf,
|
|
|
|
pub mount_at: Option<ResourceMount>,
|
|
|
|
pub constant: Option<&'static str>,
|
|
|
|
pub template_name: Option<&'static str>,
|
|
|
|
pub raw: bool
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ResourceMount {
|
2017-10-25 22:54:55 -05:00
|
|
|
pub endpoint: String,
|
2017-10-25 22:17:56 -05:00
|
|
|
pub content_type: &'static str
|
2017-10-25 20:11:32 -05:00
|
|
|
}
|
2017-10-25 21:37:20 -05:00
|
|
|
|
|
|
|
pub struct Downloader {
|
|
|
|
cache: PathBuf
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Downloader {
|
|
|
|
pub fn new<P: AsRef<Path>>(cache: P) -> Downloader {
|
|
|
|
Downloader {
|
|
|
|
cache: cache.as_ref().to_path_buf()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 01:22:39 -06:00
|
|
|
pub fn get(&self, url: &str) -> DownloaderResult {
|
|
|
|
let filename = url.get((url.rfind('/').expect("not a valid url") + 1..)).expect("failed to parse filename");
|
|
|
|
self.get_to(url, filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_to(&self, url: &str, filename: &str) -> DownloaderResult {
|
2017-10-25 21:37:20 -05:00
|
|
|
if !self.cache.exists() {
|
|
|
|
create_dir_all(self.cache.as_path()).expect("failed to create cache directory");
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut out_path = self.cache.clone();
|
|
|
|
out_path.push(filename);
|
|
|
|
|
2017-12-04 01:22:39 -06:00
|
|
|
let was_cached = out_path.exists();
|
|
|
|
if !was_cached {
|
2017-10-25 22:21:53 -05:00
|
|
|
let mut result = reqwest::get(url).expect("failed to download url");
|
|
|
|
let mut file = File::create(out_path.as_path()).expect("failed to open file");
|
|
|
|
copy(&mut result, &mut file).expect("failed to write file");
|
2017-10-25 21:37:20 -05:00
|
|
|
}
|
|
|
|
|
2017-12-04 01:22:39 -06:00
|
|
|
DownloaderResult {
|
|
|
|
out_path: out_path,
|
|
|
|
was_cached: was_cached
|
|
|
|
}
|
2017-10-25 21:37:20 -05:00
|
|
|
}
|
|
|
|
}
|
2017-12-04 01:22:39 -06:00
|
|
|
|
|
|
|
pub struct DownloaderResult {
|
|
|
|
pub out_path: PathBuf,
|
|
|
|
pub was_cached: bool
|
|
|
|
}
|