<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Gabe Berke-Williams</title>
  <id>https://gabebw.com/</id>
  <link href="https://gabebw.com/"/>
  <link href="https://gabebw.com/feed.xml" rel="self"/>
  <updated>2025-04-05T05:00:00+00:00</updated>
  <author>
    <name>Gabe Berke-Williams</name>
  </author>
  <entry>
    <title>Converting to Neovim</title>
    <id>http://gabebw.com/blog/2025/04/05/converting-to-neovim</id>
    <link rel="alternate" href="http://gabebw.com/blog/2025/04/05/converting-to-neovim"/>
    <published>2025-04-05T05:00:00+00:00</published>
    <updated>2025-04-05T05:00:00+00:00</updated>
    <author>
      <name>Gabe Berke-Williams</name>
    </author>
    <summary type="html">My journey to a working LSP config.</summary>
    <content type="html">&lt;p&gt;As a longtime Vim user, I&amp;rsquo;ve been eyeing Neovim for a while. At work, I use VS
Code because it supports language servers, which can speak LSP (the Language
Server Protocol). As much as I like Vim, working on a large codebase is
infeasible without LSP support. I tried to use the LSP packages that have popped
up for Vim, but I couldn&amp;rsquo;t get them working. Neovim has native support for LSPs,
so I decided to 1) switch to Neovim and 2) set up LSP support.&lt;/p&gt;

&lt;h2 id="step-1-razzle-dazzle"&gt;Step 1: Razzle Dazzle&lt;/h2&gt;

&lt;p&gt;I have a large and complicated &lt;code&gt;.vimrc&lt;/code&gt; file, but I wanted to try out
Neovim&amp;rsquo;s features on Neovim&amp;rsquo;s terms. So I copied kickstart.nvim&amp;rsquo;s &lt;a href="https://github.com/nvim-lua/kickstart.nvim/blob/master/init.lua"&gt;&lt;code&gt;init.lua&lt;/code&gt;
file&lt;/a&gt; into my
Neovim (at &lt;code&gt;~/.config/nvim/init.lua&lt;/code&gt;) and launched Neovim.&lt;/p&gt;

&lt;p&gt;Wow. Here&amp;rsquo;s my raw reaction: &amp;ldquo;holy shit&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/neovim/slack-5ca64ce5.png" alt="wow" /&gt;&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s a closer look at that screenshot:&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/neovim/lazy.nvim-62d3f353.png" alt="a screenshot of the lazy.nvim install screen" /&gt;&lt;/p&gt;

&lt;p&gt;This looks like a modern terminal application! It reminds me of charm or rich.
There&amp;rsquo;s lots of color, a floating popup window, parallel processes. I was sold.&lt;/p&gt;

&lt;h2 id="step-2-copy-over-my-existing-vim-config"&gt;Step 2: copy over my existing Vim config&lt;/h2&gt;

&lt;p&gt;I deleted the &lt;code&gt;kickstart.nvim&lt;/code&gt; code and ported my &lt;code&gt;.vimrc&lt;/code&gt; to Neovim.&lt;/p&gt;

&lt;p&gt;Neovim understands Vimscript, but its &amp;ldquo;native&amp;rdquo; scripting language is Lua. Since
Vimscript is a terrible language, and I like learning new things, I pored over
the &lt;a href="https://neovim.io/doc/user/lua.html"&gt;Neovim Lua guide&lt;/a&gt; and the &lt;a href="https://learnxinyminutes.com/lua/"&gt;&amp;ldquo;Learn Lua
in X minutes&amp;rdquo; cheatsheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There was a lot of fumbling with the various abstractions of Neovim/Lua/Vimscript but
I eventually figured it out and copied over my existing config in Lua. I didn&amp;rsquo;t
translate the portions that are pithier in Vimscript, like autocommands in
groups. I used &lt;code&gt;vim.cmd&lt;/code&gt; to run Vimscript directly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- `vim.cmd` accepts arbitrary Vimscript&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="s"&gt;[[
augroup vimrc
  autocmd!
  autocmd User Rails nnoremap &amp;lt;Leader&amp;gt;u :Eunittest&amp;lt;Space&amp;gt;
  autocmd BufReadCmd set nohlsearch
augroup END
]]&lt;/span&gt;
&lt;span class="c1"&gt;-- Compare to the following equivalent "pure Lua" approach.&lt;/span&gt;
&lt;span class="n"&gt;augroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_augroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"vimrc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;clear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_autocmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User Rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;augroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;Leader&amp;gt;u"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;":Eunittest&amp;lt;Space&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;noremap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_autocmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"BufReadCmd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;augroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;-- The Lua equivalent to `set nohlsearch` in Vimscript&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hlsearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can check out &lt;a href="https://github.com/gabebw/dotfiles/pull/198"&gt;this PR&lt;/a&gt; to see
where I converted from Vim to a Neovim config.&lt;/p&gt;

&lt;h2 id="step-3-oh-yeah-lsps"&gt;Step 3: oh yeah, LSPs&lt;/h2&gt;

