meme-rs 0.0.1

Generate memes from commandline or http server
Documentation
use std::{error::Error, fs::{self, File}, io::Read, net::SocketAddr, path::{Path, PathBuf}};

use rand::{prelude::SliceRandom, Rng};
use warp::{http::HeaderValue, hyper::header::CONTENT_TYPE, reply::Response, Filter};

use crate::config::{Config, MemeIndex};

/// Start a random meme generator server
pub async fn start(addr: impl Into<SocketAddr>) {
    let cfg = Config::load();
    fs::create_dir_all(&cfg.output_dir).expect("faied creating output directory");
    let uniform_dist = cfg.create_uniform_distribution();
    let meme_msg = HttpMemeMessage::new(cfg, uniform_dist);
    let routes = warp::path::end()
        .and(warp::get())
        .map(move || meme_msg.clone());

    warp::serve(routes).run(addr).await;
}

#[derive(Clone)]
struct HttpMemeMessage {
    cfg: Config,
    uniform_dist: Vec<MemeIndex>,
}

impl HttpMemeMessage {
    pub fn new(cfg: Config, uniform_dist: Vec<MemeIndex>) -> Self {
        HttpMemeMessage { cfg, uniform_dist }
    }

    fn new_meme_file(&self) -> PathBuf {
        println!("{:?}", self.uniform_dist);
        let mut rng = rand::thread_rng();
        let meme = Meme::new_random(&mut rng, &self.cfg, &self.uniform_dist);
        println!("selected meme: {:?}", meme);
        meme.write(&self.cfg).expect("failed writing meme")
    }

    fn write_file(filename: PathBuf) -> Response {
        let mut f = File::open(filename).unwrap();
        let mut buf = vec![];
        f.read_to_end(&mut buf).expect("failed reading file");
        let mut res = Response::new(buf.into());
        res.headers_mut()
            .insert(CONTENT_TYPE, HeaderValue::from_static("image/jpeg"));
        res
    }
}

impl warp::Reply for HttpMemeMessage {
    fn into_response(self) -> warp::reply::Response {
        HttpMemeMessage::write_file(self.new_meme_file())
    }
}

#[derive(Debug)]
struct Meme {
    filename: String,
    text: String,
}

impl Meme {
    fn new_random<R>(rng: &mut R, cfg: &Config, uniform_dist: &Vec<MemeIndex>) -> Self
    where
        R: Rng,
    {
        let &MemeIndex {
            image_idx,
            text_idx,
        } = uniform_dist
            .choose(rng)
            .expect("failed choosing random meme");

        println!("({},{})", image_idx, text_idx);
        let img_cfg = &cfg.images[image_idx];
        if img_cfg.raw {
            Meme {
                filename: img_cfg.name.clone(),
                text: "".to_string(),
            }
        } else {
            let mut texts = cfg.common_texts.clone();
            let mut img_texts = img_cfg.texts.clone();
            texts.append(&mut img_texts);
            let img_text = &texts[text_idx];

            Meme {
                filename: img_cfg.name.clone(),
                text: img_text.clone(),
            }
        }
    }

    pub fn write(&self, cfg: &Config) -> Result<PathBuf, Box<dyn Error + 'static>> {
        let id = crate::new_id(&self.filename, &self.text);

        let input_file = Path::new(&cfg.input_dir).join(self.filename.clone());
        let mut id_filename = Path::new(&id).to_path_buf();
        id_filename.set_extension(
            input_file
                .extension()
                .expect("failed retrieving input file extension"),
        );
        let output_file = Path::new(&cfg.output_dir).join(id_filename);

        println!("{:?}", input_file);
        if output_file.exists() {
            println!("skip generating: {:?}", output_file);
        } else {
            crate::write_meme(&input_file, &output_file, &self.text);
            println!("wrote: {:?}", output_file);
        }

        Ok(output_file)
    }
}