The Dump Project

'Crafted' /s by Isaac Clayton

metadata

Created: Thursday April 29, 2021
Last Edited: Friday April 30, 2021

↖ Back to src?


use std::{
    fs,
    time::SystemTime,
};
use ramhorns::Content;
use chrono::prelude::*;
use pulldown_cmark::{Parser, Options, html};
use crate::route::Route;

#[derive(Debug, Clone, Content, PartialEq, Eq)]
pub struct Metadata {
    /// Time file was created in RFC 2822
    pub published: String,
    /// Time file was created as `Month Day, Year`
    pub created_human: String,
    /// Time file was last modified as `Month Day, Year`
    pub modified_human: String,
    /// Time file was created
    pub created: String,
    /// Time file was last modified
    pub modified: String,
    /// Title of the item
    pub title: String,
    /// Link to the item's page
    pub link: String,
    /// Link to the item's source
    pub raw_link: String,
    /// Last part of the link
    pub slug: String,
    /// Last part of the raw link
    pub raw_slug: String,
}

fn time_in_utc(t: SystemTime) -> DateTime {
    if let Ok(dur) = t.duration_since(SystemTime::UNIX_EPOCH) {
        Utc.timestamp(dur.as_secs() as i64, dur.subsec_nanos())
    } else {
        panic!("Could not determine time")
    }
}

impl Metadata {
    pub fn from_route(route: &Route) -> Metadata {
        let fs_meta = fs::metadata(&route.path).expect("Could not read fs metadata");
        let created = time_in_utc(fs_meta.created().unwrap());
        let modified = time_in_utc(fs_meta.modified().unwrap());

        Metadata {
            published: created.format("%a, %d %b %Y %T UTC").to_string(),
            created_human: created.format("%A %B %e, %Y").to_string(),
            modified_human: modified.format("%A %B %e, %Y").to_string(),
            created:   created.format("%F").to_string(),
            modified:  modified.format("%F").to_string(),
            title:     route.title(),
            link:      route.route.join("/"),
            raw_link:  format!("{}{}", route.route.join("/"), route.ext()),
            slug:      route.slug(),
            raw_slug:  route.slug_with_ext(),
        }
    }
}

impl Ord for Metadata {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        // Works because created is YYYY-MM-DD
        self.created.cmp(&other.created)
    }
}

impl PartialOrd for Metadata {
    fn partial_cmp(&self, other: &Self) -> Option {
        Some(self.cmp(other))
    }
}

#[derive(Content)]
pub struct Markdown {
    content: String,
}

impl Markdown {
    pub fn new(raw: &str) -> Markdown {
        let parser = Parser::new_ext(&raw, Options::all());
        let mut html_output = String::new();
        html::push_html(&mut html_output, parser);
        return Markdown { content: html_output };
    }
}

#[derive(Content)]
pub struct Context {
    pub m: Metadata,
    /// TODO: vec of metadata?
    pub parent:   Option,
    pub children: Vec,
    /// If the context is valid text, this is it
    pub content:  Option,
    /// If the content is valid markdown, this is it
    pub md: Option,
}

impl Context {
    pub fn new(
        metadata: Metadata,
        parent:   Option,
        mut children: Vec,
        raw_content: Vec
    ) -> Context {
        children.sort();
        let content = match std::str::from_utf8(&raw_content) {
            Ok(v)  => Some(v.to_string()),
            Err(_) => None,
        };

        Context {
            m: metadata,
            parent,
            children,
            md: content.clone().map(|i| Markdown::new(&i)),
            content,
        }
    }
}