&lt;p&gt;Once my vim config was ported over, I set up LSPs. This was confusing because
&amp;ldquo;having an LSP&amp;rdquo; is actually a lot of different pieces, and people talk about them as if
they&amp;rsquo;re all one thing. But I learned that I can have an LSP working and still not have
autocompletion. Here are the individual pieces, and the PR in my dotfiles where I
implemented them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Having an LSP installed on your computer (e.g. &lt;code&gt;gem install ruby-lsp&lt;/code&gt;)

&lt;ol&gt;
&lt;li&gt;People use &lt;a href="https://github.com/williamboman/mason.nvim"&gt;&lt;code&gt;mason.nvim&lt;/code&gt;&lt;/a&gt; for
this, a full package manager. I decided to just install each LSP manually
until I understood everything better. I still haven&amp;rsquo;t installed &lt;code&gt;mason&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Neovim configuration for that LSP so Neovim can talk to it

&lt;ol&gt;
&lt;li&gt;Everyone uses &lt;a href="https://github.com/neovim/nvim-lspconfig"&gt;&lt;code&gt;nvim-lspconfig&lt;/code&gt;&lt;/a&gt; for this.&lt;/li&gt;
&lt;li&gt;I discovered that I had to explicitly set up each LSP that I wanted to
use. I assumed Neovim would automatically detect what&amp;rsquo;s installed.&lt;/li&gt;
&lt;li&gt;I implemented that in &lt;a href="https://github.com/gabebw/dotfiles/pull/201"&gt;this PR&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Fun mappings for various LSP functions, like a fuzzy finder for all of the
methods in your project

&lt;ol&gt;
&lt;li&gt;Neovim&amp;rsquo;s Lua interface lets me call LSP functions directly (&lt;code&gt;:lua
vim.lsp.buf.references&lt;/code&gt;), but it&amp;rsquo;s not ergonomic.
I set up &lt;a href="https://github.com/nvim-telescope/telescope.nvim"&gt;Telescope&lt;/a&gt; because it&amp;rsquo;s the most popular fuzzy finder, and it has a lot of built in
pieces for LSPs.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s a fun easter egg in Telescope: try &lt;code&gt;:Telescope planets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I implemented the nice Telescope mappings in &lt;a href="https://github.com/gabebw/dotfiles/pull/202"&gt;this PR&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Autocompletion on &lt;kbd&gt;&amp;lt;Tab&amp;gt;&lt;/kbd&gt;

&lt;ol&gt;
&lt;li&gt;I decided to use &lt;a href="https://github.com/hrsh7th/nvim-cmp"&gt;&lt;code&gt;nvim-cmp&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nvim-cmp&lt;/code&gt; can even autocomplete file paths in code &lt;em&gt;as you type them&lt;/em&gt;, which
is so cool.&lt;/li&gt;
&lt;li&gt;I implemented that in &lt;a href="https://github.com/gabebw/dotfiles/pull/203"&gt;this PR&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="step-4-enjoy-it"&gt;Step 4: enjoy it&lt;/h2&gt;

&lt;p&gt;Now I have a working LSP with autocompletion! It&amp;rsquo;s so nice.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Make a Custom Ringtone for your iPhone</title>
    <id>http://gabebw.com/blog/2024/06/30/how-to-make-a-custom-ringtone-for-your-iphone</id>
    <link rel="alternate" href="http://gabebw.com/blog/2024/06/30/how-to-make-a-custom-ringtone-for-your-iphone"/>
    <published>2024-06-30T05:00:00+00:00</published>
    <updated>2024-06-30T05:00:00+00:00</updated>
    <author>
      <name>Gabe Berke-Williams</name>
    </author>
    <summary type="html">It's easy!</summary>
    <content type="html">&lt;p&gt;Update: I made a &lt;a href="https://ring-it-to-me.netlify.app"&gt;website&lt;/a&gt; to make a ringtone
out of any audio file. Try it!&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;First, get a file on your Mac with the sound you want. It can be a video or
audio file, in pretty much any format. Just for kicks, let&amp;rsquo;s say it&amp;rsquo;s a file
called &amp;ldquo;Carly Rae Jepsen - I Really Like You.mp4&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;You&amp;rsquo;ll also need to have &lt;code&gt;ffmpeg&lt;/code&gt; installed.&lt;/p&gt;

&lt;h2 id="create-the-ringtone-file"&gt;Create the ringtone file&lt;/h2&gt;

&lt;p&gt;Run this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg \
  -i "Carly Rae Jepsen - I Really Like You.mp4" \
  -vn \
  -ac 1 \
  -b:a 128k \
  -f mp4 \
  -c:a aac \
  -ss 41.5 \
  -t 29.99 \
  "ringtone.m4r"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s break that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-i &amp;lt;filename&amp;gt;&lt;/code&gt;: the input filename&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-vn&lt;/code&gt;: strip out all of the video (&amp;ldquo;video null&amp;rdquo;). That makes the resulting
