I've been quite busy code wise lately but haven't made time to post anything. Almost all of the recent work has been on Rust projects, and while I haven't abandoned Zterm, I'm becoming more and more drawn to the Rust ecosystem. It's just that good.
At the time I started writing Gemview and Eva, I actually had no presence on Gemini whatsoever. I had a capsule in 2020, but due to the death of an SD card on a Raspberry PI it was lost and I never found the time to put up a new one until recently.
I like static content. When my second capsule went up for the first few weeks it was completely hand generated, including the indexes and atom feed. There was a whole lot of bookkeeping involved that takes away from the pleasure of just writing if I'm being honest with myself. I've tried a few other people's solutions, and used gssg for a while on my original capsule, but ultimately decided to write my own.
Zond leverages Clap to get a nice cli with multiple subcommands. I'm using the atom_syndication crate to generate the atom feed, and xml-rs to pretty print the results. Configuration is via the Ron format (Rusty Object Notation) and I'm embedding frontmatter metadata in posts and pages using the extract-frontmatter crate.
Functionally Zond works quite a lot like Hugo or Zola, minus templates since gemtext really doesn't allow for any styling. Anything inside "content" which has the .gmi extension gets rendered as a page or gemlog post, depending on it's location, while everything else just gets copied over to the output directory unchanged. The main index page gets a configurable number of the most recent gemlog posts and the user gets to decide whether to have an atom feed, gemini feed, neither, or both. Posts and pages can be further categorized with tags. All in all, it's a pretty unsurprising interface that gives you what you need to get a capsule set up and maintained without a lot of frills. Nice and boring in use so far, exactly as a Rust program should be.
I was going to parallelize it via Rayon, but the first time I ran a test build with a bunch of content it was so fast that I had to double check to make sure that I actually had any output. So, yeah, no need.
The rgba-simple rewrite
My little color library, rgba-simple, was written mainly for use with my current and potential future Gtk programs. It's only reason for existence at first was to provide a way to serialize RGBA colors into a config file, since the gdk::RGBA struct is an opaque struct, and I didn't want to duplicate that code for every program.
The original implementation worked perfectly fine, but it wasn't what I would call a great interface. There were two different RGBA structs, one for floating point f32 color channels and another, named ReducedRGBA, for u8 color channels. There was a separate HexColor struct, which included a hex string and an f32 alpha channel field. It was fairly messy and not something I was particularly proud of. I've been wanting to do a rewrite using generics for some time.
The rewrite wound up only taking a few lunch breaks actually. I may do a full post on the subject at some point, because I ran into some fairly hairy parts of the current situation with Rust generics and traits. I really, really wanted mutually exclusive traits to exist, but right now they're still just a dream. But I digress. The rewrite has a much cleaner and simpler interface with only one RGBA struct to worry about. The HexColor struct is gone, replaced with a Hex trait, allowing conversions both ways between RGBA and hexadecimal. I'm a lot happier with it, and not ashamed for others to see the code.
The Cargo xtask pattern was not my
idea, it was something that was pointed out to me over on the Rust user's forum.
The short takeaway is that by adding a
.cargo/config file to a project we can
provide a command alias and create our own Cargo subcommand, which builds a
separate binary using it's own Cargo.toml and performs tasks for us. What kind of
tasks? Well I'm using it to automate packaging in both Gfret and Zond right now
(with an Eva implementation planned). Now you can type
cargo xtask dist in the
Gfret source directory and you will get all of the files intended for distribution
target/dist directory. This includes not only the svg icon and XDG
desktop file, but generated files as well. The generated files are png icons in
various sizes, shell completions for bash, zsh, fish and powershell, and Unix
man pages. What's really cool is that the xtask binary does not shell out in
order to do any of this, increasing the portability of the software. Before,
Gfret had a Makefile which could optionally generate png icons by using either
Inkscape of rsvg_convert. That's nice and all, but obviously would fail if those
binaries weren't installed on the host system.
This has one drawback in that there is a bit of extra time taken to compile the xtask binary, but it's not a huge amount of time. It's also going to make it a lot easier to package my programs up for differnt operating systems by automating most of the work, and frankly it's a more robust system that Make. I've done the same with Zond as well, minus the icon generation.
In addition to the Xtask pattern, I want to highlight two other crates that I leveraged here to make this work. Clap mangen and Clap complete provide Unix man pages and shell completions respectively by parsing the same struct which is used to build the command line interface via Clap. If you're using clap anyway, these are two projects worth looking at.
Fretboard-layout and Gfret
Not as much to say here as most of the work was cleanup and integrating the new RGBA struct and interface. I did parallelize the calculations which lay out the frets, but it wasn's a massive speed improvement. I do think it makes the gui just slightly more responsive since the calculation is offloaded to background threads. I also made Gfret do all file saving in background threads, so there is no chance of a slow filesystem blocking the main thread now except at startup, when the configuration file is read. I figure that one we can wait for if need be. The fretboard_layout library and Gfret will both have new releases imminently.
Eva and GemView
I slowed down a bit on these projects for a while, but they're going to be picking back up again soon. The new RGBA interface got introduced this morning, and Eva now has a dark theme by default. Gemview now sends a "request-download" signal when it encounters a file type which it doesn't know how to display, and the next step in Eva is going to be handling that appropriately. There is already a setting in the preferences for whether the user wants to automatically download to a given directory, or whether to ask where to save the file every time. I'll have to match on that setting obviously. I'll also have to implement a dialog for the scenario "ask every time" and I'll probably be pulling in another dependency to provide file extensions based on the mime type of the data. Nothing too hard there, it's just code that I haven't gotten around to yet. I also need to touch up the input handling code and handle the case of "sensitive" input, which is currently just ignored.
After those things I feel that basic functionality is pretty well done with the exception of history. So next steps are kind of yet to be decided. I do want to start working on completions for the address/search bar, and I am still kicking around the idea of switching to sqlite for bookmark storage. Then there's some things that I want to try which fiddle with the page rendering in Gemview, which currently breaks the ability to copy text from block quote and preformatted blocks due to those elements being added via an anchor element to the underlying TextView widget which GemView is a subclass of.2022-04-12