swindon 0.7.8

An HTTP edge (frontend) server with smart websockets support
use std::io;
use std::path::Path;
use std::fs::read_dir;
use std::sync::Arc;

use tk_http::Status;
use trimmer::{Template, Context, Variable, Var, DataError};

use template;
use config::static_files::Static;

quick_error! {
    #[derive(Debug)]
    enum Error {
        TooManyFiles
        Io(err: io::Error) { from() }
    }
}

#[derive(Debug)]
struct Entry {
    name: String,
    is_dir: bool,
}

lazy_static! {
    static ref TEMPLATE: Template = template::PARSER.parse(
        include_str!("default_dir_index.html"))
        .expect("default dir index is a valid template");
}

fn read_files(path: &Path, settings: &Arc<Static>)
    -> Result<Vec<Entry>, Error>
{
    let mut result = Vec::new();
    for entry in read_dir(path)? {
        let entry = entry?;
        let typ = entry.file_type()?;
        result.push(Entry {
            name: Path::new(&entry.file_name()).display().to_string(),
            is_dir: typ.is_dir(),
        });
        if result.len() >= settings.generated_index_max_files {
            return Err(Error::TooManyFiles);
        }
    }
    Ok(result)
}

pub fn generate_index(path: &Path, virtual_path: &str,
    settings: &Arc<Static>)
    -> Result<Vec<u8>, Status>
{
    let files = match read_files(path, settings) {
        Ok(files) => files,
        Err(Error::TooManyFiles) => return Err(Status::Forbidden),
        Err(Error::Io(e)) => {
            error!("Error generating index for a directory {:?}: {}",
                path, e);
            return Err(Status::InternalServerError);
        }
    };
    let vpath = virtual_path.trim_right_matches('/');
    let mut ctx = Context::new();
    ctx.set("entries", &files);
    ctx.set("path", &vpath);
    let body = match TEMPLATE.render(&ctx) {
        Ok(body) => body,
        Err(e) => {
            error!("Error rendering directory index for {:?}: {}",
                path, e);
            return Err(Status::InternalServerError);
        }
    };
    Ok(body.into())
}

impl<'a> Variable<'a> for Entry {
    fn typename(&self) -> &'static str {
        "DirEntry"
    }
    fn attr<'x>(&'x self, attr: &str)
        -> Result<Var<'x, 'a>, DataError>
        where 'a: 'x
    {
        match attr {
            "name" => Ok(Var::borrow(&self.name)),
            "is_dir" => Ok(Var::borrow(&self.is_dir)),
            _ => Err(DataError::AttrNotFound),
        }
    }
}