&lt;code&gt;mp4&lt;/code&gt; file audio-only.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-ac 1&lt;/code&gt;: 1 audio channel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b:a 128k&lt;/code&gt;: set bitrate of the audio to 128k. I&amp;rsquo;m not sure why, honestly: I
copied this from an online post. But if I had to guess, it&amp;rsquo;s to keep the
filesize down by limiting the bitrate, while still keeping it high enough that
there&amp;rsquo;s almost no loss in quality. I&amp;rsquo;m not a huge audiophile but I don&amp;rsquo;t think
many people are worried about the audio quality of their ringtone. Some quick
testing showed that a 64k bitrate leads to a 244Kb file; 128k leads to 484k; and
256k leads to 956Kb.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f mp4&lt;/code&gt;: output format is MP4. Usually ffmpeg will figure it out based on the
output file&amp;rsquo;s extension, but &lt;code&gt;m4r&lt;/code&gt; is a weird one, so we specify it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-ss 41.5&lt;/code&gt;: start the clip 41.5 seconds into the file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t 29.99&lt;/code&gt;: end the clip after 29.99 seconds from when it started. Ringtones
on iPhone have a maximum length of 30 seconds.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ringtone.m4r&lt;/code&gt;: the output file name. iPhone ringtones must end in &lt;code&gt;.m4r&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I often have to run this a couple times to figure out exactly where to start
(&lt;code&gt;-ss&lt;/code&gt;) the clip. If you want to re-run it and automatically overwrite the file
without prompting, add &lt;code&gt;-y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now you have a &lt;code&gt;ringtone.m4r&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;At this point you may want to rename it to something other than &lt;code&gt;ringtone.m4r&lt;/code&gt;,
because the file name will be the name of the ringtone in your phone.&lt;/p&gt;

&lt;h2 id="get-the-ringtone-on-your-phone"&gt;Get the ringtone on your phone&lt;/h2&gt;

&lt;p&gt;You need to physically connect your iPhone to your computer. It&amp;rsquo;s odd, but there
doesn&amp;rsquo;t seem to be any other way to do it.&lt;/p&gt;

&lt;p&gt;After connecting your phone, open Music.app and find your phone in the sidebar
on the left. Then drag the &lt;code&gt;ringtone.m4r&lt;/code&gt; file onto the big white space in the
center to add it.&lt;/p&gt;

&lt;p&gt;Now it&amp;rsquo;s a ringtone, and you can go to any contact, edit it, and set the
ringtone to your newly added ringtone.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hyperlinks in the Terminal</title>
    <id>http://gabebw.com/blog/2024/01/06/hyperlinks-in-the-terminal</id>
    <link rel="alternate" href="http://gabebw.com/blog/2024/01/06/hyperlinks-in-the-terminal"/>
    <published>2024-01-06T05:00:00+00:00</published>
    <updated>2024-01-06T05:00:00+00:00</updated>
    <author>
      <name>Gabe Berke-Williams</name>
    </author>
    <summary type="html">You don't always need to use bare URLs.</summary>
    <content type="html">&lt;p&gt;Yesterday I learned that you can show a hyperlink in most terminals. That is,
you can show some text that links to a URL, like
&lt;a href="https://gabebw.com/archive"&gt;this&lt;/a&gt;, instead of a bare URL, like
&lt;a href="https://gabebw.com/archive"&gt;https://gabebw.com/archive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To check if your terminal and emulator work with this, you can check the list
&lt;a href="https://github.com/Alhadis/OSC8-Adoption/"&gt;here&lt;/a&gt;. As of today, macOS&amp;rsquo;s
Terminal.app does not support this.&lt;/p&gt;

&lt;p&gt;Or you can run this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e]8;;http://example.com\e\\This is a link\e]8;;\e\n'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That will print the text &amp;ldquo;This is a link&amp;rdquo;, all of which links to example.com.
Your terminal (or terminal emulator) will have different ways to test it, but
try hovering over it to see if there&amp;rsquo;s an underline. If there is, then open it
with whatever modifier your terminal uses (e.g. for Alacritty:
&lt;kbd&gt;Cmd-click&lt;/kbd&gt;; for Kitty: &lt;kbd&gt;Ctrl-Shift-click&lt;/kbd&gt;).&lt;/p&gt;

&lt;p&gt;This works by using something called OSC 8. (OSC stands for &lt;a href="https://blog.vucica.net/2017/07/what-are-osc-terminal-control-sequences-escape-codes.html"&gt;&amp;ldquo;operating system
command&amp;rdquo;&lt;/a&gt;.)
It&amp;rsquo;s hard to find more detail on it, not least because &amp;ldquo;operating system
command&amp;rdquo; returns lots of unrelated results, but &lt;a href="https://en.wikipedia.org/wiki/ANSI_escape_code#OSC_(Operating_System_Command)_sequences"&gt;here&amp;rsquo;s
Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, this does not work in tmux yet, so if you&amp;rsquo;re running tmux inside
a terminal that does work, you&amp;rsquo;ll have to exit tmux for the link to work. (In a
script I wrote that prompted this blog post, I added a check for the &lt;code&gt;$TMUX&lt;/code&gt; env
variable. If it&amp;rsquo;s in tmux, then it prints a bare URL instead.)&lt;/p&gt;

