zotan.pw-web/Pages/blog/Blog.cshtml.cs

81 lines
2.4 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using Markdig;
using Markdig.Extensions.Yaml;
using Markdig.Renderers;
using Markdig.Syntax;
using Microsoft.AspNetCore.Mvc.RazorPages;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace zotanpw.Pages.blog;
public class BlogModel : PageModel {
public static readonly List<BlogPost> Posts = new();
static BlogModel() {
foreach (var file in Directory.EnumerateFiles("Pages/blog/posts"))
Posts.Add(new BlogPost(Path.GetFileNameWithoutExtension(file)));
Posts = Posts.OrderByDescending(p => p.PublishedOn).ToList();
}
public void OnGet() { }
public class BlogPost {
public readonly string Shorthand;
public string Content = "";
public DateOnly PublishedOn;
public int ReadTimeMinutes;
public string Title = null!;
public BlogPost(string shorthand) {
Shorthand = shorthand;
UpdateContentAndMetadata();
}
public void UpdateContentAndMetadata() {
var markdownText = System.IO.File.ReadAllText($"Pages/blog/posts/{Shorthand}.md");
var parsed = ParseBlogPost(markdownText);
Title = parsed.metadata.Title!;
PublishedOn = parsed.metadata.Date;
ReadTimeMinutes = Regex.Matches(markdownText, @"\b\w+\b").Count / 150;
Content = parsed.html;
}
private static (PostMetadata metadata, string html) ParseBlogPost(string markdown) {
var yaml = "";
var pipeline = new MarkdownPipelineBuilder().UseGenericAttributes().UseYamlFrontMatter().Build();
var writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
var document = Markdown.Parse(markdown, pipeline);
var yamlBlock = document.Descendants<YamlFrontMatterBlock>().FirstOrDefault();
if (yamlBlock != null)
yaml = markdown.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length);
renderer.Render(document);
writer.Flush();
var html = writer.ToString();
var yamlDeserializer = new DeserializerBuilder().WithNamingConvention(LowerCaseNamingConvention.Instance).Build();
var metadata = yamlDeserializer.Deserialize<PostMetadata>(yaml);
return (metadata, html);
}
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")]
private class PostMetadata {
#pragma warning disable CS0649
public string? Title;
public DateOnly Date;
#pragma warning restore CS0649
}
}
}