razorbill 0.1.0

A toolkit for bespoke static site generators.
Documentation
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, RwLock};

use thiserror::Error;

use crate::content::{Page, Section};

pub trait Store {
    type Error: std::error::Error;

    fn store_rendered_section(
        &self,
        section: &Section,
        rendered_html: String,
    ) -> Result<(), Self::Error>;

    fn store_rendered_page(&self, page: &Page, rendered_html: String) -> Result<(), Self::Error>;
}

pub struct DiskStorage {
    output_path: PathBuf,
}

impl DiskStorage {
    pub fn new(output_path: PathBuf) -> Self {
        Self { output_path }
    }
}

impl Store for DiskStorage {
    type Error = io::Error;

    fn store_rendered_section(
        &self,
        section: &Section,
        rendered_html: String,
    ) -> Result<(), Self::Error> {
        let output_dir = self.output_path.join(
            PathBuf::from_str(
                &section
                    .path
                    .0
                    .trim_end_matches("/_index")
                    .trim_start_matches("/"),
            )
            .unwrap(),
        );

        fs::create_dir_all(&output_dir)?;

        let output_path = output_dir.join("index.html");
        let mut output_file = File::create(&output_path)?;

        output_file.write_all(rendered_html.as_bytes())?;

        Ok(())
    }

    fn store_rendered_page(&self, page: &Page, rendered_html: String) -> Result<(), Self::Error> {
        let output_dir = self
            .output_path
            .join(PathBuf::from_str(&page.path.0.trim_start_matches("/")).unwrap());

        fs::create_dir_all(&output_dir)?;

        let output_path = output_dir.join("index.html");
        let mut output_file = File::create(&output_path)?;

        output_file.write_all(rendered_html.as_bytes())?;

        Ok(())
    }
}

pub struct InMemoryStorage {
    storage: Arc<RwLock<HashMap<String, String>>>,
}

impl InMemoryStorage {
    pub fn new(storage: Arc<RwLock<HashMap<String, String>>>) -> Self {
        Self { storage }
    }
}

#[derive(Error, Debug)]
pub enum InMemoryStorageError {
    #[error("poisoned")]
    Poisoned,
}

impl Store for InMemoryStorage {
    type Error = InMemoryStorageError;

    fn store_rendered_section(
        &self,
        section: &Section,
        rendered_html: String,
    ) -> Result<(), Self::Error> {
        self.storage
            .write()
            .map_err(|_| InMemoryStorageError::Poisoned)?
            .insert(section.path.0.replace("/_index", "/"), rendered_html);

        Ok(())
    }

    fn store_rendered_page(&self, page: &Page, rendered_html: String) -> Result<(), Self::Error> {
        self.storage
            .write()
            .map_err(|_| InMemoryStorageError::Poisoned)?
            .insert(page.path.0.clone(), rendered_html);

        Ok(())
    }
}