&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;

&lt;p&gt;Thank you to &lt;a href="https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda"&gt;this gist blog post&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fun Facts and Tidbits from 2022</title>
    <id>http://gabebw.com/blog/2022/12/27/fun-facts-and-tidbits-from-2022</id>
    <link rel="alternate" href="http://gabebw.com/blog/2022/12/27/fun-facts-and-tidbits-from-2022"/>
    <published>2022-12-27T05:00:00+00:00</published>
    <updated>2022-12-27T05:00:00+00:00</updated>
    <author>
      <name>Gabe Berke-Williams</name>
    </author>
    <summary type="html">66 facts that delighted me this year.</summary>
    <content type="html">&lt;ol&gt;
&lt;li&gt;In the 1912 Olympics, the then-President of the International Olympic
Committee, Baron Pierre de Coubertin, won a gold medal&amp;hellip;for literature!&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Omicron&amp;rdquo; literally means &amp;ldquo;little O&amp;rdquo; in Greek (o mikron); compare to omega, or &amp;ldquo;great O&amp;rdquo; (ō mega).&lt;/li&gt;
&lt;li&gt;Coors encouraged the organization of its gay and lesbian employees into the Lesbian and Gay Employee Resource in 1993. Its acronym was LAGER.&lt;/li&gt;
&lt;li&gt;Rat tickling is an effective way to improve laboratory rat welfare. And you can get &lt;a href="https://www.nc3rs.org.uk/3rs-resources/rat-tickling"&gt;certified&lt;/a&gt; in it.&lt;/li&gt;
&lt;li&gt;The RMS Carpathia &lt;a href="https://mylordshesacactus.tumblr.com/post/170401018158/please-make-a-post-about-the-story-of-the-rms"&gt;moved heaven and earth&lt;/a&gt; to rescue the survivors of the Titanic.&lt;/li&gt;
&lt;li&gt;Ornithologist Mario Cohn-Haft &lt;a href="https://roach-works.tumblr.com/post/672314173743628288/all-hail-the-coming-of-the-predicted-antwren"&gt;predicted the existence of a bird&lt;/a&gt; 25 years before confirming it.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://en.wikipedia.org/wiki/Imperial_phase"&gt;&amp;ldquo;imperial phase&amp;rdquo;&lt;/a&gt; is the name for the period in which a musical artist is regarded to be at their commercial and creative peak simultaneously.&lt;/li&gt;
&lt;li&gt;St. Francis of Assisi is the reason for the popularity of the name Francis. His given name was Giovanni, and Francis was his nickname, which spread due to his fame.&lt;/li&gt;
&lt;li&gt;The stitching at the top of a big 40 lb bag of rice or oats is a &lt;a href="https://www.metafilter.com/194172/Up-to-30-stitches-per-inch-No-bobbins-Quiet#8201964"&gt;chain stitch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Things that are older than you think:

