Cadey is coffee
<Cadey> Hello! Thank you for visiting my website. You seem to be using an ad-blocker. I understand why you do this, but I'd really appreciate if it you would turn it off for my website. These ads help pay for running the website and are done by Ethical Ads. I do not receive detailed analytics on the ads and from what I understand neither does Ethical Ads. If you don't want to disable your ad blocker, please consider donating on Patreon or sending some extra cash to xeiaso.eth or 0xeA223Ca8968Ca59e0Bc79Ba331c2F6f636A3fB82. It helps fund the website's hosting bills and pay for the expensive technical editor that I use for my longer articles. Thanks and be well!

Site Update: Axum

Read time in minutes: 6

I have made a bunch of huge changes to my website that hopefully you won't notice unless you read this post that points them out to you. I have redone how the website's URL routing works to use axum instead of warp.

I chose warp fairly arbitrarily when I was getting into the swing of Rust. This choice turned out to be a bit of a mistake. Don't get me wrong, warp is a fantastic framework, but overall I've not been happy with how it impacts compile times. Warp works by pushing a lot of the complexities with HTTP routing into the type system. This can lead to undebuggable inscruitable types that make it really hard to understand what is wrong. Here is the autogenerated type for the /blog/:name route:

warp::filter::and::And<warp::filter::and::And<impl warp::filter::FilterBase<Extract = (), Error = Infallible> + warp::Filter + std::marker::Copy, Exact<warp::path::internal::Opaque<main::{closure#0}::__StaticPath>>>, warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<warp::filter::and::And<warp::filter::and::And<impl warp::filter::FilterBase<Extract = (), Error = Infallible> + warp::Filter + std::marker::Copy, impl warp::filter::FilterBase<Extract = (std::string::String,), Error = Rejection> + warp::Filter + std::marker::Copy>, impl warp::filter::FilterBase<Extract = (), Error = Rejection> + warp::Filter + std::marker::Copy>, impl warp::filter::FilterBase<Extract = (Arc<app::State>,), Error = Infallible> + warp::Filter + Clone>, impl warp::filter::FilterBase<Extract = (), Error = Rejection> + warp::Filter + std::marker::Copy>, fn(std::string::String, Arc<app::State>) -> impl warp::Future<Output = Result<Opaque(DefId(0:1249 ~ xesite[3495]::handlers::blog::post_view::{opaque#0}::{opaque#0}), []), Rejection>> {blog::post_view}>>

Numa is delet
<Numa> What the heck is that? Is that a binary tree?

Cadey is coffee
<Cadey> Yep. It's insufferable to try and debug too.

Yeah, it's really hard to understand what's going on in error messages because of this. This also means that the routes are put into a binary tree in the type system, which means if your tree is unbalanced then you get slower compile times and a slight hit at runtime as the framework traverses your binary tree to figure out what to do. This has also made it difficult for me to add features such as historical views of my RSS feed or other things I want to add like the April Fools day feature I've been working on.

When I went out framework shopping, I tried a few things and got reccomendations from a trusted friend before I finally settled on axum as the heart of this website. Axum has a few major advantages that brought me "in the door":

  • It's maintained by the tokio team
  • It leverages the type system of Rust to make writing handlers easier
  • It uses extractors (think lenses) to help you pick out the subset of data you need, not blindly giving you everything and hoping you figure it out
  • It has sub-routers which can have different middleware stacks than the main one (useful for things like API authentication)

And it has these disadvantages:

  • Writing middleware is kinda weird (though this may be because I'm not used to working with tower), but easy once you get the general flow of things
  • I can't find a way to have the template data get continuously piped to client connections instead of rendering it to a buffer and then writing that buffer to the client
  • It doesn't have the biggest mindshare and one of the best ways to get unstuck at the time of writing this article is to ask on their Discord server

Overall, I've been happy with the experience of porting over my site to using Axum. I did a stream on Twitch where I ported it all over if you want to watch the process and hear my thought processes as I was figuring things out.

As users, nothing should have changed about this site. However I'm almost certain that I did forget to port something over, so if I missed something you rely on, get in contact. I have not gotten the Patreon API interoperability code fixed yet, so that is the next major issue. I am going to figure out how refresh tokens work the hard way and make the patrons page auto-updating instead of having to manually get a new API key every month. I am also looking into having that patrons page be updated by a cronjob that emits json to the disk and have my site load from that instead of just hoping that the patreon API credentials are up to date. We'll see how that goes, but you can track that here. I will likely do a livestream for this.

I have also contacted a copyeditor for my blog. I am so happy with the results so far. My Devops post was the first thing that the editor reviewed and they absolutely tore my first draft in half and helped me put the parts back together into something more palateable. I am beyond satisfied with this and will continue to use this editor in the future. I wish I had gotten a copyeditor sooner.

This article was posted on M03 21 2022. Facts and circumstances may have changed since publication Please contact me before jumping to conclusions if something seems wrong or unclear.

Series: site-update

This post was not WebMentioned yet. You could be the first!

The art for Mara was drawn by Selicre.

The art for Cadey was drawn by ArtZorea Studios.