#![feature(universal_impl_trait)]
#![feature(conservative_impl_trait)]
#![feature(unboxed_closures)]
#![feature(fn_traits)]
extern crate bincode;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lazy_static;
extern crate serde;
use std::{fmt, io};
use std::fmt::Arguments;
use std::io::Read;
use std::path::Path;
pub mod html;
lazy_static! {
static ref IS_ENV_STPL_PROD: bool = {
if let Ok(_val) = std::env::var("STPL_PROD") {
true
} else {
false
}
};
}
pub trait Template {
type Argument: serde::Serialize + for<'de> serde::Deserialize<'de>;
fn key(&self) -> &'static str;
fn render<'a>(&self, argument: &Self::Argument, io: &'a mut io::Write) -> io::Result<()>;
}
pub trait TemplateExt: Template {
fn render_dynamic_self(&self, data: &<Self as Template>::Argument) -> DynamicResult<Vec<u8>>
where
Self: Sized,
<Self as Template>::Argument: serde::Serialize + for<'de> serde::Deserialize<'de> + 'static,
{
if *IS_ENV_STPL_PROD {
self.render_static(data).map_err(|e| e.into())
} else {
render_dynamic_self(self, data)
}
}
fn render_dynamic(
&self,
path: &Path,
data: &<Self as Template>::Argument,
) -> DynamicResult<Vec<u8>>
where
Self: Sized,
<Self as Template>::Argument: serde::Serialize + for<'de> serde::Deserialize<'de> + 'static,
{
if *IS_ENV_STPL_PROD {
self.render_static(data).map_err(|e| e.into())
} else {
render_dynamic(path, self, data)
}
}
fn render_static(&self, data: &<Self as Template>::Argument) -> io::Result<Vec<u8>>
where
Self: Sized,
<Self as Template>::Argument: serde::Serialize + for<'de> serde::Deserialize<'de> + 'static,
{
let mut v = vec![];
self.render(data, &mut v)?;
Ok(v)
}
}
impl<T: Template> TemplateExt for T {}
pub trait Renderer {
fn write(&mut self, data: &[u8]) -> io::Result<()> {
self.write_raw(data)
}
fn write_fmt(&mut self, fmt: &Arguments) -> io::Result<()> {
self.write(format!("{}", fmt).as_bytes())
}
fn write_str(&mut self, s: &str) -> io::Result<()> {
self.write(s.as_bytes())
}
fn write_raw(&mut self, data: &[u8]) -> io::Result<()>;
fn write_raw_fmt(&mut self, fmt: &Arguments) -> io::Result<()> {
self.write_raw(format!("{}", fmt).as_bytes())
}
fn write_raw_str(&mut self, s: &str) -> io::Result<()> {
self.write_raw(s.as_bytes())
}
}
pub struct RawRenderer<'a, T: 'a + ?Sized>(&'a mut T);
impl<'a, T: 'a + Renderer + ?Sized> Renderer for RawRenderer<'a, T> {
fn write(&mut self, data: &[u8]) -> io::Result<()> {
self.0.write_raw(data)
}
fn write_fmt(&mut self, fmt: &Arguments) -> io::Result<()> {
self.0.write_raw_fmt(fmt)
}
fn write_str(&mut self, s: &str) -> io::Result<()> {
self.0.write_raw_str(s)
}
fn write_raw(&mut self, data: &[u8]) -> io::Result<()> {
self.0.write_raw(data)
}
fn write_raw_fmt(&mut self, fmt: &Arguments) -> io::Result<()> {
self.0.write_raw_fmt(fmt)
}
fn write_raw_str(&mut self, s: &str) -> io::Result<()> {
self.0.write_raw_str(s)
}
}
pub trait Render {
fn render(&self, &mut Renderer) -> io::Result<()>;
}
impl<T: Render> Render for Vec<T> {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
for t in self.iter() {
t.render(r)?;
}
Ok(())
}
}
impl<T: Render> Render for [T] {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
for t in self.iter() {
t.render(r)?;
}
Ok(())
}
}
macro_rules! impl_narr {
($n:expr) => {
impl<T: Render> Render for [T; $n] {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
for t in self.iter() {
t.render(r)?;
}
Ok(())
}
}
}
}
impl_narr!(0);
impl_narr!(1);
impl_narr!(2);
impl_narr!(3);
impl_narr!(4);
impl_narr!(5);
impl_narr!(6);
impl_narr!(7);
impl_narr!(8);
impl_narr!(9);
impl_narr!(10);
impl_narr!(11);
impl_narr!(12);
impl_narr!(13);
impl_narr!(14);
impl_narr!(15);
impl_narr!(16);
impl_narr!(17);
impl_narr!(18);
impl_narr!(19);
impl_narr!(20);
impl_narr!(21);
impl_narr!(22);
impl_narr!(23);
impl_narr!(24);
impl_narr!(25);
impl_narr!(26);
impl_narr!(27);
impl_narr!(28);
impl_narr!(29);
impl_narr!(30);
impl_narr!(31);
impl_narr!(32);
impl<'a, T: Render + ?Sized> Render for &'a mut T {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
(**self).render(r)?;
Ok(())
}
}
impl<T: Render + ?Sized> Render for Box<T> {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
(**self).render(r)?;
Ok(())
}
}
impl Render for () {
fn render(&self, _: &mut Renderer) -> io::Result<()> {
Ok(())
}
}
impl<R: Render> Render for Option<R> {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
if let &Some(ref s) = self {
s.render(r)?
}
Ok(())
}
}
impl Render for String {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
r.write_raw(self.as_bytes())
}
}
macro_rules! impl_render_raw {
($t:ty) => {
impl Render for $t {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
r.write_raw_fmt(&format_args!("{}", self))
}
}
}
}
impl_render_raw!(f64);
impl_render_raw!(f32);
impl_render_raw!(i64);
impl_render_raw!(u64);
impl_render_raw!(i32);
impl_render_raw!(u32);
impl_render_raw!(usize);
impl_render_raw!(isize);
impl<'a> Render for &'a str {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
r.write_str(self)
}
}
impl<'a> Render for fmt::Arguments<'a> {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
r.write_fmt(self)
}
}
impl<'a> Render for &'a fmt::Arguments<'a> {
fn render(&self, r: &mut Renderer) -> io::Result<()> {
r.write_fmt(self)
}
}
impl<A> Render for (A,)
where
A: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)
}
}
impl<A, B> Render for (A, B)
where
A: Render,
B: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)
}
}
impl<A, B, C> Render for (A, B, C)
where
A: Render,
B: Render,
C: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)
}
}
impl<A, B, C, D> Render for (A, B, C, D)
where
A: Render,
B: Render,
C: Render,
D: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)?;
self.3.render(r)
}
}
impl<A, B, C, D, E> Render for (A, B, C, D, E)
where
A: Render,
B: Render,
C: Render,
D: Render,
E: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)?;
self.3.render(r)?;
self.4.render(r)
}
}
impl<A, B, C, D, E, F> Render for (A, B, C, D, E, F)
where
A: Render,
B: Render,
C: Render,
D: Render,
E: Render,
F: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)?;
self.3.render(r)?;
self.4.render(r)?;
self.5.render(r)
}
}
impl<A, B, C, D, E, F, G> Render for (A, B, C, D, E, F, G)
where
A: Render,
B: Render,
C: Render,
D: Render,
E: Render,
F: Render,
G: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)?;
self.3.render(r)?;
self.4.render(r)?;
self.5.render(r)?;
self.6.render(r)
}
}
impl<A, B, C, D, E, F, G, H> Render for (A, B, C, D, E, F, G, H)
where
A: Render,
B: Render,
C: Render,
D: Render,
E: Render,
F: Render,
G: Render,
H: Render,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0.render(r)?;
self.1.render(r)?;
self.2.render(r)?;
self.3.render(r)?;
self.4.render(r)?;
self.5.render(r)?;
self.6.render(r)?;
self.7.render(r)?;
Ok(())
}
}
pub struct Fn<F>(pub F);
impl<F> Render for Fn<F>
where
F: std::ops::Fn(&mut Renderer) -> io::Result<()>,
{
fn render(&self, r: &mut Renderer) -> io::Result<()> {
self.0(r)
}
}
fn handle_dynamic_impl<T: Template>(template: &T) -> io::Result<()> {
let mut v = vec![];
std::io::stdin().read_to_end(&mut v)?;
let arg: T::Argument = bincode::deserialize(&v[..])
.map_err(|_e| io::Error::new(io::ErrorKind::Other, "Deserialization error"))?;
let stdout = std::io::stdout();
let stdout = stdout.lock();
let mut out = std::io::BufWriter::new(stdout);
template.render(&arg, &mut out)?;
Ok(())
}
pub struct HandleDynamic;
pub fn handle_dynamic() -> HandleDynamic {
HandleDynamic
}
pub const EXIT_CODE_SUCCESS: i32 = 66;
pub const EXIT_CODE_FAILED: i32 = 67;
pub const EXIT_CODE_NOT_FOUND: i32 = 68;
const ENV_NAME: &'static str = "RUST_STPL_DYNAMIC_TEMPLATE_KEY";
impl HandleDynamic {
pub fn template<T: Template>(self, template: &T) -> HandleDynamic {
if let Ok(var_name) = std::env::var(ENV_NAME) {
if var_name.as_str() == template.key() {
match handle_dynamic_impl(template) {
Ok(_) => std::process::exit(EXIT_CODE_SUCCESS),
Err(e) => {
eprintln!("Dynamic template process failed: {:?}", e);
std::process::exit(EXIT_CODE_FAILED);
}
}
}
}
self
}
}
impl std::ops::Drop for HandleDynamic {
fn drop(&mut self) {
if let Ok(var_key) = std::env::var(ENV_NAME) {
if !var_key.is_empty() {
eprintln!("Couldn't find dynamic template by key: {}", var_key);
std::process::exit(EXIT_CODE_NOT_FOUND);
}
}
}
}
#[derive(Fail, Debug)]
pub enum DynamicError {
#[fail(display = "IO error")] Io(#[cause] io::Error),
#[fail(display = "Template not found: {}", key)] NotFound {
key: String,
},
#[fail(display = "Template failed")]
Failed {
exit_code: Option<i32>,
stdout: Vec<u8>,
stderr: Vec<u8>,
},
}
impl From<io::Error> for DynamicError {
fn from(e: io::Error) -> Self {
DynamicError::Io(e)
}
}
type DynamicResult<T> = std::result::Result<T, DynamicError>;
fn render_dynamic<'a, 'path, A: 'static, T: Template>(
path: &'path Path,
template: &'a T,
data: &'a A,
) -> DynamicResult<Vec<u8>>
where
A: serde::Serialize,
{
let encoded: Vec<u8> = bincode::serialize(&data, bincode::Infinite).unwrap();
use std::process::{Command, Stdio};
use std::io::Write;
let mut child = Command::new(path)
.env(ENV_NAME, template.key())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("failed to execute child");
{
let stdin = child.stdin.as_mut().expect("failed to get stdin");
stdin.write_all(&encoded).expect("failed to write to stdin");
stdin.flush().expect("failed to flush stdin");
}
child.stdin = None;
let out = child.wait_with_output()?;
match out.status.code() {
Some(EXIT_CODE_SUCCESS) => Ok(out.stdout),
Some(EXIT_CODE_NOT_FOUND) => Err(DynamicError::NotFound {
key: template.key().to_owned(),
}),
code => Err(DynamicError::Failed {
exit_code: code,
stdout: out.stdout,
stderr: out.stderr,
}),
}
}
fn render_dynamic_self<T: Template>(
template: &T,
data: &<T as Template>::Argument,
) -> DynamicResult<Vec<u8>>
where
<T as Template>::Argument: serde::Serialize + 'static,
{
let path = std::env::args_os().next().unwrap();
let path = path.as_ref();
render_dynamic(path, template, data)
}