&lt;ol&gt;
&lt;li&gt;Sharks are &lt;a href="https://old.reddit.com/r/tumblr/comments/shdlip/not_to_mention_those_plants_that_were_polinated/"&gt;older&lt;/a&gt; than trees (420 million vs 370 million years old).&lt;/li&gt;
&lt;li&gt;Crocodiles and their relatives are &lt;a href="https://old.reddit.com/r/tumblr/comments/shdlip/not_to_mention_those_plants_that_were_polinated/"&gt;older&lt;/a&gt; than Saturn&amp;rsquo;s rings (200 million years old vs 100 million years old).&lt;/li&gt;
&lt;li&gt;The Appalachian Mountains are &lt;a href="https://i.gabebw.com/tumblr/appalachians-are-older-than-bones.jpg"&gt;older than bones&lt;/a&gt; (!!!).&lt;/li&gt;
&lt;li&gt;Altoids were created in the 1780s.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;The Emmy Awards are named after &amp;ldquo;immy&amp;rdquo;, an informal term for the image orthicon tube that was common in early television cameras.&lt;/li&gt;
&lt;li&gt;Male dromedaries have an &lt;em&gt;extremely weird&lt;/em&gt; organ &lt;a href="https://www.reddit.com/r/interestingasfuck/comments/skez89/male_dromedary_camels_has_an_organ_called_a_dulla/"&gt;in their
throat&lt;/a&gt;. Turn on your sound for this video.&lt;/li&gt;
&lt;li&gt;The nonsense syllables in e.g. &amp;ldquo;Sweet Caroline&amp;rdquo; (&amp;ldquo;Sweet Caroline, &lt;em&gt;bum bum
bum&lt;/em&gt;&amp;rdquo;) are called &lt;a href="https://en.wikipedia.org/wiki/Non-lexical_vocables_in_music"&gt;non-lexical vocables&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Burpee exercise was named after Dr. Royal H. Burpee (!), a man with an
&lt;a href="https://mobile.twitter.com/brockway_llc/status/1491804318877601792"&gt;exquisite sense of style&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can fit all the planets (Pluto included) between the Earth and the Moon.&lt;/li&gt;
&lt;li&gt;Some Kosovans are named &lt;a href="https://en.wikipedia.org/wiki/Tonibler"&gt;&amp;ldquo;Tonibler&amp;rdquo;&lt;/a&gt; after Prime Minister Tony Blair, who was
credited with ending the Kosovo War.&lt;/li&gt;
&lt;li&gt;Vladimir Putin&amp;rsquo;s name in French is &lt;a href="https://fr.wikipedia.org/wiki/Vladimir_Poutine"&gt;Vladimir Poutine&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jan Evangelista Purkyn&amp;ecaron; (1787-1869) was such a famous scientist that when
people wrote to him, all that they needed to put as the address was
&amp;ldquo;Purkyn&amp;ecaron;, Europe&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Julie Kavner, the voice of Marge Simpson, is reclusive and part of her contract says that she will never have to promote The Simpsons on video.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Bell&amp;rdquo; in Taco Bell&amp;rsquo;s name came from its founder, Glen Bell, who started
selling tacos from the side window of his hamburger stand and then expanded.&lt;/li&gt;
&lt;li&gt;Papa John&amp;rsquo;s franchises in Russia are called &lt;a href="https://www.nytimes.com/2022/03/14/business/papa-johns-russia.html"&gt;PJ Western&lt;/a&gt; (!).&lt;/li&gt;
&lt;li&gt;The stamp on toilet paper that you see at hotels sometimes is called a &lt;a href="https://www.howtocleanstuff.net/what-is-the-pixie-stamp/"&gt;pixie
stamp&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Under federal law, a &lt;a href="https://matthewbutterick.com/chron/justice-stevens-reads-the-fine-print.html"&gt;ship that only visits US ports&lt;/a&gt; must be flagged
(registered) in the US. Thus, almost all cruise ships at least stop in
Mexico or Canada to avoid the expense and regulation of a US registration.&lt;/li&gt;
&lt;li&gt;Bangkok&amp;rsquo;s &lt;a href="https://www.nytimes.com/2022/04/02/world/asia/bangkok-thailand-krung-thep.html"&gt;formal name&lt;/a&gt; is so long that it is an official world
record: Krung Thep Maha Nakhon Amon Rattanakosin
Mahinthara Ayuthaya Mahadilok Phop Noppharat Ratchathani Burirom
Udomratchaniwet Mahasathan Amon Piman Awatan Sathit Sakkathattiya Witsanukam
Prasit.&lt;/li&gt;
&lt;li&gt;The names &amp;ldquo;Arctic&amp;rdquo; and &amp;ldquo;Antarctic&amp;rdquo; derive from the Greek word for bear
(&amp;ldquo;arktos&amp;rdquo;), referring to Ursa Major&amp;rsquo;s visibility (or lack thereof) from each region.&lt;/li&gt;
&lt;li&gt;A number of Latin American people misheard R2-D2&amp;rsquo;s name as
&lt;a href="https://www.starwars.com/news/arturito-and-me"&gt;&amp;ldquo;Arturito&amp;rdquo;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://en.wikipedia.org/wiki/Bizzaria"&gt;Bizzaria&lt;/a&gt; is a &amp;ldquo;bizarre&amp;rdquo; cross between a citron and an
orange, and the fruit looks like half of each fruit combined. Check out that
picture!&lt;/li&gt;
&lt;li&gt;Seymour Cray, the father of supercomputing, loved to &lt;a href="https://en.wikipedia.org/wiki/Seymour_Cray#Personal_life"&gt;dig tunnels&lt;/a&gt;
to help him think through a problem: &amp;ldquo;While I&amp;rsquo;m digging in the tunnel, the
elves will often come to me with solutions to my problem.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://upload.wikimedia.org/wikipedia/en/7/75/Versace_logo.png"&gt;Versace&lt;/a&gt; logo is Medusa&amp;rsquo;s head.&lt;/li&gt;
&lt;li&gt;Just as unitarianism is belief in God with one person, and trinitarianism is
belief in a Holy Trinity, so too is there belief in God with two persons,
and it has the excellent name of &lt;a href="https://en.wikipedia.org/wiki/Binitarianism"&gt;binitarianism&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In 1972, Dr. John Fryer &lt;a href="https://www.nytimes.com/2022/05/02/health/john-fryer-psychiatry.html"&gt;risked his career&lt;/a&gt; to tell his colleagues that gay people were not mentally ill.&lt;/li&gt;
&lt;li&gt;Passion fruit flowers look &lt;a href="https://commons.wikimedia.org/wiki/File:OQ_Passion_flower.jpg"&gt;completely wild&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;There are &lt;a href="https://www.rei.com/blog/climb/fun-scale"&gt;two types of fun&lt;/a&gt;. Type 1 Fun is enjoyable while it&amp;rsquo;s
happening. Type 2 Fun is miserable while it&amp;rsquo;s happening, but enjoyable
afterwards. (For example, some exercise.)&lt;/li&gt;
&lt;li&gt;Baseball&amp;rsquo;s first openly gay player, &lt;a href="https://www.nytimes.com/2022/06/02/sports/baseball/glenn-burke-dodgers-pride.html"&gt;Glenn Burke&lt;/a&gt;, invented the high
five in 1977.&lt;/li&gt;
&lt;li&gt;The actor who played Ser Bronn in Game of Thrones (Jerome Flynn) was a
singer with the best-selling UK single of 1995.&lt;/li&gt;
&lt;li&gt;Apparently you could make a pretty good living as an actor in a gorilla suit in
the 1940s. At least if you were &lt;a href="https://en.wikipedia.org/wiki/Ray_%22Crash%22_Corrigan"&gt;Ray &amp;ldquo;Crash&amp;rdquo; Corrigan&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The first and second derivatives of position are called velocity and
acceleration. The third derivative is jerk. And the fourth/fifth/sixth are
&lt;a href="https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position"&gt;snap, crackle, and pop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;pool smell&amp;rdquo; is chloramines, not chlorine: compounds formed in pool
water after chlorine has done its job and had a fight with a germ or algae
or whatever. Ideally your pool wouldn&amp;rsquo;t smell like much.&lt;/li&gt;
&lt;li&gt;Many wineries color their wines a deeper red using a compound called &lt;a href="https://vinepair.com/articles/what-is-mega-purple-and-what-is-it-doing-in-my-wine/"&gt;Mega
Purple&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The space-age, &lt;em&gt;Jetsons&lt;/em&gt;-like architecture has a formal name: &lt;a href="https://en.wikipedia.org/wiki/Googie_architecture"&gt;Googie
architecture&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is not a fun fact, exactly, but I love that &lt;a href="https://en.wikipedia.org/wiki/Raimbaut_d%27Aurenga"&gt;Raimbaut
d&amp;#39;Aurenga&lt;/a&gt;, who died before the year 1200, gets compliments on his
fire bars 850 years later on Wikipedia:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;He was a major troubadour, having contributed to the creation of trobar
ric, or articulate style, in troubadour poetry. About forty of his works
survive, displaying a gusto for rare rhymes and intricate poetic form.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;a href="https://en.wikipedia.org/wiki/Dublin_whiskey_fire"&gt;Dublin whiskey fire&lt;/a&gt; of 1875, 13 people died. But none of them
died from smoke inhalation, burns, and so on. They died from alcohol
poisoning from drinking the whiskey that flowed out of the factory!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I had cause to learn a bit about violins this year, and I was impressed by
how much effort goes into the parts of the violin that are not the actual
body. For example, bows can cost tens of thousands of dollars by themselves.
I also found &lt;a href="https://stringsmagazine.com/a-guide-to-choosing-the-right-violin-strings/"&gt;this website about violin strings&lt;/a&gt;, which I
can only describe as having &amp;ldquo;Ollivander&amp;rsquo;s wand shop vibes&amp;rdquo;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Just as Harlem in NYC is named after Haarlem in the Netherlands, so too is
Brooklyn named after &lt;a href="https://en.wikipedia.org/wiki/Breukelen"&gt;Breukelen&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Magnús Scheving created the TV show Lazytown. But before that, he became the
Icelandic champion in aerobics after making a bet with his friend that they
could each master a topic they knew nothing about in three years. His friend
became the Icelandic champion in snooker as a result of the bet. I can&amp;rsquo;t
believe this worked for both of them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In &lt;em&gt;Babe: Pig in the City&lt;/em&gt;, the landlady&amp;rsquo;s elderly uncle is named
&lt;a href="https://en.wikipedia.org/wiki/Babe:_Pig_in_the_City"&gt;Fugly&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There was a small but vocal movement to &lt;a href="https://magazine.atavist.com/american-hippopotamus/"&gt;import hippopotami&lt;/a&gt;
to America to raise them for food in the early 1900s.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The state flag of Hawaii includes &lt;a href="https://www.bbc.com/news/magazine-35890670"&gt;the Union Jack&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The hamster&amp;rsquo;s Arabic name translates to &amp;ldquo;Mr. Saddlebags&amp;rdquo; in reference to its roomy cheek pouches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rivers Cuomo has a &lt;a href="https://github.com/riverscuomo"&gt;GitHub account&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oprah&amp;rsquo;s birth name was &lt;a href="https://en.wikipedia.org/wiki/Oprah_Winfrey#Early_life"&gt;Orpah&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hallmark Cards owns &lt;a href="https://en.wikipedia.org/wiki/Hallmark_Cards"&gt;Crayola&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Steve Aoki is the &lt;a href="https://mobile.twitter.com/merrittk/status/1578095308151873538"&gt;heir&lt;/a&gt; to the Benihana fortune. Also, actress Devon
Aoki is his half-sister.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I was extremely amused to discover that Pilates was founded by &lt;a href="https://www.nytimes.com/2022/10/14/style/the-fight-for-the-soul-of-pilates-moves-from-instagram-to-court.html"&gt;Joseph
Pilates&lt;/a&gt;. It&amp;rsquo;s like if baseball was started by Jim Baseball!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The sport of rugby is named after &lt;a href="https://en.wikipedia.org/wiki/Rugby_School"&gt;Rugby School&lt;/a&gt;, 300 years after its
founding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sagittarius B2 is a giant molecular cloud that (probably) smells like
&lt;a href="https://en.wikipedia.org/wiki/Sagittarius_B2"&gt;raspberry rum&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shrapnel is named after &lt;a href="https://en.wikipedia.org/wiki/Henry_Shrapnel"&gt;Henry Shrapnel&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aqua Net was a popular hair spray of the 1960s. It was created by Rayette,
which used the profits to buy&amp;hellip;Faberg&amp;eacute;, the jewellery company, for no
reason I&amp;rsquo;ve been able to find. Maybe Rayette just really liked fancy eggs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Dutch baseball league is called &lt;a href="https://en.wikipedia.org/wiki/Honkbal_Hoofdklasse"&gt;Honkbal Hoofdklasse&lt;/a&gt;, which is an
extremely funny name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are &lt;a href="https://bradfieldpiano.com/grand-vs-baby-grand-piano/"&gt;7 sizes of piano&lt;/a&gt;, not just &amp;ldquo;regular&amp;rdquo; and &amp;ldquo;grand&amp;rdquo;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The town of &lt;a href="https://en.wikipedia.org/wiki/Kenner,_Louisiana"&gt;Kenner, Louisiana&lt;/a&gt; was called &amp;ldquo;Cannes-Brûlées&amp;rdquo; by the French
settlers. I just love that they landed there and thought &amp;ldquo;It&amp;rsquo;s just like
Cannes, except it&amp;rsquo;s a million degrees hotter&amp;rdquo;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zellerbach Hall, a local performing arts space in the SF Bay Area, is named
after the &lt;a href="https://en.wikipedia.org/wiki/Crown_Zellerbach"&gt;Crown Zellerbach corporation&lt;/a&gt;, which invented modern
molded egg cartons (among other things).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Ratherius"&gt;Ratherius&lt;/a&gt;, a bishop born in 890 CE, was so quarrelsome and
difficult to get along with that he was sent to prison and exiled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Pit_viper"&gt;Pit vipers&lt;/a&gt; are called that not because they like to hang out in
pits in the ground but because they have an infrared organ that is exposed
via a deep pit on their head.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>What I learned building Candle in Rust</title>
    <id>http://gabebw.com/blog/2019/10/13/learning-rust-by-candlelight</id>
    <link rel="alternate" href="http://gabebw.com/blog/2019/10/13/learning-rust-by-candlelight"/>
    <published>2019-10-13T05:00:00+00:00</published>
    <updated>2019-10-13T05:00:00+00:00</updated>
    <author>
      <name>Gabe Berke-Williams</name>
    </author>
    <summary type="html">Useful Rust patterns I learned from building Candle.</summary>
    <content type="html">&lt;p&gt;&lt;a href="https://github.com/gabebw/candle"&gt;Candle&lt;/a&gt; is a tool that takes in a body of
