From ee04ed912419dd9b255f5e44af69906ef273faff Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 5 Jan 2023 15:02:31 -0500 Subject: [PATCH] Add Rewritten in Rust --- site/css/main.scss | 10 +++++ site/posts/2019-09-18-reincarnation.md | 2 +- site/posts/2023-01-05-rewritten-in-rust.md | 50 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 site/posts/2023-01-05-rewritten-in-rust.md diff --git a/site/css/main.scss b/site/css/main.scss index 907080a..0da7af6 100644 --- a/site/css/main.scss +++ b/site/css/main.scss @@ -238,6 +238,16 @@ article { // and padding for horizontal so that the space is between the border and the text margin: 20px 0; padding: 0 40px; + + &.pull { + margin: 0 10px; + padding: 0 20px; + width: 40%; + font-size: 1.15rem; + + &.left { float: left; } + &.right { float: right; } + } } p code { diff --git a/site/posts/2019-09-18-reincarnation.md b/site/posts/2019-09-18-reincarnation.md index a64080a..688f530 100644 --- a/site/posts/2019-09-18-reincarnation.md +++ b/site/posts/2019-09-18-reincarnation.md @@ -42,7 +42,7 @@ The site remains almost entirely JavaScript free. There are two places where cli ## The Backend -The previous version of my website used [Jekyll](https://jekyllrb.com/) (and WordPress before that, and Jekyll again before that). In what may become a pattern, I've once more switched away from Jekyll. Version Four uses something completely custom. It has been a work-in-progress in one form or another for about a year now. It started out as a Node.js project that was going to be a general-purpose static site generator. Then, around the time I was learning Elixir (which I love, and will be the subject of another blog post), I attempted to rewrite it in that[^3]. Then we finally arrive at the current iteration of the current iteration of my website. In spite of my distaste for the ecosystem[^4], I returned to Node.js. This time, however, the project took a bit of a different direction than the previous two attempts at a rewrite. It has two main parts: the static site generator and the ActivityPub integration. +The previous version of my website used [Jekyll](https://jekyllrb.com/) (and WordPress before that, and Jekyll again before that). In what may become a pattern, I've once more switched away from Jekyll. Version Five uses something completely custom. It has been a work-in-progress in one form or another for about a year now. It started out as a Node.js project that was going to be a general-purpose static site generator. Then, around the time I was learning Elixir (which I love, and will be the subject of another blog post), I attempted to rewrite it in that[^3]. Then we finally arrive at the current iteration of the current iteration of my website. In spite of my distaste for the ecosystem[^4], I returned to Node.js. This time, however, the project took a bit of a different direction than the previous two attempts at a rewrite. It has two main parts: the static site generator and the ActivityPub integration. [^3]: Unfortunately, this attempt ran into some issues fairly quickly. Elixir itself is wonderful, but the package ecosystem for web-related things such as Sass, Markdown rendering, and syntax highlighting, is lackluster. diff --git a/site/posts/2023-01-05-rewritten-in-rust.md b/site/posts/2023-01-05-rewritten-in-rust.md new file mode 100644 index 0000000..dbfd67b --- /dev/null +++ b/site/posts/2023-01-05-rewritten-in-rust.md @@ -0,0 +1,50 @@ +``` +title = "Rewritten in Rust" +tags = ["meta"] +date = "2023-01-05 14:30:42 -0500" +short_desc = "If you're reading this message, I'm being held hostage by the Rust Evangelism Strike Force at—" +slug = "rewritten-in-rust" +``` + +So, about six months ago I decided I wanted to rewrite my perfectly-working blog backend in Rust. Why? Because I was bored and wanted an excuse to use Rust more. + + + +The fundamental architecture of my site is unchanged from the last [rewrite](/2019/reincarnation). All of the HTML pages are generated up front and written to disk. The HTTP server can then handle any ActivityPub-specific requests and fall back to serving files straight from disk. + +
+i look forward to finishing this rewrite and then being able to sit back and enjoy... *checks notes* the exact same website i had before +
+ +Because this project was undertaken with the deliberate goal of using Rust more, I let myself spend more time bikeshedding and working on pieces that I ordinarily would have ignored or left to 3rd party libraries. One of those was spending probably too much time writing a bunch of code to slugify post titles—that is, turning titles with lots of punctuation and things into a nice and URL-safe format. It handles a bunch of pet peeves I have when I look at URLs on other websites (e.g., non-ASCII characters geting blindly replaced resulting in long sequences of hyphens), even if those are highly unlikely to ever arise here. + +Another component I spent a great deal of time working on was the Markdown to HTML rendering. I'm using the [pulldown-cmark](https://lib.rs/crates/pulldown-cmark) crate, which handles a great deal for me, but not quite everything. In the previous implementation of my blog, I was using the [markdown-it](https://github.com/markdown-it/markdown-it) package which has some other little niceties to make the generated HTML better, in addition to the custom plugins I was using for the Markdown decorations you see if you're reading this on my blog itself. I have custom code for handling the link and heading decorations, as before. I also override how footnote definitions are generated, to make them all appear at the end of the HTML rather appearing in the same locations they're defined in the Markdown. As part of the changes to footnote definitions, I also generate backlinks which go from the footnote back to where it was referenced, to make reading them a bit easier. + +## Syntax Highlighting + +The previous, Node.js implementation of my blog used highlight.js for syntax highlighting. This works decently well, but it doesn't produce the most accurate highlighting since it's essentially a big pile of regexes rather than parsing the syntax. Now, I'm using [Tree Sitter](https://tree-sitter.github.io/tree-sitter/) which actually parses the language and so highlighting ends up being more accurate. + +Unfortunately, software being what it is, this is not always the case. The syntax highlighting for Swift with the best available Tree Sitter grammar is substantially worse than the highlight.js results. It routinely misses keywords, misinterprets variables as functions, and—to top all that off—something about how the highlight query is structured is incredibly slow for Tree Sitter to load. It more than doubles the time it takes to generate my blog, from about 0.5 seconds when skipping Swift highlighting to 1.3s when using tree-sitter-swift. + +So, because I've never met a problem I couldn't yak-shave, I decided the solution was to use John Sundell's [Splash](https://github.com/johnsundell/Splash) library for highlighting Swift snippets. A number of Swift blogs I follow use it, and it seems to produce very good results. But, of course, it's written in Swift, so I need some way of accessing it from Rust. This was a little bit of an ordeal, and it ended up being very easy, then very difficult, then easy, then confusing, and finally not too bad. The details of how exactly everything works and what I went through are a subject for another time, but if you want to see how it works, the source code for the Rust/Swift bridge is [available here](https://git.shadowfacts.net/shadowfacts/splash-rs/src/branch/main). + +## ActivityPub + +One of the big features from the last time I rewrote my blog was the [ActivityPub](https://www.w3.org/TR/activitypub/) integration. Almost nobody uses it, but I think it’s a cool feature and so I wanted to keep it. I'm using the [activitystreams](https://lib.rs/crates/activitystreams) crate, and what I've learned is that statically typed languages are maybe not so great for writing AP implementations. Lots of properties can be multiple different types, so you can't directly read anything, you have to check that it is in fact the type you expect. This does make for a more correct implementation, but it ends up being a big pain in the ass. + + + +ActivityPub support was undoubtedly the part of this project that took the most time. Just about all of the static generator was complete within a couple weeks. The AP support dragged on over the next 6 months because it was so unpleasant (both for the aforementioned reasons, and just that dealing with all the quirks of different AP implementations is a pain). + +But, now that it's all done, I'm pretty happy with where it is. The ActivityPub support is mostly unchanged. Blog posts are still AP `Article` objects that you can interact with and comment on, and you can still follow the blog AP actor from Mastodon/etc. to get new posts to show up in your home feed. I did make a couple small quality-of-life changes: + +1. If you reply to a blog post with a non-public post, you'll get an automated reply back telling you that's not supported. The purpose of replying to a blog post is to make comments show up, and I don't wnat to display things that people didn't intend to be public. If you want to send a private comment, you can message me directly, rather than the blog itself. +2. When there are new comments, the blog will automatically send me a notification (via AP, of course) with a digest of new posts. Previously, I had to manually check if there were any comments (if you ever commented and I never noticed it, sorry). + +## Misc + +The only other notable change is the addition of the [TV](/tv/) section, which is an archive of the various long-running commentary threads I've written on Mastodon.