259 lines
8.8 KiB
Rust
Raw Normal View History

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);
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")])
.status()
2017-10-25 20:11:32 -05:00
.expect("failed to build js bundle");
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);
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")])
.status()
2017-10-25 20:11:32 -05:00
.expect("failed to build css bundle");
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));
let include = self.create_include(&resource, &canonical_source);
if resource.constant.is_some() {
consts.push_str(&self.create_const(&resource, include.as_ref().expect("Expected include for constant")));
2017-10-25 20:11:32 -05:00
}
2017-12-28 23:43:47 -06:00
if resource.mount_at.is_some() {
let (function_name, route_function) = match include {
Some(ref include) => self.create_rocket_route(resource, &include),
None => self.create_rocket_route_link(resource, &canonical_source)
};
2017-12-28 23:43:47 -06:00
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.as_ref().expect("Expected include for handlebars template")));
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
fn create_include(&self, resource: &Resource, source: &PathBuf) -> Option<String> {
if resource.link {
None
} else {
Some(format!(
"include_{}!(\"{}\")",
if resource.raw { "bytes" } else { "str" },
source.display()
))
}
}
fn create_const(&self, resource: &Resource, include: &str) -> String {
format!(
"const {}: &'static {} = {};\n",
resource.constant.unwrap(),
if resource.raw { "[u8]" } else { "str" },
include
)
}
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 create_rocket_route_link(&self, resource: &Resource, source: &PathBuf) -> (String, String) {
let mount_at = resource.mount_at.as_ref().unwrap();
let mut function_name = String::from("route");
function_name.push_str(&mount_at.endpoint.replace("/", "_").replace(".", "_").replace("-", "_"));
(function_name.clone(), format!(
"#[get(\"{}\")]\nfn {}() -> io::Result<NamedFile> {{\n\tNamedFile::open(\"{}\")\n}}\n\n",
mount_at.endpoint,
function_name,
source.display()
))
}
2017-12-28 23:43:47 -06:00
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, NamedFile};\nuse rocket::http::{ContentType, Status};\n\n");
outfile.write(b"use std::io;\nuse std::io::Cursor;\n\n");
2017-12-28 23:43:47 -06:00
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,
pub link: bool
2017-10-25 20:11:32 -05:00
}
pub struct ResourceMount {
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()
}
}
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);
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
}
DownloaderResult {
out_path: out_path,
was_cached: was_cached
}
2017-10-25 21:37:20 -05:00
}
}
pub struct DownloaderResult {
pub out_path: PathBuf,
pub was_cached: bool
}