HTML plus some CSS selectors, then runs the CSS against the HTML to find what you&amp;rsquo;re
looking for. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo "&amp;lt;h1 id='my-id'&amp;gt;&amp;lt;span&amp;gt;text&amp;lt;/span&amp;gt;&amp;lt;/h1&amp;gt;" | candle 'h1 {text}, h1 attr{id}'
text
my-id
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s the largest project I&amp;rsquo;ve built in Rust so far, and I learned some excellent
parts of Rust that will continue to be useful for my new projects.&lt;/p&gt;

&lt;h2 id="i-like-filter_map"&gt;I like &lt;code&gt;filter_map&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;It&amp;rsquo;s a small thing, but I like the &lt;code&gt;filter_map&lt;/code&gt; method. Here&amp;rsquo;s an example of
where it&amp;rsquo;s useful.&lt;/p&gt;

&lt;p&gt;In Candle, each combination of a selector and an operation (e.g. &lt;code&gt;a.highlighted
attr{href}&lt;/code&gt;) is called a &lt;code&gt;Finder&lt;/code&gt;. Each &lt;code&gt;Finder&lt;/code&gt; is tested against each
HTML element and its operation is run if it matches:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lightly edited to remove some code that's not important here&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Finder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;match_and_apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ElementRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.selector&lt;/span&gt;&lt;span class="nf"&gt;.matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.operation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;FinderOperation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nn"&gt;FinderOperation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="c1"&gt;// Some(value) or None, if the attr doesn't exist&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="nn"&gt;FinderOperation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the selector matches &lt;em&gt;and&lt;/em&gt; the operation returned something, it returns
&lt;code&gt;Some(...)&lt;/code&gt;. Otherwise, it return &lt;code&gt;None&lt;/code&gt;. Returning an &lt;code&gt;Option&lt;/code&gt; here makes it
easy to filter out empty results using &lt;code&gt;filter_map&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;finders&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.filter_map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="nf"&gt;.match_and_apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;filter_map&lt;/code&gt; runs the closure, and if the closure returns &lt;code&gt;Some(x)&lt;/code&gt;, it uses &lt;code&gt;x&lt;/code&gt;
(i.e. it auto-unwraps the value) and if it returns &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;filter_map&lt;/code&gt; discards
the value entirely. So it turns &lt;code&gt;Vec&amp;lt;Option&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt; into &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt;. This
pattern pops up pretty often and &lt;code&gt;filter_map&lt;/code&gt; is a neat solution.&lt;/p&gt;

&lt;h2 id="the-read-trait-and-testing"&gt;The &lt;code&gt;Read&lt;/code&gt; trait and testing&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://doc.rust-lang.org/std/io/trait.Read.html"&gt;&lt;code&gt;Read&lt;/code&gt; trait&lt;/a&gt; allows for
reading bytes from a source. It requires one method, &lt;code&gt;read&lt;/code&gt;. Other methods are
defined in terms of &lt;code&gt;Read&lt;/code&gt;. (This is similar to how Rust&amp;rsquo;s &lt;code&gt;Iterable&lt;/code&gt; module
only requires a &lt;code&gt;next&lt;/code&gt; method but gives many other methods defined in terms of
&lt;code&gt;next&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;I had a method that read from &lt;code&gt;io::stdin&lt;/code&gt;, called &lt;code&gt;read_from_stdin&lt;/code&gt; that took
zero arguments. In order to test it, I changed it to instead take in anything
that implements &lt;code&gt;Read&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-fn read_from_stdin() -&amp;gt; Option&amp;lt;String&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+fn read_from&amp;lt;R: Read&amp;gt;(mut reader: R) -&amp;gt; Option&amp;lt;String&amp;gt; {
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now instead of calling &lt;code&gt;read_from_stdin&lt;/code&gt;, I call &lt;code&gt;read_from(io::stdin())&lt;/code&gt;. The
real benefit comes from testing the method. I can now pass in a
&lt;a href="https://doc.rust-lang.org/std/io/struct.Cursor.html"&gt;&lt;code&gt;Cursor&lt;/code&gt;&lt;/a&gt;, which lets us
use slices or vectors as if they&amp;rsquo;re files. Here&amp;rsquo;s the full test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_less_than_1024_bytes_of_html&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;r#"
        &amp;lt;!DOCTYPE html&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;title&amp;gt;Hello, world!&amp;lt;/title&amp;gt;
        &amp;lt;h1 class="foo"&amp;gt;Hello, &amp;lt;i&amp;gt;world!&amp;lt;/i&amp;gt;&amp;lt;/h1&amp;gt;
    "#&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(&lt;a href="https://github.com/gabebw/candle/commit/9f38d93aae971336bc101909380e6bf7b7a9472d"&gt;Here&amp;rsquo;s&lt;/a&gt;
the commit where I added &lt;code&gt;Read&lt;/code&gt; and &lt;code&gt;Cursor&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cursor&lt;/code&gt; is a very helpful struct and I&amp;rsquo;ll definitely keep it in mind when
writing future tests.&lt;/p&gt;

&lt;h2 id="pipes-and-panic"&gt;Pipes and panic&lt;/h2&gt;

&lt;p&gt;In an early version of Candle, piping the output to anything that truncated the
output (like &lt;code&gt;head&lt;/code&gt;) would cause it to panic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;$ curl --silent daringfireball.net | candle 'html {html}' | head -1
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"daringfireball-net"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', src/libstd/io/stdio.rs:792:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a common problem with any Rust program that writes to the terminal in
Rust. There&amp;rsquo;s an &lt;a href="https://github.com/rust-lang/rust/issues/46016"&gt;open Rust issue about
it&lt;/a&gt;. As I understand it, when
enough output has been read, the kernel closes the pipe and sends a SIGPIPE
signal to the application that&amp;rsquo;s generating output (i.e. &lt;code&gt;candle&lt;/code&gt;) so it
gracefully dies. However, Rust ignores SIGPIPE, and keeps trying to generate
output, and then panics when it detects the now-broken pipe.&lt;/p&gt;

&lt;p&gt;Specifically, the issue is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;println!&lt;/code&gt; panics when it runs into any errors. The fix is to use &lt;code&gt;writeln!&lt;/code&gt;,
which does not panic but instead returns &lt;code&gt;std::io::Result&amp;lt;()&amp;gt;&lt;/code&gt;. We can then
catch broken pipes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;writeln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.kind&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nn"&gt;ErrorKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BrokenPipe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This new code does 2 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It catches the &lt;code&gt;Err&lt;/code&gt; and ignores it if it&amp;rsquo;s a broken pipe, and&lt;/li&gt;
&lt;li&gt;If it&amp;rsquo;s a real error, it prints the error to STDERR and exits with
an error code (but &lt;em&gt;without&lt;/em&gt; printing a gross error message)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is much better than &lt;code&gt;println!&lt;/code&gt;&amp;lsquo;s panicky behavior.&lt;/p&gt;

&lt;p&gt;(I did this in two PRs: &lt;a href="https://github.com/gabebw/candle/pull/13"&gt;#13&lt;/a&gt; and
&lt;a href="https://github.com/gabebw/candle/pull/14"&gt;#14&lt;/a&gt;.)&lt;/p&gt;
</content>
  </entry>
</feed>
