Created
June 19, 2019 18:50
-
-
Save jeffesp/e298b8638bba365b9d73c212385c7207 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.IO | |
open System.Text.RegularExpressions | |
open System.Linq | |
open MarkdownDeep | |
module Seq = | |
let sortByDesc f s = System.Linq.Enumerable.OrderByDescending(s, new System.Func<'a, 'b>(f)) | |
type Header = {date:DateTime; author:string; title:string; published:bool; layout:string; permalink:string } | |
type Post = {meta:Header; content:string} | |
let splitLine line = | |
let items = (line:string).Split(':') | |
match items with | |
| i when i.Length = 2 -> Some((items.[0].Trim(), items.[1].Trim())) | |
| i -> None | |
// this feels hacky | |
let getItem items (key:string) def = | |
let i = items |> Seq.find(fun i -> | |
match i with | |
| Some(i) -> | |
let (k:string), _ = i | |
k.ToLower() = key.ToLower() | |
| None -> false) | |
match i with | |
| Some(t) -> | |
let _, v = t | |
v | |
| None -> def | |
let itemsToHeader (items:seq<(string * string) option>) = | |
//TODO: the first 4 should fail instead of using bad defaults, the last two are optional | |
let findItem = getItem items | |
let date = findItem "date" "2000-01-01" |> DateTime.Parse | |
let author = findItem "author" "Unknown" | |
let title = findItem "title" "A Post" | |
let pub = findItem "published" "false" |> Boolean.Parse | |
let layout = findItem "layout" "" | |
let permalink = findItem "permalink" "" | |
{date = date; author = author; title = title; published = pub; layout = layout; permalink = permalink} | |
let parseValues heading = | |
let lines = (heading:string).Split([| "\n"; "\r"; "\r\n"; |], StringSplitOptions.RemoveEmptyEntries) |> Seq.map splitLine | |
let result = itemsToHeader lines | |
result | |
let extractHeader (content:string) = | |
let headStart = content.IndexOf "---" | |
let headEnd = content.IndexOf ("---", headStart + 1) | |
match headStart, headEnd with | |
| (s,e) when s >= 0 && e > 0 && e > s -> content.Substring(headStart, headEnd - headStart) |> parseValues |> Some | |
| (s,e) -> None | |
let extractContent (content:string) = | |
let bodyStart = content.LastIndexOf("---") | |
match bodyStart with | |
| -1 -> None | |
| _ -> Some(content.Substring(bodyStart + 3)) | |
let splitContent content = | |
let header = extractHeader content | |
let body = extractContent content | |
(header, body) | |
let createPost = function | |
| (Some(h), Some(c)) -> { meta = h; content = c} | |
| (_, _) -> failwith "Header or body not found in post." | |
let parsePost p = | |
printfn "Processing post in file: %s" p | |
use s = File.OpenText p | |
s.ReadToEnd() |> splitContent |> createPost | |
let applyLayout rootPath outputType part = | |
File.ReadAllText(Path.Combine(rootPath, String.Format("{0}.{1}", part, outputType))) | |
let outputPath (rootPath) (p:Post) = | |
let transformTitle (title:string) = | |
title.Replace(" ", "_") | |
let filep = | |
match p.meta.permalink with | |
| path when path = String.Empty -> | |
p.meta.date.ToString(@".\\yyyy\\MM\\dd\\") + (transformTitle p.meta.title) + ".html" | |
| _ -> | |
p.meta.permalink | |
Path.Combine(rootPath, filep).ToLower() | |
let getPosts baseDir = | |
let isPostFile fileName = | |
File.Exists(fileName) | |
&& Path.GetExtension(fileName) = ".md" | |
&& Regex.IsMatch(Path.GetFileNameWithoutExtension(fileName), @"\d\d\d\d-\d\d-\d\d-[A-Za-z0-9%_]") | |
if not(Directory.Exists(baseDir)) then failwithf "%s does not exist" baseDir | |
Directory.GetFiles(baseDir) |> Seq.filter isPostFile | |
let writeContent rootPath posts = | |
let writePost (rootPath) (p:Post) = | |
let applyHtml = applyLayout rootPath "html" | |
let md = new MarkdownDeep.Markdown() | |
let path = outputPath rootPath p | |
let head = String.Format((applyHtml "head"), p.meta.title) | |
let body = String.Format((applyHtml "body"), p.content) | |
let tail = applyHtml "tail" | |
match Directory.Exists(path) with | |
| false -> Directory.CreateDirectory(Path.GetDirectoryName(path)) |> ignore | |
| true -> () | |
use w = File.CreateText(path) | |
w.Write(head) | |
w.Write(body) | |
w.Write(tail) | |
let writePostToPath = writePost rootPath | |
posts |> Seq.iter(fun f -> writePostToPath f) | |
posts | |
let writeRss rootPath posts = | |
let applyRss = applyLayout rootPath "rss" | |
let writeRssItem stream post = | |
() | |
use rss = File.CreateText(Path.Combine(rootPath, "rss.xml")) | |
posts |> Seq.sortByDesc(fun p -> p.meta.date) |> Seq.take(10) |> Seq.iter(fun p -> writeRssItem rss p) | |
posts | |
[<EntryPoint>] | |
let main argv = | |
match argv with | |
| [| "compile"; dir |] -> | |
getPosts dir |> Seq.map(fun f -> parsePost f) |> writeContent dir |> writeRss dir |> ignore | |
| _ -> printfn "Usage: Thursday.exe compile [path to input files]" | |
0 // return an integer exit code | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment