<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Jonfk</title>
      <link>https://www.jonfk.ca</link>
      <description>jonfk&#x27;s home on the web</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.jonfk.ca/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sun, 11 May 2025 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Exploring Neovim Configs can be Tiring</title>
          <pubDate>Sun, 11 May 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/neovim-config-explorations-issues/</link>
          <guid>https://www.jonfk.ca/blog/neovim-config-explorations-issues/</guid>
          <description xml:base="https://www.jonfk.ca/blog/neovim-config-explorations-issues/">&lt;p&gt;I want a faster way to understand a Neovim setup than grepping through Lua or reading through multiple websites documentation.&lt;br &#x2F;&gt;
Right now I can &lt;em&gt;sort of&lt;&#x2F;em&gt; discover keymaps via a picker &lt;code&gt;:lua Snacks.picker.keymap()&lt;&#x2F;code&gt;, &lt;code&gt;:Telescope keymaps&lt;&#x2F;code&gt;, or the classic &lt;code&gt;:map&lt;&#x2F;code&gt;, &lt;code&gt;:imap&lt;&#x2F;code&gt;, etc., and I can read plugin help—but there’s no single place that surfaces &lt;strong&gt;everything&lt;&#x2F;strong&gt; (keymaps, commands, plugin tweaks) in one go.&lt;&#x2F;p&gt;





  


&lt;div class=&quot;callout callout-info&quot;&gt;
  &lt;i class=&quot;callout-icon fas fa-info-circle&quot;&gt;&lt;&#x2F;i&gt;
  &lt;div class=&quot;callout-body&quot;&gt;
    &lt;p&gt;&lt;strong&gt;What’s a “picker”?&lt;&#x2F;strong&gt;
In Neovim land, a &lt;em&gt;picker&lt;&#x2F;em&gt; is an interactive fuzzy-finder window—typically powered by &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nvim-telescope&#x2F;telescope.nvim&quot;&gt;&lt;strong&gt;Telescope.nvim&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; or Folke’s &lt;strong&gt;Snacks&lt;&#x2F;strong&gt; plugin—that lets you search lists (files, keymaps, commands) and act on a selected item.&lt;&#x2F;p&gt;

  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;where-current-tools-fall-short&quot;&gt;Where Current Tools Fall Short&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;&#x2F;th&gt;&lt;th&gt;Strength&lt;&#x2F;th&gt;&lt;th&gt;Pain Point&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Telescope &#x2F; Snacks pickers&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Fast fuzzy search&lt;&#x2F;td&gt;&lt;td&gt;Still bounce between multiple pickers for keymaps, commands, help, and settings. And you kind of need to know what you are looking for.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;&lt;code&gt;:map&lt;&#x2F;code&gt;, &lt;code&gt;:imap&lt;&#x2F;code&gt;, &lt;code&gt;:verbose map {lhs}&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Raw lists of mappings&lt;&#x2F;td&gt;&lt;td&gt;Zero context—hard to see &lt;em&gt;why&lt;&#x2F;em&gt; and &lt;em&gt;where&lt;&#x2F;em&gt; a mapping was set&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;which-key.nvim&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Gorgeous pop-ups&lt;&#x2F;td&gt;&lt;td&gt;Can’t enumerate &lt;strong&gt;insert-mode&lt;&#x2F;strong&gt; maps or show a fully-flattened view&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Plugin docs&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Detailed reference&lt;&#x2F;td&gt;&lt;td&gt;Scattered across dozens of Markdown files; you must already know which plugin to look at. A lot to read through when you are looking for something in particular.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Put together, that’s a lot of jumping around and context switching.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-configs-so-far&quot;&gt;My Configs So Far&lt;&#x2F;h2&gt;
&lt;p&gt;I have been using &lt;strong&gt;kickstart.nvim&lt;&#x2F;strong&gt; for a while now and this rabbit hole began when I decided it was time to &lt;strong&gt;upgrade my own config&lt;&#x2F;strong&gt; and figured I’d see what other distros were doing right. I tried out LazyVim, LunarVim, NvChad, and friends.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kickstart&quot;&gt;Kickstart&lt;&#x2F;h3&gt;
&lt;p&gt;You read the entire config and own it. I still had blind spots (the &lt;code&gt;lspconfig&lt;&#x2F;code&gt; &#x2F; Mason interactions), but I still felt like I knew my config and where to change something if I needed to. Because the expected use is to pull the config and start modifying it for your own use, I found upgrading your current kickstart config can get difficult. You have to handle merge conflicts, so of which can include major refactorings and outright changes to the plugins used by the config. This means that an upgrade is more than simply resolving merge conflicts to options, you might need to be ready to understand the config anew.&lt;&#x2F;p&gt;
&lt;p&gt;This is why I found it easier to simply restart the config whenever I am upgrading kickstart. It&#x27;s actually a pretty nice process since on every upgrade you understand neovim much more and can make deeper and more specific changes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;full-featured-distros&quot;&gt;Full-featured distros&lt;&#x2F;h3&gt;
&lt;p&gt;A bunch of Lua wires everything up, but these distros don&#x27;t usually expect the average user to read through them. You’re &lt;em&gt;meant&lt;&#x2F;em&gt; to override their configs to customize them, yet finding the right lever is a scavenger hunt. (I will admit that only LazyVim got a real trial. I only played around a bit with LunarVim)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Bottom line&lt;&#x2F;strong&gt;: distro exploration feels like spelunking without a headlamp.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exploration-fatigue&quot;&gt;Exploration Fatigue&lt;&#x2F;h2&gt;
&lt;p&gt;It might very well be a skill issue on my part but I find that exploring a config to find out keymaps, commands and other configured settings quite scattered and not straightforward at all.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Quick demo: find &lt;em&gt;where&lt;&#x2F;em&gt; a mapping is defined&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I use this when I&#x27;ve spotted a mapping but don’t know which plugin or file created it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;vim&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-vim &quot;&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; Normal-mode maps that start with &amp;lt;leader&amp;gt;f
&lt;&#x2F;span&gt;&lt;span&gt;:verbose &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;nmap &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;&amp;lt;leader&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;f
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; Output ends with something like:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot;     Last set from &#x2F;path&#x2F;to&#x2F;plugin&#x2F;file.lua line 42
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When I am exploring things generally, this is what I do.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;vim&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-vim &quot;&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; List all normal-mode keymaps via Telescope
&lt;&#x2F;span&gt;&lt;span&gt;:Telescope keymaps mode=n
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; Get every mapping (across all modes) and dump to a scratch buffer
&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;lua&lt;&#x2F;span&gt;&lt;span&gt; vim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(vim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;tbl_map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(m) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lhs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;, vim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;nvim_get_keymap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; Fuzzy-find every help tag—great for plugin docs
&lt;&#x2F;span&gt;&lt;span&gt;:Telescope help_tags
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; Jump straight to the help page for a specific plugin (e.g. Telescope)
&lt;&#x2F;span&gt;&lt;span&gt;:help telescope&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;nvim
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&amp;quot; (Most plugins expose a tag that matches their repo name.)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each of these helps, but none gives me the single dashboard I’m after.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-better-approach-a-queryable-neovim&quot;&gt;A Better Approach?: A Queryable Neovim&lt;&#x2F;h2&gt;
&lt;p&gt;Imagine chatting with your editor:&lt;&#x2F;p&gt;
&lt;p&gt;“What keymaps are active right now?”
“Who defines gx, and where?”
“List all Telescope customisations.”&lt;&#x2F;p&gt;
&lt;p&gt;Simply dumping config files into an LLM hits token limits on big distros.
A more scalable path is to expose runtime APIs an LLM can call:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;list installed plugins&lt;&#x2F;li&gt;
&lt;li&gt;fetch help for any command&#x2F;topic&lt;&#x2F;li&gt;
&lt;li&gt;enumerate active keymaps with their sources&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With that, an MCP-style RPC server could feed structured answers to the model.
There’s already a promising skeleton: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigcodegen&#x2F;mcp-neovim-server&quot;&gt;bigcodegen&#x2F;mcp-neovim-server&lt;&#x2F;a&gt; aimed at editing assistance; its architecture looks adaptable for this my use-case. I might be able to modify it to do what I want.&lt;&#x2F;p&gt;





  


&lt;div class=&quot;callout callout-info&quot;&gt;
  &lt;i class=&quot;callout-icon fas fa-info-circle&quot;&gt;&lt;&#x2F;i&gt;
  &lt;div class=&quot;callout-body&quot;&gt;
    &lt;p&gt;What’s MCP?
Model-Control-Protocol: a lightweight JSON-RPC layer that lets an LLM plugin talk to Neovim over stdio or TCP, request data, and send actions back.&lt;&#x2F;p&gt;

  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Have you hacked on something similar, or know plugins I’ve missed?
Drop me a note. I’d love to swap ideas and maybe prototype this together.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Bye tmux, hello neovim. How I am trying neovim as my terminal multiplexer</title>
          <pubDate>Sun, 27 Apr 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/neovim-as-terminal-multiplexer/</link>
          <guid>https://www.jonfk.ca/blog/neovim-as-terminal-multiplexer/</guid>
          <description xml:base="https://www.jonfk.ca/blog/neovim-as-terminal-multiplexer/">&lt;p&gt;I&#x27;ve been using Tmux as my terminal multiplexer for a little bit and well I have been pretty satisfied with the setup
that I have. I have a set of plugins that allow me to do some pretty things I&#x27;ve been wanting to try a Tmux free
workflow to that end I have tweaked my NeoVim config to allow Neovim to become the main central point of my
development workflow. Now that means that instead of my terminal being the central point that hosts all of the
other programs and processes to do various things in my workflow, I&#x27;m trying to replace that with Neovim. Let&#x27;s see
how this goes.&lt;&#x2F;p&gt;
&lt;p&gt;But before explaining how I&#x27;m going to do this with Neovim, let&#x27;s take a short detour how I&#x27;m currently using Tmux
as my terminal multiplexer.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;my-tmux-config-and-workflow&quot;&gt;My Tmux config and workflow&lt;&#x2F;h1&gt;
&lt;p&gt;You can find my full unedited tmux config at this link: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;blob&#x2F;82baa45823132a71b50a6a333e1bd750c4127f8b&#x2F;tmux&#x2F;.tmux.conf&quot;&gt;70e3965&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tmux-sessions&quot;&gt;Tmux Sessions&lt;&#x2F;h2&gt;
&lt;p&gt;My main workflow revolves around creating sessions for each project I am working on. I can then switch between sessions
my this little fzf command. I find the experience much better than the default choose tree. You can fuzzy find the
session you are looking for in a few keys real quick.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;bind s display-popup -E &amp;quot;tmux list-sessions | sed -E &amp;#39;s&#x2F;:.*$&#x2F;&#x2F;&amp;#39; | grep -v \&amp;quot;^$(tmux display-message -p &amp;#39;#S&amp;#39;)\$\&amp;quot; | fzf --reverse --preview &amp;#39;tmux capture-pane -ep -t {}&amp;#39; --preview-window=right:60% | xargs tmux switch-client -t&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;vi-copy-mode&quot;&gt;Vi Copy Mode&lt;&#x2F;h2&gt;
&lt;p&gt;For getting text in and out of a tmux buffer, I use the &lt;code&gt;vi&lt;&#x2F;code&gt; copy-mode. It allows me to use vi keybindings to navigate,
yank, paste, etc.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;# vi bindings for copy mode
&lt;&#x2F;span&gt;&lt;span&gt;setw -g mode-keys vi
&lt;&#x2F;span&gt;&lt;span&gt;bind -T copy-mode-vi v send -X begin-selection
&lt;&#x2F;span&gt;&lt;span&gt;bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel
&lt;&#x2F;span&gt;&lt;span&gt;bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;tmux-windows-in-sessions-and-workflow&quot;&gt;Tmux windows in sessions and workflow&lt;&#x2F;h2&gt;
&lt;p&gt;A little quality of life thing I have is to start tmux windows at 1 instead of at 0. That&#x27;s because it keeps all the
window numbers close together even if you don&#x27;t have a lot of windows. Not a super big thing, but helps a lot I find.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;# Start windows and panes at 1, not 0
&lt;&#x2F;span&gt;&lt;span&gt;set -g base-index 1
&lt;&#x2F;span&gt;&lt;span&gt;setw -g pane-base-index 1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While we are here, let&#x27;s talk about how I create windows for each session. Like many other vimmers, my first window
in a tmux session, is a Neovim window. The next window (2), is always a shell prompt. It is always free for interactive
use. This allows me to go back and forth between the shell and nvim.&lt;&#x2F;p&gt;
&lt;p&gt;Then the next few other buffers are project dependent, but a few common ones are a file watcher that runs and rebuilds
the project as files change, documentation, an ssh session, etc.&lt;&#x2F;p&gt;
&lt;p&gt;I am a 1 window, 1 screen kind of person. While I have experimented with splits and multiple screens in the past, I
found that there would always be 1 main window at which I would always staring at, and a bunch of other things on the
other screens that I would really rarely care about. And if I tried to make more effective use of the other screens,
I would be moving my head around way too much and giving me neck strain. Overall, I found my comfort best with 1 screen
on which I concentrate on, and being able to switch between the various windows or tabs effectively and quickly. How I do
that quickly and effectively, is program and OS dependent. I might expand on that one day. For now you can take a quick
look at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;blob&#x2F;82baa45823132a71b50a6a333e1bd750c4127f8b&#x2F;hammerspoon&#x2F;.hammerspoon&#x2F;init.lua&quot;&gt;my hammerspoon config&lt;&#x2F;a&gt;
if you want to know how I do that for things outside of tmux and nvim.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tmux-plugins&quot;&gt;Tmux plugins&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, here&#x27;s the shortlist of plugins that I use. I try to keep it fairly short since I generally don&#x27;t like to
have to remember too many different things from various plugins. Let&#x27;s go through them.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;set -g @plugin &amp;#39;tmux-plugins&#x2F;tpm&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;tmux-plugins&#x2F;tmux-sensible&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;catppuccin&#x2F;tmux#v2.1.1&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;tmux-plugins&#x2F;tmux-yank&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;christoomey&#x2F;vim-tmux-navigator&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;tmux-plugins&#x2F;tmux-resurrect&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;set -g @plugin &amp;#39;tmux-plugins&#x2F;tmux-continuum&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;TPM&lt;&#x2F;strong&gt; is the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tmux-plugins&#x2F;tpm&quot;&gt;Tmux Plugin Manager&lt;&#x2F;a&gt;.
It&#x27;s the easiest way to manage plugins. Although it is a bit annoying that I need to manually fetch and initialize
it before it can do it&#x27;s work. Maybe one day it could come default with a tmux install.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;tmux-sensible&lt;&#x2F;strong&gt; is a small package with a bunch of default settings that are pretty sensible. I used to carry around
a bunch of such settings and found that that package was pretty much what I wanted and cleaned up my config.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;catppuccin&lt;&#x2F;strong&gt; is nice for the eyes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;tmux-yank&lt;&#x2F;strong&gt; allows to yank to the system clipboard which makes integrating with other applications much easier.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;vim-tmux-navigator&lt;&#x2F;strong&gt; gives keybinds for switching between tmux panes and nvim panes easy. I honestly used it very little
because I rarely do splits in tmux.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;tmux-resurrect&lt;&#x2F;strong&gt; and &lt;strong&gt;tmux-continuum&lt;&#x2F;strong&gt; for session persistence. Resurrect restarts sessions from the previous instance
of tmux and continuum autosaves the sessions so I don&#x27;t have to think about it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;what-am-i-going-to-do-in-neovim&quot;&gt;What am I going to do in Neovim?&lt;&#x2F;h1&gt;
&lt;p&gt;So as I mentioned earlier, I basically plan on making NeoVim my terminal multiplexer. So instead of starting a
terminal instance and then starting Tmux sessions inside of that terminal instance and in those Tmux sessions start
the NeoVim instances and various other shell prompts. I will start a Neovim instance and from there start terminal
buffers inside of Neovim.&lt;&#x2F;p&gt;
&lt;p&gt;From my light amount of testing of this workflow, I found that NeoVim&#x27;s terminal works pretty well for me and my zsh
config. So I&#x27;m pretty happy with zsh in Neovim.&lt;&#x2F;p&gt;
&lt;p&gt;As for how I will replicate sessions into Neovim, well &lt;a href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;doc&#x2F;user&#x2F;starting.html#_views-and-sessions&quot;&gt;Neovim&lt;&#x2F;a&gt;
has &lt;code&gt;:mksessions&lt;&#x2F;code&gt; which allows you to create sessions and I&#x27;ll be using a few plugins to basically do the same thing as
what I was doing with tmux-resurrect and tmux-continuum which is to restart sessions whenever the tmux instance was
killed or in this case the Neovim instance gets killed and autosave the sessions as I am using them.&lt;&#x2F;p&gt;
&lt;p&gt;I am currently trying out the &lt;strong&gt;auto-session&lt;&#x2F;strong&gt; plugin. You can see the changes to my config here: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;commit&#x2F;7d6bea52b6088bef6b50dc2b7e7990eb513e4b37&quot;&gt;commit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For terminal buffers, I can start them with &lt;code&gt;:terminal&lt;&#x2F;code&gt; and I can switch between the buffers as I did before with
telescope. I can even rename the terminal buffers with &lt;code&gt;:file new-buffer-name&lt;&#x2F;code&gt; if I ever need longer running instances.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that I would like to figure out would be to have tmux like persistence of the shell history when restarting
the sessions and the ability to restart the previously running processes in the terminal buffers if the session is
killed. But for now these are minor concerns that I will tackle when the time comes.&lt;&#x2F;p&gt;
&lt;p&gt;Now, in closing, I would like to mention that what allows me to do this is the fact that I make a fairly lightweight
use of Tmux, other than being able to yank things from the buffers, being able to save sessions and restart sessions
automatically, and basically a nice little visual flair with catpucchin. And I think those can be fairly easily
replicated inside of neovim. This may not be without issues for people who make much heavier use of split spans and
various other power features of tmux but since that&#x27;s not me I&#x27;m gonna give it a try.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A Git Configuration Overhaul</title>
          <pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/upgrading-my-gitconfig/</link>
          <guid>https://www.jonfk.ca/blog/upgrading-my-gitconfig/</guid>
          <description xml:base="https://www.jonfk.ca/blog/upgrading-my-gitconfig/">&lt;p&gt;I was drinking my morning coffee while browsing the usual tech updates when I stumbled across &lt;a href=&quot;https:&#x2F;&#x2F;blog.gitbutler.com&#x2F;how-git-core-devs-configure-git&#x2F;&quot;&gt;an article&lt;&#x2F;a&gt; about how core Git developers configure their own &lt;code&gt;git config&lt;&#x2F;code&gt; settings. This immediately caught my attention, since I was curious to learn how the people who actually work on Git configure their own setups. It seemed like valuable insider knowledge.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-spartan-config-before&quot;&gt;My Spartan Config Before&lt;&#x2F;h2&gt;
&lt;p&gt;My Git configuration was pretty minimal before reading this article:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[user]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Jonathan Fok kan
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; jonathan&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;@fokkan.&lt;&#x2F;span&gt;&lt;span&gt;ca
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[core]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;editor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nvim
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[alias]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;review &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!f(){ git fetch $1 pull&#x2F;$2&#x2F;head;git checkout FETCH_HEAD;}; f&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;shove &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!f(){ git push --force-with-lease $1 $2;}; f&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[includeIf &amp;quot;gitdir:~&#x2F;work&#x2F;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= .&lt;&#x2F;span&gt;&lt;span&gt;gitconfig&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;work
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The only somewhat advanced thing I was using was the &lt;code&gt;includeIf&lt;&#x2F;code&gt; directive, which allows different configurations for personal versus work projects and different SSH keys for each context.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-learned&quot;&gt;What I Learned&lt;&#x2F;h2&gt;
&lt;p&gt;The article detailed settings that even core Git developers change from the defaults. It turns out there are numerous quality-of-life improvements that could be made with just a few configuration tweaks.&lt;&#x2F;p&gt;
&lt;p&gt;One of the &lt;a href=&quot;https:&#x2F;&#x2F;blog.gitbutler.com&#x2F;git-autosquash&#x2F;&quot;&gt;linked articles&lt;&#x2F;a&gt; about &lt;code&gt;git autosquash&lt;&#x2F;code&gt; was particularly intriguing. At a high level, autosquash allows you to create &quot;fixup&quot; commits that automatically get squashed into the right place during an interactive rebase. Instead of manually reordering commits during a rebase, you can mark a commit as a fixup for a previous commit, and Git will handle the placement automatically. This would make one of my common Git workflows much more efficient, though I&#x27;ll need to experiment with it more before it fully clicks.&lt;&#x2F;p&gt;
&lt;details&gt;
	&lt;summary&gt;A video showing how autosquash works&lt;&#x2F;summary&gt;
	&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;Md44rcw13k4?si=ZhNb3o0zH2Ix3oCv&amp;amp;start=745&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;From the Hacker News comments, I discovered &lt;a href=&quot;https:&#x2F;&#x2F;dandavison.github.io&#x2F;delta&#x2F;&quot;&gt;Delta&lt;&#x2F;a&gt;, a syntax-highlighting pager for Git. I&#x27;ve implemented it, and while I appreciate the syntax highlighting, I find the UI a bit busy. I&#x27;m curious to see how it displays three-way diffs with &lt;code&gt;zdiff3&lt;&#x2F;code&gt;, but I might need some time to get used to it compared to the plain diff view I&#x27;m accustomed to.&lt;&#x2F;p&gt;
&lt;p&gt;I also learned that adding &lt;code&gt;-p&lt;&#x2F;code&gt; to various Git commands like &lt;code&gt;log&lt;&#x2F;code&gt; and &lt;code&gt;add&lt;&#x2F;code&gt; will show the actual changes, which is super useful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-expanded-config-after&quot;&gt;My Expanded Config After&lt;&#x2F;h2&gt;
&lt;p&gt;After reading the article and comments, I&#x27;ve significantly expanded my Git configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[user]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Jonathan Fok kan
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; jonathan&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;@fokkan.&lt;&#x2F;span&gt;&lt;span&gt;ca
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[core]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;editor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nvim
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[alias]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;review &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!f(){ git fetch $1 pull&#x2F;$2&#x2F;head;git checkout FETCH_HEAD;}; f&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;shove &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!f(){ git push --force-with-lease $1 $2;}; f&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;out &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;log @{u}..&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[column]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;ui &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; auto
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[branch]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;sort &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= -&lt;&#x2F;span&gt;&lt;span&gt;committerdate
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[tag]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;sort &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;refname
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[init]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;defaultBranch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; main
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[diff]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;algorithm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; histogram
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;colorMoved &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; plain
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;mnemonicPrefix &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;renames &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[push]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; simple
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;autoSetupRemote &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;followTags &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[fetch]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;prune &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;pruneTags &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;all &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[help]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;autocorrect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; prompt
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[commit]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;verbose &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[rerere]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;enabled &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;autoupdate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[rebase]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;autoSquash &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;autoStash &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;updateRefs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[pull]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;rebase &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[core]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# a matter of taste (uncomment if you dare)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# Useful for really large repos but adds the overhead of a process per repository
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# fsmonitor = true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# untrackedCache = true
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[core]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;pager &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; delta
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[interactive]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;diffFilter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; delta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;--&lt;&#x2F;span&gt;&lt;span&gt;color&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;only
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[delta]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;navigate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# use n and N to move between diff sections
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;dark &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# or light = true, or omit for auto-detection
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;[merge]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# (just &amp;#39;diff3&amp;#39; if git version &amp;lt; 2.3)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conflictstyle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; zdiff3
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some highlights of what I&#x27;ve added:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Better branch and tag sorting (&lt;code&gt;branch.sort&lt;&#x2F;code&gt;, &lt;code&gt;tag.sort&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Improved diff algorithm and visualization (&lt;code&gt;diff.algorithm&lt;&#x2F;code&gt;, &lt;code&gt;colorMoved&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Enhanced pushing and fetching (&lt;code&gt;push.autoSetupRemote&lt;&#x2F;code&gt;, &lt;code&gt;fetch.prune&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Rebase improvements (&lt;code&gt;rebase.autoSquash&lt;&#x2F;code&gt;, &lt;code&gt;rebase.autoStash&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Delta integration for prettier diffs&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;out&lt;&#x2F;code&gt; alias for listing all unpushed commits
&lt;ul&gt;
&lt;li&gt;Read it with the Terminator&#x27;s voice&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m particularly looking forward to seeing how the &lt;code&gt;zdiff3&lt;&#x2F;code&gt; conflict style helps with merge conflicts, as this was highly recommended by the core developers.&lt;&#x2F;p&gt;
&lt;p&gt;While I haven&#x27;t enabled the filesystem monitoring options (&lt;code&gt;fsmonitor&lt;&#x2F;code&gt; and &lt;code&gt;untrackedCache&lt;&#x2F;code&gt;), I might consider them for larger repositories despite the overhead of running additional processes.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s amazing how much I was able to improve my Git experience with just these configuration changes. Sometimes it pays to see how the experts configure their own tools!&lt;&#x2F;p&gt;
&lt;p&gt;Here is a link to my dotfiles repo if you are interested. There might have been more updates since. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;git&#x2F;.gitconfig&quot;&gt;Github&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.gitbutler.com&#x2F;how-git-core-devs-configure-git&#x2F;&quot;&gt;How Core Git Developers Configure Git&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=43169435&quot;&gt;HN comments for &quot;How Core Git Developers Configure Git&quot;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.gitbutler.com&#x2F;git-autosquash&#x2F;&quot;&gt;Fixing up Git with Autosquash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>My keyboard symbol layer</title>
          <pubDate>Mon, 24 Feb 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/my-symbol-layer/</link>
          <guid>https://www.jonfk.ca/blog/my-symbol-layer/</guid>
          <description xml:base="https://www.jonfk.ca/blog/my-symbol-layer/">&lt;h1 id=&quot;background&quot;&gt;Background&lt;&#x2F;h1&gt;
&lt;p&gt;I am a dvorak keyboard user. I learned to touch type fairly late in life, in university at the age of 18 or so. Before
then, I had my own idiosyncratic way of touch typing with 2 or 3 fingers and glancing to the keyboard for certain keys.
To improve my typing accuracy and speed while doing my CS degree, I tried to learn it on qwerty but had a hard time
because I already had habits that were hard to break. To break those habits and learn from a clean slate, I ended up
learning dvorak.&lt;&#x2F;p&gt;
&lt;p&gt;Aside: Dvorak was the popular alternative keyboard layout at the time, while there is much more in this space now, I
think dvorak is a decent choice since I noticed a lot of support from various software at all levels of the stack
from games to OSes. But the level of support might not really be necessary now with custom keyboards.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;qmk-co&quot;&gt;QMK &amp;amp; Co&lt;&#x2F;h1&gt;
&lt;p&gt;I tried the Planck not that long ago. The ortholinear layout promised vast improvements for homerowing because all
the keys are parallel, it&#x27;s easier to move around. At least that was the promise. I found it hard to get used to,
but still persevered. What stopped me was the wrist pain I started getting. I am not sure if it was the ortholinear or
the higher profile, but in the end I had to abandon it.&lt;&#x2F;p&gt;
&lt;p&gt;What really got me on it though was the QMK software, which allows you to customize the keyboard at the firmware level.
That was pretty incredible because you now don&#x27;t need to have OS or software support. All the customization you would
want can be done on the keyboard itself. There are so many options for customizations, I would recommend to take a look
for yourself if that interests you. A few in particular I found useful were the extra layers, home row mods, and tap dancing.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;layers&quot;&gt;Layers&lt;&#x2F;h1&gt;
&lt;p&gt;By default, keyboards are actually already layered. If you squint, the shift key is a layer trigger where each key now
has another character it can type. So layers are essentially a way to get extra keys under certain circumstances, the
simplest one being when a key is pressed and held such as with shift.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;my-customizations&quot;&gt;My customizations&lt;&#x2F;h1&gt;
&lt;p&gt;I currently type on a regular apple magic keyboard. I like it a lot mainly because it&#x27;s so similar to the Macbook Pro
keyboard which I am often using. I find the typing experience pretty comfortable.&lt;&#x2F;p&gt;
&lt;p&gt;Because it&#x27;s a regular keyboard, I don&#x27;t have many options for layer keys. But because of a few of my own idiosyncracies,
I don&#x27;t use the mod keys to the right of the spacebar much. So I am now experimenting with using the key to the right
of the spacebar as my symbol layer trigger.&lt;&#x2F;p&gt;
&lt;p&gt;The idea of the symbol layer is to map some of the special characters that I find hard to type for some reason.
I can&#x27;t exactly explain why but moving my right pinky and ring finger is hard and I often mistype those symbols. In
addition to that, I find that moving my right hand to reach the number row is slower and I often have a hard time
settling my fingers back to the homerow.&lt;&#x2F;p&gt;
&lt;p&gt;So to help with that I decided to remap the special characters that are on the right side of the keyboard while keeping
the characters that I am currently content with. I decided to add a small ux improvement that the symbol trigger also
acts as the shift for certain keys so that I can more easily access them. The reason for those is because I often favor
the left shift and therefore end up typing those left side symbols with one hand instead of 2 as would be the recommended
way.&lt;&#x2F;p&gt;
&lt;p&gt;Without much further ado, here are the layouts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;my-symbol-layer&#x2F;.&#x2F;keyboard-layout.png&quot; alt=&quot;keyboard layouts&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.keyboard-layout-editor.com&#x2F;##@@_a:7&amp;amp;w:14&amp;amp;h:1&amp;amp;d:true%3B&amp;amp;=Standard%2060%25%2F%2F65%25%20Apple%20Keyboard%20-%20QWERTY%20Base%20layer%3B&amp;amp;@%3B&amp;amp;@=%60&amp;amp;=1&amp;amp;=2&amp;amp;=3&amp;amp;=4&amp;amp;=5&amp;amp;=6&amp;amp;=7&amp;amp;=8&amp;amp;=9&amp;amp;=0&amp;amp;=-&amp;amp;=%2F=&amp;amp;_w:2%3B&amp;amp;=BKSP%3B&amp;amp;@_w:1.5%3B&amp;amp;=TAB&amp;amp;=Q&amp;amp;=W&amp;amp;=E&amp;amp;=R&amp;amp;=T&amp;amp;=Y&amp;amp;=U&amp;amp;=I&amp;amp;=O&amp;amp;=P&amp;amp;=%5B&amp;amp;=%5D&amp;amp;_w:1.5%3B&amp;amp;=%5C%3B&amp;amp;@_w:1.75%3B&amp;amp;=CAPS&amp;amp;=A&amp;amp;=S&amp;amp;=D&amp;amp;=F&amp;amp;=G&amp;amp;=H&amp;amp;=J&amp;amp;=K&amp;amp;=L&amp;amp;=%2F%3B&amp;amp;=&amp;#x27;&amp;amp;_w:2.25%3B&amp;amp;=RETURN%3B&amp;amp;@_w:2.25%3B&amp;amp;=SHIFT&amp;amp;=Z&amp;amp;=X&amp;amp;=C&amp;amp;=V&amp;amp;=B&amp;amp;=N&amp;amp;=M&amp;amp;=,&amp;amp;=.&amp;amp;=%2F%2F&amp;amp;_w:2.75%3B&amp;amp;=SHIFT%3B&amp;amp;@_w:1.25%3B&amp;amp;=LWIN&amp;amp;_w:1.25%3B&amp;amp;=LALT&amp;amp;_w:1.25%3B&amp;amp;=LCTL&amp;amp;_w:6.25%3B&amp;amp;=SPACE&amp;amp;_w:1.25%3B&amp;amp;=RCTL&amp;amp;_w:1.25%3B&amp;amp;=RALT&amp;amp;_w:1.25%3B&amp;amp;=Arrows%3B&amp;amp;@%3B&amp;amp;@_a:7&amp;amp;w:14&amp;amp;h:1&amp;amp;d:true%3B&amp;amp;=Dvorak%20Base%20layer%3B&amp;amp;@%3B&amp;amp;@_a:4%3B&amp;amp;=%60&amp;amp;=1&amp;amp;=2&amp;amp;=3&amp;amp;=4&amp;amp;=5&amp;amp;=6&amp;amp;=7&amp;amp;=8&amp;amp;=9&amp;amp;=0&amp;amp;=%5B&amp;amp;=%5D&amp;amp;_w:2%3B&amp;amp;=BKSP%3B&amp;amp;@_w:1.5%3B&amp;amp;=TAB&amp;amp;=&amp;#x27;&amp;amp;=,&amp;amp;=.&amp;amp;=P&amp;amp;=Y&amp;amp;=F&amp;amp;=G&amp;amp;=C&amp;amp;=R&amp;amp;=L&amp;amp;=%2F%2F&amp;amp;=%2F=&amp;amp;_w:1.5%3B&amp;amp;=%5C%3B&amp;amp;@_w:1.75%3B&amp;amp;=CAPS&amp;amp;=A&amp;amp;=O&amp;amp;=E&amp;amp;=U&amp;amp;=I&amp;amp;=D&amp;amp;=H&amp;amp;=T&amp;amp;=N&amp;amp;=S&amp;amp;=-&amp;amp;_w:2.25%3B&amp;amp;=RETURN%3B&amp;amp;@_w:2.25%3B&amp;amp;=SHIFT&amp;amp;=%2F%3B&amp;amp;=Q&amp;amp;=J&amp;amp;=K&amp;amp;=X&amp;amp;=B&amp;amp;=M&amp;amp;=W&amp;amp;=V&amp;amp;=Z&amp;amp;_w:2.75%3B&amp;amp;=SHIFT%3B&amp;amp;@_w:1.25%3B&amp;amp;=LWIN&amp;amp;_w:1.25%3B&amp;amp;=LALT&amp;amp;_w:1.25%3B&amp;amp;=LCTL&amp;amp;_w:6.25%3B&amp;amp;=SPACE&amp;amp;_w:1.25%3B&amp;amp;=RCTL&amp;amp;_w:1.25%3B&amp;amp;=RALT&amp;amp;_w:1.25%3B&amp;amp;=Arrows%3B&amp;amp;@%3B&amp;amp;@_a:7&amp;amp;w:14&amp;amp;h:1&amp;amp;d:true%3B&amp;amp;=Dvorak%20Symbol%20layer%3B&amp;amp;@%3B&amp;amp;@_a:4%3B&amp;amp;=&amp;amp;=!&amp;amp;=%2F@&amp;amp;=%23&amp;amp;=$&amp;amp;=%25&amp;amp;=%5E&amp;amp;=%2F&amp;amp;&amp;amp;=*&amp;amp;=(&amp;amp;=)&amp;amp;=%7B&amp;amp;=%20%7D&amp;amp;_w:2%3B&amp;amp;=BKSP%3B&amp;amp;@_w:1.5%3B&amp;amp;=TAB&amp;amp;=%22&amp;amp;=%3C&amp;amp;=%3E&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=%2F%2F&amp;amp;=%2F=&amp;amp;_w:1.5%3B&amp;amp;=%7C%3B&amp;amp;@_w:1.75%3B&amp;amp;=CAPS&amp;amp;=%5B&amp;amp;=%5D&amp;amp;=(&amp;amp;=)&amp;amp;=&amp;amp;=%20%5E&amp;amp;=%2F&amp;amp;&amp;amp;=*&amp;amp;=%2F%2F&amp;amp;=%2F=&amp;amp;=&amp;amp;_w:2.25%3B&amp;amp;=RETURN%3B&amp;amp;@_w:2.25%3B&amp;amp;=SHIFT&amp;amp;=%2F:&amp;amp;=&amp;amp;=%7B&amp;amp;=%7D&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;=&amp;amp;_w:2.75%3B&amp;amp;=SHIFT%3B&amp;amp;@_w:1.25%3B&amp;amp;=LWIN&amp;amp;_w:1.25%3B&amp;amp;=LALT&amp;amp;_w:1.25%3B&amp;amp;=LCTL&amp;amp;_w:6.25%3B&amp;amp;=SPACE&amp;amp;_w:1.25%3B&amp;amp;=RCTL&amp;amp;_w:1.25%3B&amp;amp;=RALT&amp;amp;_w:1.25%3B&amp;amp;=Arrows&quot;&gt;Layout on keyboard-layout-editor&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;details&gt;
	&lt;summary&gt;If you prefer plain text&lt;&#x2F;summary&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;# Standard 60%&#x2F;65% Apple Keyboard 
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## QWERTY Base layer
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;┌─────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
&lt;&#x2F;span&gt;&lt;span&gt;│ `   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ BKSP  │
&lt;&#x2F;span&gt;&lt;span&gt;├─────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ TAB   │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
&lt;&#x2F;span&gt;&lt;span&gt;├───────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ CAPS   │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ &amp;#39; │ RETURN │
&lt;&#x2F;span&gt;&lt;span&gt;├────────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ SHIFT    │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ &#x2F; │  SHIFT   │
&lt;&#x2F;span&gt;&lt;span&gt;├──────┬───┴┬──┴──┬┴───┴───┴───┴───┴───┴──┬┴───┴┬──┴─┬────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ LWIN │LALT│ LCTL│          SPACE        │ RCTL│RALT│ Arrows │
&lt;&#x2F;span&gt;&lt;span&gt;└──────┴────┴─────┴───────────────────────┴─────┴────┴────────┘
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## Dvorak Base layer
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;┌─────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
&lt;&#x2F;span&gt;&lt;span&gt;│ `   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ [ │ ] │ BKSP  │
&lt;&#x2F;span&gt;&lt;span&gt;├─────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ TAB   │ &amp;#39; │ , │ . │ P │ Y │ F │ G │ C │ R │ L │ &#x2F; │ = │  \  │
&lt;&#x2F;span&gt;&lt;span&gt;├───────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ CAPS   │ A │ O │ E │ U │ I │ D │ H │ T │ N │ S │ - │ RETURN │
&lt;&#x2F;span&gt;&lt;span&gt;├────────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ SHIFT    │ ; │ Q │ J │ K │ X │ B │ M │ W │ V │ Z │  SHIFT   │
&lt;&#x2F;span&gt;&lt;span&gt;├──────┬───┴┬──┴──┬┴───┴───┴───┴───┴───┴──┬┴───┴┬──┴─┬────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ LWIN │LALT│ LCTL│          SPACE        │ RCTL│RALT│ Arrows │
&lt;&#x2F;span&gt;&lt;span&gt;└──────┴────┴─────┴───────────────────────┴─────┴────┴────────┘
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## Dvorak Symbol layer
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;┌─────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
&lt;&#x2F;span&gt;&lt;span&gt;│     │ ! │ @ │ # │ $ │ % │ ^ │ &amp;amp; │ * │ ( │ ) │ { │ } │ BKSP  │
&lt;&#x2F;span&gt;&lt;span&gt;├─────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ TAB   │ &amp;quot; │ &amp;lt; │ &amp;gt; │   │   │   │   │   │   │   │ &#x2F; │ = │  |  │
&lt;&#x2F;span&gt;&lt;span&gt;├───────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ CAPS   │ [ │ ] │ ( │ ) │   │ ^ │ &amp;amp; │ * │ &#x2F; │ = │   │ RETURN │
&lt;&#x2F;span&gt;&lt;span&gt;├────────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ SHIFT    │ : │   │ { │ } │   │   │   │   │   │   │  SHIFT   │
&lt;&#x2F;span&gt;&lt;span&gt;├──────┬───┴┬──┴──┬┴───┴───┴───┴───┴───┴──┬┴───┴┬──┴─┬────────┤
&lt;&#x2F;span&gt;&lt;span&gt;│ LWIN │LALT│ LCTL│          SPACE        │ RCTL│RALT│ Arrows │
&lt;&#x2F;span&gt;&lt;span&gt;└──────┴────┴─────┴───────────────────────┴─────┴────┴────────┘
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;details&gt;
&lt;h1 id=&quot;implementation&quot;&gt;Implementation&lt;&#x2F;h1&gt;
&lt;p&gt;I am currently only implementing this on xremap for my linux desktop for now. If I find it useful, I will add it
to my karabiner config too.&lt;&#x2F;p&gt;
&lt;p&gt;One note is that because I am using the dvorak keyboard layout and then doing the remaping for the symbol layer on
xremap, the config is a bit weird. If you are interested in something similar, you would have to adapt it to your
specific setup.&lt;&#x2F;p&gt;
&lt;p&gt;It might actually be simpler if I did the dvorak keyboard remapping directly in xremap. Not sure of what the side effects
of that would be, but something to experiment with possibly.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;tree&#x2F;master&#x2F;xremap&quot;&gt;xremap config&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>NFS vs SMB transfer speed on a Linux server</title>
          <pubDate>Tue, 08 Oct 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/nfs-vs-smb-transfer-speed-on-a-linux-server/</link>
          <guid>https://www.jonfk.ca/blog/nfs-vs-smb-transfer-speed-on-a-linux-server/</guid>
          <description xml:base="https://www.jonfk.ca/blog/nfs-vs-smb-transfer-speed-on-a-linux-server/">&lt;p&gt;I also posted the full data in a git repo at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nfs-smb-benchmarking-experiment&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nfs-smb-benchmarking-experiment&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;nfs-vs-smb-transfer-speed-on-a-linux-server&quot;&gt;NFS vs SMB transfer speed on a Linux server&lt;&#x2F;h1&gt;
&lt;p&gt;I have been setting up my homelab server, migrating it from baremetal to Proxmox, a virtualization solution. The
whole process is probably a blog post for another time. But while doing the migration, I got bitten by the improvement
bug, where I started wanting to improve various features I had decided on a good enough solution at the time. There
are probably several blog posts worth of things to discuss just on what I did, but let&#x27;s not get side tracked.&lt;&#x2F;p&gt;
&lt;p&gt;One of the things was using SMB vs NFS on Linux. I have a Synology (ahem, actually two) NAS which I use for storing
large amounts of data that are not actively used on the system that generates it or the data might need to be shared
through the filesystem with multiple devices. Some examples are backups, photos, videos, entertainment media, etc. In
this particular case, it was a backup share from my Synology DS218+ to this new VM I was setting up.&lt;&#x2F;p&gt;
&lt;p&gt;So in the past, I had tried to use an NFS mount from my Linux server to the NAS but kept hitting issues with permissions
and user mappings that I didn&#x27;t care to resolve at the time. The setup for SMB was simple enough that I decided to
just go with it at the time. But the original reason I wanted to try NFS was that it is supposed to be more resource
efficient and theoretically faster than SMB. So I finally decided to barrel through all the issues and got it working.
Now with 2 working mounting solutions, I wanted to see if I could benchmark the difference.&lt;&#x2F;p&gt;
&lt;p&gt;So here is what I found out. But first, how I ran the tests and some info about the servers.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;server&quot;&gt;Server&lt;&#x2F;h2&gt;
&lt;p&gt;A Synology DS218+ NAS with the following specs:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Model&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Intel Celeron J3355&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Architecture&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;64-bit&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Frequency&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Dual Core 2.0 burst up to 2.5 GHz&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hardware Transcoding Engine&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;H.264 (AVC), H.265 (HEVC), MPEG-2 and VC-1; maximum resolution: 4K (4096 x 2160); maximum frame rate per second (FPS): 30&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;System Memory&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;10 GB DDR3L&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Total Effective Storage&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;15.7TB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Drives&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;2 x Seagate IronWolf Pro ST18000NT001 18TB 7200 RPM&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Network Interface&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;1GbE&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;nfs-config&quot;&gt;NFS Config&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Maximum NFS Protocol&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;NFSv4.1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Read Packet Size&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;8KB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Write Packet Size&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;8KB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;smb-config&quot;&gt;SMB Config&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Maximum SMB protocol&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;SMB3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Minimum SMB protocol&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;SMB1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SMB range&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;SMB1,5MB2,SMB2 and Large MTU,SMB3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Transport encryption mode&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Client defined&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable server signing:&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable Opportunistic Locking&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Enabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable SMB2 lease&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable SMB durable handles (Cross-protocol file locking will be disabled)&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable Local Master Browser&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enable DirSort VFS module&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Veto files&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Allow symbolic links within shared folders&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Enabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Allow symbolic links across shared folders&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;**Disable multiple connections from the same IP address&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Collect debug logs&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Apply default UNIX permissions&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Enabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Do not reserve disk space when creating files&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Disabled&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;client&quot;&gt;Client&lt;&#x2F;h2&gt;
&lt;p&gt;A little confusing since in this case the Linux server is acting as a client to the NAS.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Model&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;AMD Ryzen™ 7 3750H&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Architecture&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;64-bit&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CPU Frequency&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;4 Cores&#x2F;8 Threads (4MB Cache , up to 4.0 GHz)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;System Memory&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;16 GB DDR4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Network Interface&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;2.5GbE&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;mount-configs&quot;&gt;Mount Configs&lt;&#x2F;h3&gt;
&lt;p&gt;The section of my cloud-init config that defines the NFS and SMB mounts.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;#cloud-config
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mounts&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - [ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;192.168.50.55:&#x2F;volume1&#x2F;backups&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;mnt&#x2F;nfs&#x2F;ds218&#x2F;backups&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;nfs4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;rw,noatime,nodiratime,vers=4.1,soft,retrans=2,_netdev,async,rsize=32768,wsize=32768,fsc,cto,acl&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  - [ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;&#x2F;192.168.50.55&#x2F;backups&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;mnt&#x2F;smb&#x2F;ds218&#x2F;backups&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cifs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;username=notmyusername,password=notmypassword,vers=3.1.1,uid=1000,_netdev&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;network&quot;&gt;Network&lt;&#x2F;h2&gt;
&lt;p&gt;The NAS and server are connected wired through a 2.5GbE switch.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;methodology&quot;&gt;Methodology&lt;&#x2F;h1&gt;
&lt;p&gt;For more details on the exact script see &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nfs-smb-benchmarking-experiment&#x2F;blob&#x2F;main&#x2F;fs_bench.sh&quot;&gt;fs_bench.sh&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To test the filesystems speed, I ran the fs_bench.sh script which runs fio with the 5 test types on both mounts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sequential read&lt;&#x2F;li&gt;
&lt;li&gt;Sequential write&lt;&#x2F;li&gt;
&lt;li&gt;Random read&lt;&#x2F;li&gt;
&lt;li&gt;Random write&lt;&#x2F;li&gt;
&lt;li&gt;Mixed random read&#x2F;write&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;results&quot;&gt;Results&lt;&#x2F;h1&gt;
&lt;p&gt;For the raw results see &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nfs-smb-benchmarking-experiment&#x2F;blob&#x2F;main&#x2F;fio_output.txt&quot;&gt;fio_output.txt&lt;&#x2F;a&gt;. Otherwise here a summary created using the
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nfs-smb-benchmarking-experiment&#x2F;blob&#x2F;main&#x2F;analysis.py&quot;&gt;analysis.py&lt;&#x2F;a&gt; script follows in the next section.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;performance-comparison-nfs-vs-smb&quot;&gt;Performance Comparison: nfs vs smb&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;❯ python3 analysis.py
&lt;&#x2F;span&gt;&lt;span&gt;Performance Comparison: NFS vs SMB
&lt;&#x2F;span&gt;&lt;span&gt;==================================
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;SEQUENTIAL_READ:
&lt;&#x2F;span&gt;&lt;span&gt;  IOPS:        NFS: 110.00    SMB: 112.00    Difference: 1.82%
&lt;&#x2F;span&gt;&lt;span&gt;  Bandwidth:   NFS: 111.00 MiB&#x2F;s    SMB: 112.00 MiB&#x2F;s    Difference: 0.90%
&lt;&#x2F;span&gt;&lt;span&gt;  Avg Latency: NFS: 36091.93 µs    SMB: 35625.10 µs    Difference: 1.31%
&lt;&#x2F;span&gt;&lt;span&gt;  P99 Latency: NFS: 42206.00 µs    SMB: 35914.00 µs    Difference: 17.52%
&lt;&#x2F;span&gt;&lt;span&gt;  CPU Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    User:      NFS: 0.03%    SMB: 0.04%    Difference: 0.01%
&lt;&#x2F;span&gt;&lt;span&gt;    System:    NFS: 0.73%    SMB: 0.46%    Difference: -0.27%
&lt;&#x2F;span&gt;&lt;span&gt;    Total:     NFS: 0.76%    SMB: 0.50%    Difference: -0.26%
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;SEQUENTIAL_WRITE:
&lt;&#x2F;span&gt;&lt;span&gt;  IOPS:        NFS: 107.00    SMB: 110.00    Difference: 2.80%
&lt;&#x2F;span&gt;&lt;span&gt;  Bandwidth:   NFS: 108.00 MiB&#x2F;s    SMB: 110.00 MiB&#x2F;s    Difference: 1.85%
&lt;&#x2F;span&gt;&lt;span&gt;  Avg Latency: NFS: 37040.95 µs    SMB: 36137.06 µs    Difference: 2.50%
&lt;&#x2F;span&gt;&lt;span&gt;  P99 Latency: NFS: 57410.00 µs    SMB: 60556.00 µs    Difference: -5.20%
&lt;&#x2F;span&gt;&lt;span&gt;  CPU Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    User:      NFS: 0.11%    SMB: 0.13%    Difference: 0.02%
&lt;&#x2F;span&gt;&lt;span&gt;    System:    NFS: 0.65%    SMB: 1.23%    Difference: 0.58%
&lt;&#x2F;span&gt;&lt;span&gt;    Total:     NFS: 0.76%    SMB: 1.36%    Difference: 0.60%
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;RANDOM_READ:
&lt;&#x2F;span&gt;&lt;span&gt;  IOPS:        NFS: 7334.00    SMB: 5513.00    Difference: -24.83%
&lt;&#x2F;span&gt;&lt;span&gt;  Bandwidth:   NFS: 28.70 MiB&#x2F;s    SMB: 21.50 MiB&#x2F;s    Difference: -25.09%
&lt;&#x2F;span&gt;&lt;span&gt;  Avg Latency: NFS: 541.17 µs    SMB: 721.68 µs    Difference: -25.01%
&lt;&#x2F;span&gt;&lt;span&gt;  P99 Latency: NFS: 1319.00 µs    SMB: 1352.00 µs    Difference: -2.44%
&lt;&#x2F;span&gt;&lt;span&gt;  CPU Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    User:      NFS: 0.80%    SMB: 1.05%    Difference: 0.25%
&lt;&#x2F;span&gt;&lt;span&gt;    System:    NFS: 2.96%    SMB: 2.56%    Difference: -0.40%
&lt;&#x2F;span&gt;&lt;span&gt;    Total:     NFS: 3.76%    SMB: 3.61%    Difference: -0.15%
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;RANDOM_WRITE:
&lt;&#x2F;span&gt;&lt;span&gt;  IOPS:        NFS: 6618.00    SMB: 7691.00    Difference: 16.21%
&lt;&#x2F;span&gt;&lt;span&gt;  Bandwidth:   NFS: 25.90 MiB&#x2F;s    SMB: 30.00 MiB&#x2F;s    Difference: 15.83%
&lt;&#x2F;span&gt;&lt;span&gt;  Avg Latency: NFS: 599.69 µs    SMB: 514.93 µs    Difference: 16.46%
&lt;&#x2F;span&gt;&lt;span&gt;  P99 Latency: NFS: 1532.00 µs    SMB: 2008.00 µs    Difference: -23.71%
&lt;&#x2F;span&gt;&lt;span&gt;  CPU Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    User:      NFS: 0.75%    SMB: 1.33%    Difference: 0.58%
&lt;&#x2F;span&gt;&lt;span&gt;    System:    NFS: 2.94%    SMB: 10.33%    Difference: 7.39%
&lt;&#x2F;span&gt;&lt;span&gt;    Total:     NFS: 3.69%    SMB: 11.66%    Difference: 7.97%
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;MIXED_RANDRW:
&lt;&#x2F;span&gt;&lt;span&gt;  IOPS:        NFS: 3531.00    SMB: 3137.00    Difference: -11.16%
&lt;&#x2F;span&gt;&lt;span&gt;  Bandwidth:   NFS: 13.80 MiB&#x2F;s    SMB: 12.30 MiB&#x2F;s    Difference: -10.87%
&lt;&#x2F;span&gt;&lt;span&gt;  Avg Latency: NFS: 588.18 µs    SMB: 598.77 µs    Difference: -1.77%
&lt;&#x2F;span&gt;&lt;span&gt;  P99 Latency: NFS: 1516.00 µs    SMB: 2376.00 µs    Difference: -36.20%
&lt;&#x2F;span&gt;&lt;span&gt;  CPU Usage:
&lt;&#x2F;span&gt;&lt;span&gt;    User:      NFS: 0.79%    SMB: 1.17%    Difference: 0.38%
&lt;&#x2F;span&gt;&lt;span&gt;    System:    NFS: 3.16%    SMB: 5.37%    Difference: 2.21%
&lt;&#x2F;span&gt;&lt;span&gt;    Total:     NFS: 3.95%    SMB: 6.54%    Difference: 2.59%
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Summary:
&lt;&#x2F;span&gt;&lt;span&gt;  sequential_read:
&lt;&#x2F;span&gt;&lt;span&gt;    Performance: SMB is faster by 1.82%
&lt;&#x2F;span&gt;&lt;span&gt;    CPU Efficiency: SMB uses less CPU by 0.26%
&lt;&#x2F;span&gt;&lt;span&gt;  sequential_write:
&lt;&#x2F;span&gt;&lt;span&gt;    Performance: SMB is faster by 2.80%
&lt;&#x2F;span&gt;&lt;span&gt;    CPU Efficiency: NFS uses less CPU by 0.60%
&lt;&#x2F;span&gt;&lt;span&gt;  random_read:
&lt;&#x2F;span&gt;&lt;span&gt;    Performance: NFS is faster by 33.03%
&lt;&#x2F;span&gt;&lt;span&gt;    CPU Efficiency: SMB uses less CPU by 0.15%
&lt;&#x2F;span&gt;&lt;span&gt;  random_write:
&lt;&#x2F;span&gt;&lt;span&gt;    Performance: SMB is faster by 16.21%
&lt;&#x2F;span&gt;&lt;span&gt;    CPU Efficiency: NFS uses less CPU by 7.97%
&lt;&#x2F;span&gt;&lt;span&gt;  mixed_randrw:
&lt;&#x2F;span&gt;&lt;span&gt;    Performance: NFS is faster by 12.56%
&lt;&#x2F;span&gt;&lt;span&gt;    CPU Efficiency: NFS uses less CPU by 2.59%
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;The difference ended up being somewhat negligible for sequential reads and writes. With both NFS and SMB getting pretty
close to the theoretical limit of 125MB&#x2F;s of the 1GbE link.&lt;&#x2F;p&gt;
&lt;p&gt;The difference in random read was fairly large of about 30% in favor of NFS. While the random write speed was in favor
of SMB with it being about 15% faster. And in a mixed workload of random reads and writes, NFS wins with the lightest
of margins of about 12%. In these, it does seem like NFS is slightly more CPU efficient but not really by much.&lt;&#x2F;p&gt;
&lt;p&gt;Was all this worth it? Not really but at least now I know that id doesn&#x27;t make much of a difference at least with my
current setup. I have been planning on upgrading the NAS and network to 2.5GbE, I wonder if that would make a difference.
At least now that I had an easy quick setup to test this, I can do it again once I upgrade.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My Personal Self-Hosted Infrastructure Part 1</title>
          <pubDate>Mon, 09 Sep 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/my-personal-self-hosted-infrastructure-part-1/</link>
          <guid>https://www.jonfk.ca/blog/my-personal-self-hosted-infrastructure-part-1/</guid>
          <description xml:base="https://www.jonfk.ca/blog/my-personal-self-hosted-infrastructure-part-1/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;Over the past decade, there&#x27;s been a noticeable shift in how software is delivered and consumed. Many applications that were once standalone programs are now served over the web. This trend towards web-based services and software-as-a-service (SaaS) models has its merits, but it also opens up opportunities for individuals to take control of their digital lives through self-hosting. In this post, I&#x27;ll explore the reasons for this shift, the benefits of self-hosting, and share my personal journey into building a self-hosted infrastructure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-web-services&quot;&gt;Why Web Services?&lt;&#x2F;h2&gt;
&lt;p&gt;The move towards web-based services has several advantages:&lt;&#x2F;p&gt;
&lt;p&gt;Personal Benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Access to apps and data from anywhere&lt;&#x2F;li&gt;
&lt;li&gt;Easier collaboration on shared projects&lt;&#x2F;li&gt;
&lt;li&gt;Simple sharing of content like photo galleries or works-in-progress&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Technical Benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Access to software through APIs&lt;&#x2F;li&gt;
&lt;li&gt;Ability to schedule tasks remotely&lt;&#x2F;li&gt;
&lt;li&gt;Offloading processing to potentially more powerful servers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While these benefits could theoretically be achieved through desktop applications or command-line interfaces, web services with HTTP JSON APIs provide one of the simplest ways to reap these advantages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-case-for-self-hosting&quot;&gt;The Case for Self-Hosting&lt;&#x2F;h2&gt;
&lt;p&gt;Despite the convenience of services like Google Workspace or Dropbox, there are compelling reasons to consider self-hosting:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Data Control: You maintain ownership and control over your data.&lt;&#x2F;li&gt;
&lt;li&gt;Customization: You can modify and adapt services to your specific needs.&lt;&#x2F;li&gt;
&lt;li&gt;Privacy: Your data remains on your own infrastructure.&lt;&#x2F;li&gt;
&lt;li&gt;Learning Opportunity: Self-hosting provides hands-on experience with various technologies.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Self-hosting does come with its challenges, but starting small and focusing on useful, frequently-used services can help you build a sustainable personal infrastructure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-it-started-my-self-hosting-journey&quot;&gt;How It Started: My Self-Hosting Journey&lt;&#x2F;h2&gt;
&lt;p&gt;My venture into self-hosting began in November 2017 with the purchase of a Synology DS218+ NAS, equipped with an 8TB WD Red Pro drive. Initially intended for expanded storage and photo backups, I soon discovered its potential as a home server through Docker support.&lt;&#x2F;p&gt;
&lt;p&gt;The first services I installed were:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Plex: For media organization and playback&lt;&#x2F;li&gt;
&lt;li&gt;Transmission: A torrent client&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;These services, which I had previously run locally, now benefited from operating on a dedicated, always-on server.&lt;&#x2F;p&gt;
&lt;p&gt;I started by using the Docker UI provided by Synology, which was sufficient for managing these two services. For remote access, I utilized Synology&#x27;s DDNS service.&lt;&#x2F;p&gt;
&lt;p&gt;As I expanded my setup, I learned valuable lessons about storage management and hardware selection:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Understanding RAID: When my original 8TB drive began to fill up, I realized I needed to choose between additional storage (RAID 0) or data redundancy (RAID 1). I opted for RAID 1 to protect my important photos and videos.&lt;&#x2F;li&gt;
&lt;li&gt;Scaling Up: To increase storage capacity, I eventually purchased a larger NAS, the Synology DS1019+ with 5 bays. This experience taught me the importance of anticipating future needs when selecting hardware.&lt;&#x2F;li&gt;
&lt;li&gt;Start Small, Think Big: If I could advise my past self, I&#x27;d recommend thoroughly evaluating needs before investing in hardware. The adage &quot;buy once, cry once&quot; applies well to NAS and server setups. For those unsure about their requirements, starting with a used mini PC can be an excellent way to test use cases before committing to more expensive hardware.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Self-hosting offers a powerful way to take control of your digital life, but it requires careful planning and a willingness to learn. As my journey shows, starting small and gradually expanding can lead to a robust and personalized infrastructure that meets your specific needs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technical-terms-glossary&quot;&gt;Technical Terms Glossary&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RAID (Redundant Array of Independent Disks)&lt;&#x2F;strong&gt;: A storage technology that combines multiple disk drive components into a logical unit for data redundancy and performance improvement. Common RAID levels include:
&lt;ul&gt;
&lt;li&gt;RAID 0: Stripes data across drives for improved performance but no redundancy.&lt;&#x2F;li&gt;
&lt;li&gt;RAID 1: Mirrors data on two drives for full redundancy.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DDNS (Dynamic DNS)&lt;&#x2F;strong&gt;: A method of automatically updating a name server in the Domain Name System with the active DHCP address. This is particularly useful for accessing home servers with dynamic IP addresses.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;NAS (Network Attached Storage)&lt;&#x2F;strong&gt;: A file-level computer data storage server connected to a computer network providing data access to heterogeneous network clients.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;&#x2F;strong&gt;: A platform for developing, shipping, and running applications in containers, which are lightweight, standalone, executable packages of software that include everything needed to run an application.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>RFC Processes for Software Development</title>
          <pubDate>Wed, 14 Aug 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/rfc-process-in-software-development/</link>
          <guid>https://www.jonfk.ca/blog/rfc-process-in-software-development/</guid>
          <description xml:base="https://www.jonfk.ca/blog/rfc-process-in-software-development/">&lt;p&gt;Documenting at a high level what you want to achieve before committing to the work is often a good idea. Especially for complex projects or ideas. This allows others to look over the proposed changes, poke holes at it or be made aware of changes that could affect them.&lt;&#x2F;p&gt;
&lt;p&gt;A few ways I have seen this done at different companies are Technical Designs (TDs), Architecture Decision Records (ADRs) and Request for Comments.&lt;&#x2F;p&gt;
&lt;p&gt;I have found that a great way to write these kind of documents is in a format not too dissimilar to IETF RFCs. Those often have the right level of detail and a structure that lends itself well to software ideas. Words such as must, shall, should, etc as defined in &lt;a href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;html&#x2F;rfc2119&quot;&gt;RFC-2119&lt;&#x2F;a&gt; are also useful to communicate the right level of requirement.&lt;&#x2F;p&gt;
&lt;p&gt;For documenting decisions at a project level, such as a tool or individual service, ADRs can be useful where a change can affect the overall project or projects interacting with it. I found that ADRs often break down and don&#x27;t work well when multiple teams and projects are in one Architecture Decision Log (ADL, a collection of ADRs). They work better in high context situations where ADRs can be short and teams have enough understanding of the project to not have too much background details in ADRs.&lt;&#x2F;p&gt;
&lt;p&gt;Depending on your company or situation, you might want to make these documents more or less formal and structured.&lt;&#x2F;p&gt;
&lt;p&gt;One good example of an RFC process used at a private company for which their documents are public is Oxide Computer with their RFD documents. &lt;a href=&quot;https:&#x2F;&#x2F;rfd.shared.oxide.computer&#x2F;&quot;&gt;Requests for Discussion&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Trying to understand how Next.js render server components on updates</title>
          <pubDate>Fri, 31 May 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/nextjs-14-server-components-update/</link>
          <guid>https://www.jonfk.ca/blog/nextjs-14-server-components-update/</guid>
          <description xml:base="https://www.jonfk.ca/blog/nextjs-14-server-components-update/">&lt;p&gt;I was reading the &lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;learn&#x2F;dashboard-app&quot;&gt;Next.js App Router course&lt;&#x2F;a&gt;, mostly for my own front end curiosity but also because I was looking at doing some frontend projects, as one does when you are frontend curious but mostly do backend development. When I got to &lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;learn&#x2F;dashboard-app&#x2F;adding-search-and-pagination&quot;&gt;Chapter 11: Adding Search and Pagination&lt;&#x2F;a&gt;, I was having a hard time really understanding what was happening and how Next was rendering the updates to the page.&lt;&#x2F;p&gt;
&lt;p&gt;First let&#x27;s start with what this part of the Next.js tutorial is about and how it explains it.&lt;&#x2F;p&gt;
&lt;p&gt;You start with a fairly static page.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Pagination &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;pagination&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Search &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;search&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;table&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ CreateInvoice } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;buttons&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ lusitana } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;fonts&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ InvoicesTableSkeleton } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;skeletons&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ Suspense } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;react&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;export default async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;w-full&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;flex w-full items-center justify-between&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;h1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;`${lusitana.className} text-2xl`&lt;&#x2F;span&gt;&lt;span&gt;}&amp;gt;Invoices&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mt-4 flex items-center justify-between gap-2 md:mt-8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Search &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;placeholder&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Search invoices...&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;CreateInvoice &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;*  &amp;lt;Suspense key={query + currentPage} fallback={&amp;lt;InvoicesTableSkeleton &#x2F;&amp;gt;}&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;        &amp;lt;Table query={query} currentPage={currentPage} &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;      &amp;lt;&#x2F;Suspense&amp;gt; *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mt-5 flex w-full justify-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;* &amp;lt;Pagination totalPages={totalPages} &#x2F;&amp;gt; *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The 2 main components we care about are the &lt;code&gt;&amp;lt;Search&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; components.
The &lt;code&gt;&amp;lt;Search&amp;gt;&lt;&#x2F;code&gt; component is the input that will take a query and somehow update the results in the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt;. The tutorial explains that the best way to handle this is through using the page&#x27;s url query params with &lt;code&gt;useSearchParams&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;Search&lt;&#x2F;code&gt; component then looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;use client&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ MagnifyingGlassIcon } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@heroicons&#x2F;react&#x2F;24&#x2F;outline&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ useSearchParams, usePathname, useRouter } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;next&#x2F;navigation&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;Search&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;searchParams &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;useSearchParams&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;pathname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;usePathname&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ replace } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;useRouter&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;handleSearch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;term&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;params &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;URLSearchParams(searchParams);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(term) {
&lt;&#x2F;span&gt;&lt;span&gt;      params.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;query&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, term);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      params.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;query&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;`${pathname}?${params.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;()}`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F;...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that part of the component was elided. See the full component in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nextjs-14-server-components-updates-post&#x2F;blob&#x2F;main&#x2F;app&#x2F;ui&#x2F;search.tsx&quot;&gt;Git repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we need to wire the query params that were updated by our &lt;code&gt;Search&lt;&#x2F;code&gt; component into the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component.&lt;&#x2F;p&gt;
&lt;p&gt;The page will now look like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Pagination &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;pagination&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Search &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;search&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;table&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ CreateInvoice } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;invoices&#x2F;buttons&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ lusitana } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;fonts&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ Suspense } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;react&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ InvoicesTableSkeleton } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;ui&#x2F;skeletons&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;export default async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;searchParams&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  searchParams&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;query &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;searchParams?.query &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;currentPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Number&lt;&#x2F;span&gt;&lt;span&gt;(searchParams?.page) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;w-full&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;flex w-full items-center justify-between&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;h1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;`${lusitana.className} text-2xl`&lt;&#x2F;span&gt;&lt;span&gt;}&amp;gt;Invoices&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mt-4 flex items-center justify-between gap-2 md:mt-8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Search &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;placeholder&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Search invoices...&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;CreateInvoice &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Suspense &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{query &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;currentPage} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;InvoicesTableSkeleton &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;}&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{query} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;currentPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{currentPage} &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Suspense&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mt-5 flex w-full justify-center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;* &amp;lt;Pagination totalPages={totalPages} &#x2F;&amp;gt; *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, whenever the &lt;code&gt;&amp;lt;Search&amp;gt;&lt;&#x2F;code&gt; component updates the query params with the search query entered, the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; will have the search query passed into it&#x27;s props so that it can show the right results.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, let&#x27;s take a look at what&#x27;s happening in the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;export default async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;InvoicesTable&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;currentPage&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  currentPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;invoices &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;fetchFilteredInvoices&lt;&#x2F;span&gt;&lt;span&gt;(query, currentPage);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This shows that the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component calls an async function &lt;code&gt;fetchFilteredInvoices&lt;&#x2F;code&gt; with the query and currentPage obtained from the Props of the component. This async function could actually fetch the invoices from any data source. In the Next.js tutorial, it&#x27;s from a database covered in a previous chapter.&lt;&#x2F;p&gt;
&lt;p&gt;The tutorial for this chapter actually continues and covers a few more things: input debouncing (which which delays triggering any change until what is triggering the change stops, in this case it&#x27;s the user typing) and pagination. But for our purposes, we can stop here and summarize what we and the tutorial covers and see we can understand what happened.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;quick-summary&quot;&gt;Quick Summary&lt;&#x2F;h1&gt;
&lt;p&gt;Our page has 2 components, a &lt;code&gt;&amp;lt;Search&amp;gt;&lt;&#x2F;code&gt; and a &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component. These 2 components interact with each other through the url query parameters of the page.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;Search&amp;gt;&lt;&#x2F;code&gt; component takes user input of the search query and updates the query parameter of the page with that search query. e.g. the user enters &lt;code&gt;Lee&lt;&#x2F;code&gt; the url changes to &lt;code&gt;&#x2F;dashboard&#x2F;invoices?query=Lee&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component takes the query parameters as it&#x27;s Props and renders the right data.&lt;&#x2F;p&gt;
&lt;p&gt;When the user types a new query into the search box, the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component updates the data.&lt;&#x2F;p&gt;
&lt;p&gt;But how is this all happening?
Does the page refresh when the query parameters are updated?
How does the table receive the updated data based on the search terms?&lt;&#x2F;p&gt;
&lt;p&gt;Maybe I am particularly thick or I might have missed something while reading this tutorial sleepy right before bed. But I had all these questions popping up in my head while reading this Chapter.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;learn&#x2F;dashboard-app&#x2F;streaming&quot;&gt;Chapter 9: Streaming&lt;&#x2F;a&gt; does discuss how you can chunks of a page by loading partial content initially and then streaming in the rest of the content is chunks. This uses React &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;&#x2F;code&gt; feature or the Next.js &lt;code&gt;loading.tsx&lt;&#x2F;code&gt; feature. Maybe Next.js is streaming in the data to the component?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;learn&#x2F;dashboard-app&#x2F;partial-prerendering&quot;&gt;Chapter 10: Partial Prerendering&lt;&#x2F;a&gt; covers how a page can be both static and dynamic. It describes the feature at a high level but I am not entirely sure if this is related to what I am missing.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;let-s-see-how-it-works&quot;&gt;Let&#x27;s see how it works&lt;&#x2F;h1&gt;
&lt;p&gt;I have often found that the best way to understand how something works is to build and poke at what you want to understand. In this case, since this is a web thing, we can easily poke at it using dev tools. So let&#x27;s do it.&lt;&#x2F;p&gt;
&lt;p&gt;We can reuse most of the code from the tutorial, except for the &lt;code&gt;fetchFilteredInvoices&lt;&#x2F;code&gt; function which uses a database and was setup in &lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;learn&#x2F;dashboard-app&#x2F;setting-up-your-database&quot;&gt;Chapter 6 and 7&lt;&#x2F;a&gt;. Instead we will mock this and use the data provided in the tutorial to populate the database.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ customers, invoices } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;#39;@&#x2F;app&#x2F;lib&#x2F;placeholder-data&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;FETCH_SIMULATED_DELAY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; delay in milliseconds
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;fetchFilteredInvoices&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;currentPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;{
&lt;&#x2F;span&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  image_url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  customer_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  amount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;}[]&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return new &lt;&#x2F;span&gt;&lt;span&gt;Promise((&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Combine invoices with customer details
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;detailedInvoices &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;invoices.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;invoice &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;customer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;customers.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;c &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;c.id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span&gt;invoice.customer_id);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;invoice,
&lt;&#x2F;span&gt;&lt;span&gt;          name: customer?.name,
&lt;&#x2F;span&gt;&lt;span&gt;          email: customer?.email,
&lt;&#x2F;span&gt;&lt;span&gt;          image_url: customer?.image_url,
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Simulate filtering based on the query
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;filteredInvoices &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;detailedInvoices.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;invoice &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        invoice.name?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Simulate pagination
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;pageSize &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;startIndex &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(currentPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;pageSize;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;paginatedInvoices &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;filteredInvoices.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(startIndex, startIndex &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;pageSize);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(paginatedInvoices);
&lt;&#x2F;span&gt;&lt;span&gt;    }, FETCH_SIMULATED_DELAY); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Simulate an x second delay
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For good measure, I also added a delay to the &lt;code&gt;fetchFilteredInvoices&lt;&#x2F;code&gt; function to simulate calling an external data source.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s run this example and see what&#x27;s happening.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; npm run dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;![first_page_load] Insert screenshot here&lt;&#x2F;p&gt;
&lt;p&gt;On first page load everything is as expected. The initial request is a call to &lt;code&gt;&#x2F;dashboard&#x2F;invoices&lt;&#x2F;code&gt; which gets an html response. This initial request is followed by several various js requests to load the script to bootstrap the client side application.&lt;&#x2F;p&gt;
&lt;p&gt;But what happens when we enter a search query?&lt;&#x2F;p&gt;
&lt;p&gt;![search_query_partial_render] Insert screenshot&lt;&#x2F;p&gt;
&lt;p&gt;We can see that the application does 2 requests. We can finally see what they are doing by inspecting them. What we see are that the requests on &lt;code&gt;&#x2F;dashboard&#x2F;invoices&lt;&#x2F;code&gt; with specific headers and query parameters, return a react tree response. This react tree is the part of the page that were updated. The first request is the initial render of the Skeleton Table in the &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;&#x2F;code&gt; tags while the &lt;code&gt;fetchFilteredInvoices&lt;&#x2F;code&gt; loaded the data to be rendered. The second request is then the &lt;code&gt;&amp;lt;Table&amp;gt;&lt;&#x2F;code&gt; component rendered with the loaded data.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; 1. First Request
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;dashboard&#x2F;invoices?query=Lee&amp;amp;_rsc=14zw6&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;credentials&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;include&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;headers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;User-Agent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Mozilla&#x2F;5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko&#x2F;20100101 Firefox&#x2F;126.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accept&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;*&#x2F;*&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accept-Language&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;en-US,en;q=0.5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RSC&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Next-Router-State-Tree&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;%5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%22children%22%3A%5B%22invoices%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%5D%7D%5D%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Next-Router-Prefetch&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Next-Url&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;dashboard&#x2F;invoices&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Dest&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;empty&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Mode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cors&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Site&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;same-origin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Priority&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;u=4&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;referrer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;dashboard&#x2F;invoices&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;method&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;GET&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cors&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; 2. Second Request
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;dashboard&#x2F;invoices?query=Lee&amp;amp;_rsc=181do&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;credentials&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;include&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;headers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;User-Agent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Mozilla&#x2F;5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko&#x2F;20100101 Firefox&#x2F;126.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accept&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;*&#x2F;*&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accept-Language&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;en-US,en;q=0.5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RSC&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Next-Router-State-Tree&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;%5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%22children%22%3A%5B%22invoices%22%2C%7B%22children%22%3A%5B%22__PAGE__%3F%7B%5C%22query%5C%22%3A%5C%22Lee%5C%22%7D%22%2C%7B%7D%2Cnull%2C%22refetch%22%5D%7D%5D%7D%5D%7D%5D&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Next-Url&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;dashboard&#x2F;invoices&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Dest&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;empty&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Mode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cors&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sec-Fetch-Site&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;same-origin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Priority&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;u=4&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;referrer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;dashboard&#x2F;invoices&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;method&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;GET&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;mode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cors&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;0:[&amp;quot;development&amp;quot;,[[&amp;quot;children&amp;quot;,&amp;quot;dashboard&amp;quot;,&amp;quot;children&amp;quot;,&amp;quot;invoices&amp;quot;,&amp;quot;children&amp;quot;,&amp;quot;__PAGE__?{\&amp;quot;query\&amp;quot;:\&amp;quot;Lee\&amp;quot;}&amp;quot;,[&amp;quot;__PAGE__?{\&amp;quot;query\&amp;quot;:\&amp;quot;Lee\&amp;quot;}&amp;quot;,{}],null,null]]]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;0:[&amp;quot;development&amp;quot;,[[&amp;quot;children&amp;quot;,&amp;quot;dashboard&amp;quot;,&amp;quot;children&amp;quot;,&amp;quot;invoices&amp;quot;,&amp;quot;children&amp;quot;,&amp;quot;__PAGE__?{\&amp;quot;query\&amp;quot;:\&amp;quot;Lee\&amp;quot;}&amp;quot;,[&amp;quot;__PAGE__?{\&amp;quot;query\&amp;quot;:\&amp;quot;Lee\&amp;quot;}&amp;quot;,{}],&amp;quot;$L1&amp;quot;,[null,&amp;quot;$L2&amp;quot;]]]]
&lt;&#x2F;span&gt;&lt;span&gt;1:[&amp;quot;$L3&amp;quot;,&amp;quot;$L4&amp;quot;,null]
&lt;&#x2F;span&gt;&lt;span&gt;2:[[&amp;quot;$&amp;quot;,&amp;quot;meta&amp;quot;,&amp;quot;0&amp;quot;,{&amp;quot;name&amp;quot;:&amp;quot;viewport&amp;quot;,&amp;quot;content&amp;quot;:&amp;quot;width=device-width, initial-scale=1&amp;quot;}],[&amp;quot;$&amp;quot;,&amp;quot;meta&amp;quot;,&amp;quot;1&amp;quot;,{&amp;quot;charSet&amp;quot;:&amp;quot;utf-8&amp;quot;}],[&amp;quot;$&amp;quot;,&amp;quot;meta&amp;quot;,&amp;quot;2&amp;quot;,{&amp;quot;name&amp;quot;:&amp;quot;next-size-adjust&amp;quot;}]]
&lt;&#x2F;span&gt;&lt;span&gt;5:I[&amp;quot;(app-pages-browser)&#x2F;.&#x2F;app&#x2F;ui&#x2F;search.tsx&amp;quot;,[&amp;quot;app&#x2F;dashboard&#x2F;invoices&#x2F;page&amp;quot;,&amp;quot;static&#x2F;chunks&#x2F;app&#x2F;dashboard&#x2F;invoices&#x2F;page.js&amp;quot;],&amp;quot;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;6:&amp;quot;$Sreact.suspense&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;4:[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;w-full&amp;quot;,&amp;quot;children&amp;quot;:[[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;flex w-full items-center justify-between&amp;quot;,&amp;quot;children&amp;quot;:[&amp;quot;$&amp;quot;,&amp;quot;h1&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;__className_712214 text-2xl&amp;quot;,&amp;quot;children&amp;quot;:&amp;quot;Invoices&amp;quot;}]}],[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;mt-4 flex items-center justify-between gap-2 md:mt-8&amp;quot;,&amp;quot;children&amp;quot;:[&amp;quot;$&amp;quot;,&amp;quot;$L5&amp;quot;,null,{&amp;quot;placeholder&amp;quot;:&amp;quot;Search invoices...&amp;quot;}]}],[&amp;quot;$&amp;quot;,&amp;quot;$6&amp;quot;,&amp;quot;Lee1&amp;quot;,{&amp;quot;fallback&amp;quot;:[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;mt-6 flow-root&amp;quot;,&amp;quot;children&amp;quot;:[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;inline-block min-w-full align-middle&amp;quot;,&amp;quot;children&amp;quot;:[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;rounded-lg bg-gray-50 p-2 md:pt-0&amp;quot;,&amp;quot;children&amp;quot;:[[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;md:hidden&amp;quot;,&amp;quot;children&amp;quot;:[[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;mb-2 w-full rounded-md bg-white p-4&amp;quot;,&amp;quot;children&amp;quot;:[[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;flex items-center justify-between border-b border-gray-100 pb-8&amp;quot;,&amp;quot;children&amp;quot;:[[&amp;quot;$&amp;quot;,&amp;quot;div&amp;quot;,null,{&amp;quot;className&amp;quot;:&amp;quot;flex items-
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h1&gt;
&lt;p&gt;So we were able to answer some of the questions we had initially. Next.js manages the request lifecycle of the application. It does a full page load initially where it is necessary, but once loaded, replaces only parts of the page that are necessary. No full page reload happens when the query params are updated on the page or when new data is rendered to the page. The data is passed through the page&#x27;s url query parameters mediated through &lt;code&gt;next&#x2F;navigation&lt;&#x2F;code&gt; which manages the updates to the page when necessary.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;mermaid&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-mermaid &quot;&gt;&lt;code class=&quot;language-mermaid&quot; data-lang=&quot;mermaid&quot;&gt;&lt;span&gt;sequenceDiagram
&lt;&#x2F;span&gt;&lt;span&gt;	User -&amp;gt;&amp;gt; UserAgent: Load &#x2F;dashboard&#x2F;invoices
&lt;&#x2F;span&gt;&lt;span&gt;	UserAgent -&amp;gt;&amp;gt; Next.js Server: Request &#x2F;dashboard&#x2F;invoices
&lt;&#x2F;span&gt;&lt;span&gt;	activate Next.js Server
&lt;&#x2F;span&gt;&lt;span&gt;	Next.js Server --&amp;gt;&amp;gt; UserAgent: text&#x2F;html
&lt;&#x2F;span&gt;&lt;span&gt;	deactivate Next.js Server
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	User -&amp;gt;&amp;gt; UserAgent: Enters search query
&lt;&#x2F;span&gt;&lt;span&gt;	UserAgent -&amp;gt;&amp;gt; Next.js Server: Request &#x2F;dashboard&#x2F;invoices?query=...&amp;amp;_rsc=...
&lt;&#x2F;span&gt;&lt;span&gt;	activate Next.js Server
&lt;&#x2F;span&gt;&lt;span&gt;	Note right of Next.js Server: Next.js renders the changes
&lt;&#x2F;span&gt;&lt;span&gt;	Next.js Server --&amp;gt;&amp;gt; UserAgent: text&#x2F;x-component of the changes
&lt;&#x2F;span&gt;&lt;span&gt;	deactivate Next.js Server
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Check out the repo where I tried to create a minimal example of what I looked into here.
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;nextjs-14-server-components-updates-post&quot;&gt;Github link&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Playing with Automerge and text CRDTs</title>
          <pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/playing-with-automerge-and-text-crdts/</link>
          <guid>https://www.jonfk.ca/blog/playing-with-automerge-and-text-crdts/</guid>
          <description xml:base="https://www.jonfk.ca/blog/playing-with-automerge-and-text-crdts/">&lt;p&gt;You will find the code and output from the work described here at the following links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;text-crdt-experiment-automerge-ts&quot;&gt;Github Repo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;jonfk.github.io&#x2F;text-crdt-experiment-automerge-ts&quot;&gt;Web App&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;automerge-a-javascript-crdt-library&quot;&gt;Automerge: a Javascript CRDT library&lt;&#x2F;h2&gt;
&lt;p&gt;I had heard of &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Conflict-free_replicated_data_type&quot;&gt;Conflict-free replicated data types&lt;&#x2F;a&gt;
before but their use crystalized in my mind recently when I found out about the
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;automerge&#x2F;automerge&quot;&gt;Automerge&lt;&#x2F;a&gt; Javascript library. It&#x27;s blurb states that it&#x27;s a JSON-like data
structure that can be modified concurrently and merged automatically. What this means is that it enables the
creation of collaborative applications where multiple users could modify the same structure locally, send their changes
over the network and have their changes merge together even when they made conflicting modifications. It is of course
not perfect since the library cannot know in advance what the right state would be if conflicting modifications happened
on the same property if it doesn&#x27;t actually support it, in these cases, it chooses one of the updates and saves the
other in it&#x27;s conflicts. What&#x27;s great about it&#x27;s conflict resolution, is that when it chooses one resolution, all the
nodes using automerge receiving the conflicting changes will also resolve it in the same consistent way.&lt;&#x2F;p&gt;
&lt;p&gt;One of the data structures I was interested on was the &lt;code&gt;Text&lt;&#x2F;code&gt; data type, which promises to enable text editors to
become collaborative and allow concurrent updates even offline. With that in mind, a lot of use cases started to pop
into my mind, since I have been thinking about a better way of storing my notes recently. It matches several criteria
that could help be useful for my use case, namely, being in Javascript, I could use it to build a web app to sync my notes
in several places and even enable merging conflicting updates when I come back online from different devices.&lt;&#x2F;p&gt;
&lt;p&gt;But I haven&#x27;t been working much with frontend web technologies recently, so I decided to start small and experiment with
the library with a small prototype. This is what I learned while doing that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;react-and-redux&quot;&gt;React and Redux&lt;&#x2F;h2&gt;
&lt;p&gt;Any good frontend project has to start with some kind of rendering library or framework. I could go vanilla js, or choose
one of the many frontend libraries available today. One that seems to have largely taken over the frontend world these
days seem to be React. I used React at a startup several years ago and from a cursory look, the ecosystem seems to have
moved forward quite well, the APIs are largely the same, but the breath of libraries, tooling and documentation has
improved a lot in the intervening years. So I decided to go with a React&#x2F;Redux rendering stack.&lt;&#x2F;p&gt;
&lt;p&gt;This architecture is quite similar to &lt;a href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, which I have been using in some of my side projects.
I should probably write about it sometime, but today isn&#x27;t about Elm. Let&#x27;s just say that Elm is a functional programming
language for frontend development with a strong focus on no runtime errors (at least not ones of the typed kind). Since
it&#x27;s the tech I have been using the most in my side projects where I need a web frontend, that is the starting point for
my comparisons.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing I noticed digging around is how much more breath of libraries and integrations there are for a React
application. By comparison in Elm, I found myself feeling a little isolated by lack of libraries or different tooling
from the rest of the web ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;I also found the improved documentation around React since the last time I used it to be very nice. The React, Redux and
Create React App official websites had a lot of examples and pragmatic use cases to help.&lt;&#x2F;p&gt;
&lt;p&gt;The onboarding process for a new project seems to have been streamlined greatly since I used React. The Create React App
and react-scripts enables you to have a working project from a template and configuration less build pretty quick. You
can of course configure your own bundler but I enjoy being able to start on my project fairly quick and only needing to
configure the bundler once I have a specific need to do that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;typescript&quot;&gt;Typescript&lt;&#x2F;h2&gt;
&lt;p&gt;I am not big fan of Javascript, it&#x27;s dynamic type system, it&#x27;s prototype base inheritance or it&#x27;s sometimes confusing scoping
rules and it&#x27;s many other faults don&#x27;t help when I don&#x27;t use the language often. So seeing typescript coming up in popularity
in the Javascript ecosystem fills me with much joy. Even if Typescript doesn&#x27;t have the strictest type system or one that
covers all cases that can occur in Javascript, it&#x27;s still better than nothing. That is why I wanted to get some familiarity
with it.&lt;&#x2F;p&gt;
&lt;p&gt;After about 15mins on its &lt;a href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;home.html&quot;&gt;documentation&lt;&#x2F;a&gt;, I found myself having enough of a
grasp of the language to start using it in a project. So that&#x27;s encouraging.
&lt;a href=&quot;https:&#x2F;&#x2F;react-redux.js.org&#x2F;using-react-redux&#x2F;static-typing&quot;&gt;React&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;redux.js.org&#x2F;recipes&#x2F;usage-with-typescript&quot;&gt;Redux&lt;&#x2F;a&gt; and
&lt;a href=&quot;https:&#x2F;&#x2F;create-react-app.dev&#x2F;docs&#x2F;getting-started#creating-a-typescript-app&quot;&gt;create-react-app&lt;&#x2F;a&gt; all have some documentation
for using them with typescript, which is also a huge help to get started. I also found that most libraries that I encountered
had typescript types either included with the library or as a &lt;code&gt;@type&#x2F;&lt;&#x2F;code&gt; package.&lt;&#x2F;p&gt;
&lt;p&gt;The only issue I encountered was went I tried to use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;immerjs&#x2F;immer&quot;&gt;immer&lt;&#x2F;a&gt; for my redux reducers. It cause
strange type errors causing the Automerge objects stored in my redux store to not be typed as Automerge types because of the
readonly properties added by immer it seemed. I didn&#x27;t really debug much further and quickly reverted back to plain reducers.
If I were to use it in the future, I would try to debug the issue, but that does give me pause about adding libraries and
their interactions between each other and typescript.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;issues-with-webpack-prod-build&quot;&gt;Issues with Webpack prod build&lt;&#x2F;h2&gt;
&lt;p&gt;The final problem I encountered was at the very end of my experiment. During my whole development, I had been using it&#x27;s
development mode to run my app and it was working fine until I decided to run &lt;code&gt;npm run build&lt;&#x2F;code&gt;. The production build started
to give me undefined errors on a class method I was calling on an Automerge object. Looking strictly at the code was no help
since from the typescript source code, I was calling the method correctly to bind &lt;code&gt;this&lt;&#x2F;code&gt; and it was working correctly on the
development build. It was only after asking for some help from a coworker with much more experience on the front end that
we ended up debugging the issue to the bundled and minified code from webpack. It required disabling source maps and locating
the corresponding code in the transpiled output using judiciously placed unique literal strings that we found that the code
wasn&#x27;t being transpiled to the same output we would expect. The solution we ended up implementing was to add a &lt;code&gt;.bind()&lt;&#x2F;code&gt; which
looked something like this.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;utils&amp;#x2F;automerge.js&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;doc.text.insertAt?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;bind&lt;&#x2F;span&gt;&lt;span&gt;(doc.text)(idx, changedText);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This did the trick but doesn&#x27;t inspire me with a ton of confidence in the tooling in the Javascript ecosystem.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;This has been a bit of an off the cuff post on my experience getting back into frontend development and my experiment with
Typescript and the Automerge CRDT library. Overall, I found that the Javascript ecosystem has a huge breath of libraries
to do almost anything. That Typescript is pretty awesome but the integration with the libraries you want to use might not be
perfect. Bringing libraries to your project can have lots of risks and you should only bring them in if you feel confident
in truly owning those dependencies, and that&#x27;s especially true in an ecosystem with so many libraries.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the goal of the experiment to see if Automerge was a viable data structure for a generic note editor, indicated
to me that although it works amazingly well, it may not be the best match for the potential generic note editor project I had
in mind. The updates to the Text data structure took a bit too long (moderate updates to the text sometimes took over 4s) and
the storage overhead of the data structure was also way too high (3KB of text took 650KB of storage for the data structure
which is about a 200x overhead).&lt;&#x2F;p&gt;
&lt;p&gt;Check out the code in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;text-crdt-experiment-automerge-ts&quot;&gt;Github repo&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;jonfk.github.io&#x2F;text-crdt-experiment-automerge-ts&#x2F;&quot;&gt;web app&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I will keep an eye out for it in the future as it improves. The author is working on improving the performance of the Text
data structure and overall library as these issue indicate.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;https:&#x2F;&#x2F;github.com&#x2F;automerge&#x2F;automerge&#x2F;issues&#x2F;212&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;github.com&#x2F;automerge&#x2F;automerge&#x2F;issues&#x2F;89&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Integrating Ory Hydra OAuth 2.0 with a Rust web service</title>
          <pubDate>Sat, 18 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/integration-a-rust-web-service-with-oauth2/</link>
          <guid>https://www.jonfk.ca/blog/integration-a-rust-web-service-with-oauth2/</guid>
          <description xml:base="https://www.jonfk.ca/blog/integration-a-rust-web-service-with-oauth2/">&lt;p&gt;I have been using Rust quite a bit more over the last year in a variety of use cases and I have been quite satisfied with
the language, libraries and the community. So I think it might be time to write a little bit about how well suited Rust works
in one area that I have worked a lot in, and that is HTTP APIs.&lt;&#x2F;p&gt;
&lt;p&gt;For this post I will be covering using Rust to write a web service that integrates with the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;hydra&quot;&gt;Ory Hydra OAuth 2.0
Server and OpenID Connect Provider&lt;&#x2F;a&gt;. I think that might give a decent idea of the
features offered by Rust and Rust libraries for web development.&lt;&#x2F;p&gt;
&lt;p&gt;Before we start, all the code we will be working on will be pushed to the following &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;hydra-auth-example-rs&quot;&gt;repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What will we be doing in this post:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#what-is-oauth-2-0&quot;&gt;What is OAuth 2.0?&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#when-should-you-used-it&quot;&gt;When should you used it?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#implementing-authorization-with-oauth-2-0&quot;&gt;Implementing authorization with OAuth 2.0&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#hydra-an-openid-certified-oauth-2-0-server-and-openid-connect-provider&quot;&gt;Hydra: An OpenID Certified OAuth 2.0 Server and OpenID Connect Provider&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#integrating-with-hydra&quot;&gt;Integrating with Hydra&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#a-rust-web-service&quot;&gt;A Rust web service&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#hello-world&quot;&gt;Hello World&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#login-and-consent&quot;&gt;Login and consent&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#setting-up-the-hydra-services&quot;&gt;Setting up the Hydra services&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#generating-the-hydra-openapi-client&quot;&gt;Generating the Hydra OpenApi Client&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#updating-our-login-and-consent-implementation&quot;&gt;Updating our login and consent implementation&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#enabling-logging&quot;&gt;Enabling logging&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#testing-our-login-and-consent-integration&quot;&gt;Testing our login and consent integration&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#async-rust&quot;&gt;Async Rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#where-to-go-from-here&quot;&gt;Where to go from here?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#references&quot;&gt;References&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-is-oauth-2-0&quot;&gt;What is OAuth 2.0?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The OAuth 2.0 authorization framework enables a third-party
application to obtain limited access to an HTTP service, either on
behalf of a resource owner by orchestrating an approval interaction
between the resource owner and the HTTP service, or by allowing the
third-party application to obtain access on its own behalf.
From &lt;a href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;rfc6749&quot;&gt;RFC 6749: The OAuth 2.0 Authorization Framework&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Essentially OAuth 2.0 enables applications (servers, mobile apps, SPAs, etc) to obtain access tokens to access
an HTTP service scoped only to authorized actions&#x2F;resources without needing the username and password of the
account it would like to obtain access on behalf of.&lt;&#x2F;p&gt;
&lt;p&gt;As an almost too short to be correct summary of the OAuth 2.0 Authorization Framework, it defines 2 endpoints, the authorization
and the token endpoints. The first is to request authorization with the scopes one would like to receive and the second is to
request tokens. It also defines a set of grant types (authorization code, client credentials, etc...) and flows to follow
for the different grant types. As for crypto, the protocol simply defines that it needs to be over https. So what we will be
doing here won&#x27;t actually be to spec until we put TLS termination into our system.&lt;&#x2F;p&gt;
&lt;p&gt;There are also several extensions to the protocol, the most important one we will be interested in in this post is the
&lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#oauth2-introspection&quot;&gt;Introspection endpoint&lt;&#x2F;a&gt; which defines how resource servers communicate with the authorization server to verify access
tokens. Another important one that might be of interested but that we won&#x27;t cover in this post is the &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#oauth2-openid-connect&quot;&gt;OpenID Connect 1.0
specification&lt;&#x2F;a&gt; which is a protocol build over OAuth 2.0.&lt;&#x2F;p&gt;
&lt;p&gt;An example use case where we would use OAuth 2 is when you would like to give access to your youtube account to an application to manage your comments.
You wouldn&#x27;t want that application to also be able to post videos for you or change your profile page. You can do this with OAuth 2.0
by authorizing that application if it has an OAuth 2.0 client with Youtube. That application can go through an OAuth 2.0 flow that
will give it an access token that it can then use to get access to manage your comments.&lt;&#x2F;p&gt;
&lt;p&gt;Although at first glance, the protocol may sound simple, it is anything but, implementing a spec compliant and interoperable version
of the protocol that doesn&#x27;t have security holes, takes some serious work and no one should trust such an implementation without at
least an audit. So how can we use it then if we don&#x27;t have deep pockets and all the time in the world to write one? That&#x27;s what we
will try to answer in this post using an existing and well tested OAuth 2.0 server with a well documented API.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-should-you-used-it&quot;&gt;When should you used it?&lt;&#x2F;h3&gt;
&lt;p&gt;Given the additional complexity that implementing and integration an OAuth 2.0 server adds to a project, why should you use OAuth 2?&lt;&#x2F;p&gt;
&lt;p&gt;The rule of thumb for when you need an OAuth 2.0 implementation, is if you need the ability to authorize third party applications
to access your services. Otherwise, if you only need the ability to authorize users on your services through
first party clients or applications, an encrypted cookie may better serve you.&lt;&#x2F;p&gt;
&lt;p&gt;For more information, check out the following &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#ory-access-authz-patterns&quot;&gt;post describing different types of access control and authorization patterns&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;implementing-authorization-with-oauth-2-0&quot;&gt;Implementing authorization with OAuth 2.0&lt;&#x2F;h3&gt;
&lt;p&gt;Although it is of course possible to implement an OAuth 2.0 server from scratch, it is strongly not recommended to do so. Getting
something wrong with the fundamental building blocks of the security of your system is not to be taken lightly. Another way to
implement an OAuth 2 server is with your language&#x27;s trusted OAuth 2 libraries. Being maintained and kept updated by domain
experts, means that your service will have more eyes to find bugs as more people would be using them and gives your an OAuth
implementation almost for free.&lt;&#x2F;p&gt;
&lt;p&gt;But after having implemented and maintained OAuth2 servers with the Spring OAuth 2 library in Java, I have grown a strong distaste
towards this solution too. The reasons for that could be a post in and of itself, but most of the issues stem from maintenance of such such servers
when they also contain much of the business logic for the access and authorization rules. It doesn&#x27;t mean that this needs to be the case
but it is often the case in my experience when we go this way.&lt;&#x2F;p&gt;
&lt;p&gt;That is why my preference nowadays lean towards the 3rd solution to implementing an authorization server, by using an external blessed
implementation with good integration mechanisms. This forces you to keep your business logic and custom authorization rules seperate
from a pure spec OAuth 2.0 or OpenID Connect server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hydra-an-openid-certified-oauth-2-0-server-and-openid-connect-provider&quot;&gt;Hydra: An OpenID Certified OAuth 2.0 Server and OpenID Connect Provider&lt;&#x2F;h2&gt;
&lt;p&gt;This is where &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;hydra&quot;&gt;Hydra&lt;&#x2F;a&gt; comes in, a hardened, OpenID Certified OAuth 2.0 Server and OpenID Connect Provider
optimized for low-latency, high throughput, and low resource consumption. Instead of implementing an OAuth2 server yourself, having
to verify your implementation complies with the spec and doesn&#x27;t have any security holes, we can use Hydra to provide the OAuth2
server functionality and implement the authentication and authorization logic as separate services.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;integrating-with-hydra&quot;&gt;Integrating with Hydra&lt;&#x2F;h3&gt;
&lt;p&gt;To integrate with the Hydra OAuth2 server, we need to implement a login and consent application. You can review the documentation for
doing so &lt;a href=&quot;https:&#x2F;&#x2F;www.ory.sh&#x2F;docs&#x2F;hydra&#x2F;login-consent-flow&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-rust-web-service&quot;&gt;A Rust web service&lt;&#x2F;h2&gt;
&lt;p&gt;If you have never programmed in rust, I would suggest &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;&quot;&gt;The Rust Book&lt;&#x2F;a&gt;
as the introduction to the language.&lt;&#x2F;p&gt;
&lt;p&gt;First lets start with a rust web service. Let&#x27;s create a rust project.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo new hydra-auth-example-rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now inside of the newly created project directory called &lt;code&gt;hydra-auth-example-rs&lt;&#x2F;code&gt;, you should find 2 things, a src directory with a &lt;code&gt;main.rs&lt;&#x2F;code&gt; file and a
&lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;$ tree
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;├── Cargo.toml
&lt;&#x2F;span&gt;&lt;span&gt;└── src
&lt;&#x2F;span&gt;&lt;span&gt;    └── main.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;1 directory, 2 files
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To do anything we will need some dependencies to get our server working. Add the following below in &lt;code&gt;[dependencies]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;warp &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;git &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;seanmonstar&#x2F;warp.git&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;rev &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;e94309e274872efed34e2a80f1e4553a45963510&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;tokio &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.2.6&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;log &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.4.8&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;env_logger &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.7&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;hello-world&quot;&gt;Hello World&lt;&#x2F;h3&gt;
&lt;p&gt;We can now use these dependencies to provide a basic web service. Let&#x27;s write a basic hello world endpoint as in the
example.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, path, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; GET &#x2F;hello&#x2F;warp =&amp;gt; 200 OK with body &amp;quot;Hello, warp!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hello &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;path!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hello&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;| format!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Hello, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, name));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::serve(hello).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can run this hello world service with the following command in the project directory.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or to create a release build.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;cargo build --release
&lt;&#x2F;span&gt;&lt;span&gt;# and to run this we will find the built binary at
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;target&#x2F;release&#x2F;hydra-auth-example-rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To test this endpoint, you can call it with curl.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;$ curl http:&#x2F;&#x2F;localhost:3000&#x2F;hello&#x2F;jon
&lt;&#x2F;span&gt;&lt;span&gt;Hello, jon!%
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since we will also need login and consent pages for the Hydra integration, let&#x27;s test html templating. For that
we will be using the &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;tera&quot;&gt;Tera templating library&lt;&#x2F;a&gt;. There are many other templating
libraries in rust and you can find a non-exhaustive list &lt;a href=&quot;https:&#x2F;&#x2F;www.arewewebyet.org&#x2F;topics&#x2F;templating&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.
So let&#x27;s add that dependency and use it.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;tera &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;1.0.1&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;tera::{Context, Tera};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, path, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[tokio::main]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; tera &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Tera::default();
&lt;&#x2F;span&gt;&lt;span&gt;    tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;add_raw_template&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hello.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;#&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;title&amp;gt;Hello&amp;lt;&#x2F;title&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;p&amp;gt;Hello, {{ name }}!&amp;lt;&#x2F;p&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;#&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; with_tera &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;warp::any().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;move ||&lt;&#x2F;span&gt;&lt;span&gt; tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hello &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;path!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hello&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(with_tera)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: String, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;tera&lt;&#x2F;span&gt;&lt;span&gt;: Tera| {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Context::new();
&lt;&#x2F;span&gt;&lt;span&gt;            context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;name);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hello.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;context).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            warp::reply::html(body)
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::serve(hello).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)).await;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if we test our hello endpoint again, we should see it returning html instead.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;curl -i http:&#x2F;&#x2F;localhost:3000&#x2F;hello&#x2F;jon
&lt;&#x2F;span&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;content-type: text&#x2F;html; charset=utf-8
&lt;&#x2F;span&gt;&lt;span&gt;content-length: 126
&lt;&#x2F;span&gt;&lt;span&gt;date: Sat, 04 Jan 2020 19:59:03 GMT
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;title&amp;gt;Hello&amp;lt;&#x2F;title&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;p&amp;gt;Hello, jon!&amp;lt;&#x2F;p&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or you could point your browser to http:&#x2F;&#x2F;localhost:3000&#x2F;hello&#x2F;name, to see the html rendered.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You will notice that I am using &lt;code&gt;.unwrap()&lt;&#x2F;code&gt; is several places where the function is returning
a &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;&quot;&gt;Result&lt;&#x2F;a&gt;. I am doing that so that I don&#x27;t have do to error
handling yet. I may write another post about error handling but for now, if you want more
information about error handling in rust, &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch09-02-recoverable-errors-with-result.html&quot;&gt;this section of the book&lt;&#x2F;a&gt;
should help.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;login-and-consent&quot;&gt;Login and consent&lt;&#x2F;h3&gt;
&lt;p&gt;We showed the basic building blocks for creating a web service in the previous section. Let&#x27;s put that to use
and build our integration to our OAuth 2.0 server. We will implement the login and consent pages and their
corresponding form post endpoints.&lt;&#x2F;p&gt;
&lt;p&gt;We will first need to update dependencies to add &lt;a href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;Serde&lt;&#x2F;a&gt; which is the library in
Rust to serialize&#x2F;deserialize to various data formats.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;serde &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;1.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;derive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this let&#x27;s implement the login page and form handler first.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;view;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Will create a view module where we can put our template rendering functions.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;view.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;tera::Tera;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;with_tera&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(Tera,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    warp::any().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;move || &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;tera_templates&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;tera_templates&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Tera {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; login_tpl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;#&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;title&amp;gt;Login&amp;lt;&#x2F;title&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;h1&amp;gt;Login page&amp;lt;&#x2F;h1&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;form action=&amp;quot;&#x2F;login&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;{% if login_challenge %}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;login_challenge&amp;quot; value=&amp;quot;{{login_challenge}}&amp;quot;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;{% else %}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;{% endif %}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;label for=&amp;quot;username&amp;quot;&amp;gt;Username&amp;lt;&#x2F;label&amp;gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;username&amp;quot; name=&amp;quot;username&amp;quot; autofocus=&amp;quot;autofocus&amp;quot;&#x2F;&amp;gt; &amp;lt;br&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;label for=&amp;quot;password&amp;quot;&amp;gt;Password&amp;lt;&#x2F;label&amp;gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;input type=&amp;quot;password&amp;quot; id=&amp;quot;password&amp;quot; name=&amp;quot;password&amp;quot;&#x2F;&amp;gt; &amp;lt;br&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Log in&amp;quot;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;form&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;#&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; tera &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Tera::default();
&lt;&#x2F;span&gt;&lt;span&gt;    tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;add_raw_templates&lt;&#x2F;span&gt;&lt;span&gt;(vec![(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, login_tpl)])
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    tera
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now for the actual login page using the rendering template we just created in the
&lt;code&gt;view&lt;&#x2F;code&gt; module.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;serde::{Deserialize, Serialize};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;tera::{Context, Tera};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;view;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[tokio::main]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; routes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;auth_routes&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    warp::serve(routes).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)).await;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;auth_routes&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::reply::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    warp::path(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;login_page&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_page&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;LoginQueryParams {
&lt;&#x2F;span&gt;&lt;span&gt;        login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::get()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(warp::query::query())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(view::with_tera())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;move |&lt;&#x2F;span&gt;&lt;span&gt;query_params: LoginQueryParams, tera: Tera&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Context::new();
&lt;&#x2F;span&gt;&lt;span&gt;            context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login_challenge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;query_params.login_challenge);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;context).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            warp::reply::html(body)
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we submit the login form from the login page, we should receive a 404. To fix that
we need to implement the form post handling endpoint.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;serde::{Deserialize, Serialize};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;tera::{Context, Tera};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;view;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;auth_routes&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::reply::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    warp::path(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;login_page&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;accept_login&lt;&#x2F;span&gt;&lt;span&gt;()))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;accept_login&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;FormBody {
&lt;&#x2F;span&gt;&lt;span&gt;        login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        username: String,
&lt;&#x2F;span&gt;&lt;span&gt;        password: String,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::post()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            warp::body::content_length_limit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1024 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(warp::body::form())
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;form_body&lt;&#x2F;span&gt;&lt;span&gt;: FormBody| warp::reply::json(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;form_body)),
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We now have a working login page and login handler which returns the form body back
as json. So if you try to login with the username, &lt;code&gt;&quot;username&quot;&lt;&#x2F;code&gt;, and password,
&lt;code&gt;&quot;password&quot;&lt;&#x2F;code&gt;, we should receive the following.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;http:&amp;#x2F;&amp;#x2F;localhost:3000&amp;#x2F;login&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login_challenge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;null
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Following the same pattern, you can add the consent page and post endpoint.&lt;&#x2F;p&gt;
&lt;p&gt;You can see what you should end up with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;hydra-auth-example-rs&#x2F;tree&#x2F;1-login-and-consent&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-the-hydra-services&quot;&gt;Setting up the Hydra services&lt;&#x2F;h3&gt;
&lt;p&gt;To be able to test our integration to hydra we will need to run it locally and to do that, the easiest way
I found is to run hydra with their &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;hydra&#x2F;blob&#x2F;v1.2.0&#x2F;quickstart-postgres.yml&quot;&gt;docker-compose&lt;&#x2F;a&gt; file.
We will then make a few modifications to it. We could run hydra with an in-memory database, but with a local postgres database
we will be able to see how the api calls we will be doing to the hydra instance affect its data store.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;docker-compose.yml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;3.3&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;postgres&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;postgres:latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;5432:5432&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;POSTGRES_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;password
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;volumes&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;${PWD}&#x2F;initdb.sh:&#x2F;docker-entrypoint-initdb.d&#x2F;init-user-db.sh
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;hydra&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;depends_on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;hydra-migrate
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;oryd&#x2F;hydra:latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;4444:4444&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# Public port
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;4445:4445&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# Admin port
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;5555:5555&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# Port for hydra token user
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;serve all --dangerous-force-http
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# https:&#x2F;&#x2F;www.ory.sh&#x2F;docs&#x2F;hydra&#x2F;configuration
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# - LOG_LEVEL=debug
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;URLS_SELF_ISSUER=http:&#x2F;&#x2F;localhost:4444
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;URLS_CONSENT=http:&#x2F;&#x2F;localhost:3000&#x2F;consent
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;URLS_LOGIN=http:&#x2F;&#x2F;localhost:3000&#x2F;login
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;DSN=postgres:&#x2F;&#x2F;hydra:hello@postgres:5432&#x2F;hydra?sslmode=disable
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;SECRETS_SYSTEM=youReallyNeedToChangeThis
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;SECRETS_COOKIE=youReallyNeedToChangeThisToo
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;OAUTH2_EXPOSE_INTERNAL_ERRORS=1
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;OIDC_SUBJECT_TYPE_PAIRWISE_SALT=youReallyNeedToChangeThisToo2
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;restart&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;unless-stopped
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;hydra-migrate&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;depends_on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;postgres
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;oryd&#x2F;hydra:latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;DSN=postgres:&#x2F;&#x2F;hydra:hello@postgres:5432&#x2F;hydra?sslmode=disable&amp;amp;max_conns=20&amp;amp;max_idle_conns=4
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;migrate sql -e --yes
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;restart&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;on-failure
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;code-title&quot;&gt;initdb.sh&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;#!&#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-e
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;psql&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; -v&lt;&#x2F;span&gt;&lt;span&gt; ON_ERROR_STOP=1&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; --username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;$POSTGRES_USER&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; --dbname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;$POSTGRES_DB&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;lt;&amp;lt;-EOSQL
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    CREATE USER hydra WITH PASSWORD &amp;#39;hello&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    CREATE USER auth WITH PASSWORD &amp;#39;authpassword&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    CREATE DATABASE hydra;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    CREATE DATABASE auth;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    GRANT ALL PRIVILEGES ON DATABASE hydra TO hydra;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;    GRANT ALL PRIVILEGES ON DATABASE auth TO auth;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;EOSQL
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main difference between this &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt; file and the one provided by in the Hydra repository, is the
&lt;code&gt;initdb.sh&lt;&#x2F;code&gt; script that creates 2 databases and the corresponding user and assigns that user to it&#x27;s database only.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generating-the-hydra-openapi-client&quot;&gt;Generating the Hydra OpenApi Client&lt;&#x2F;h3&gt;
&lt;p&gt;The Hydra server provides &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#ory-rest-api-docs&quot;&gt;public and admin REST APIs&lt;&#x2F;a&gt; to interface with it and allows us to implement the integration.
They also provide sdks, for Go and Javascript, but since we are using Rust we will need to implement our own. Luckily
they also provide &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;hydra&#x2F;blob&#x2F;v1.2.0&#x2F;docs&#x2F;api.swagger.json&quot;&gt;OpenAPI specs&lt;&#x2F;a&gt; and the OpenAPI
generator implements a Rust client generator. The code quality isn&#x27;t great but that should be enough to get us started
with our integration and we can always improve it later by implementing our own client using the generated models.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the command to generate our rust OpenAPI client.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;openapi-generator-cli generate -i https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;ory&#x2F;hydra&#x2F;v1.1.1&#x2F;docs&#x2F;api.swagger.json --package-name hydra --library reqwest -g rust -o &#x2F;local&#x2F;hydra
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or even better, instead of installing the &lt;code&gt;openapi-generator-cli&lt;&#x2F;code&gt;, we can use the docker image to do the same thing and
let&#x27;s put it inside a bash script to be able to reuse it if necessary and remember next time we need to regenerate.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;generate-hydra-api.sh&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;#!&#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-o&lt;&#x2F;span&gt;&lt;span&gt; errexit
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-o&lt;&#x2F;span&gt;&lt;span&gt; pipefail
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-o&lt;&#x2F;span&gt;&lt;span&gt; nounset
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-x
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;docker run&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; --rm -v &lt;&#x2F;span&gt;&lt;span&gt;${PWD}:&#x2F;local openapitools&#x2F;openapi-generator-cli:latest generate&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;ory&#x2F;hydra&#x2F;v1.1.1&#x2F;docs&#x2F;api.swagger.json&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; --package-name&lt;&#x2F;span&gt;&lt;span&gt; hydra&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; --library&lt;&#x2F;span&gt;&lt;span&gt; reqwest&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; -g&lt;&#x2F;span&gt;&lt;span&gt; rust&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;local&#x2F;hydra
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we have 2 crates of code, one that we wrote and the other that we generated. We will need to make some space
where this new crate can live and compile the together. Cargo provides the workspace functionality for handling
multiple crates in the same repository. To do that, we will move the code we have written so far into a sub
directory. Let&#x27;s call that the &lt;code&gt;auth&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;mkdir auth
&lt;&#x2F;span&gt;&lt;span&gt;mv Cargo.toml auth
&lt;&#x2F;span&gt;&lt;span&gt;mv src auth
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Change the package name in the &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; to auth to match the directory name.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[package]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;auth&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;# &amp;lt;--- name to change
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.1.0&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;authors &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Jonathan Fok Kan &amp;lt;jfokkan@gmail.com&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;edition &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;2018&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then create a &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; file with the 2 sub crates we now have.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[workspace]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;members &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;auth&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hydra&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lets compile everything and see if the new generated crate works.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;cargo clean
&lt;&#x2F;span&gt;&lt;span&gt;cargo build
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Oh Ho, something&#x27;s not working. We are getting a compile error.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;error[E0308]: mismatched types
&lt;&#x2F;span&gt;&lt;span&gt;   --&amp;gt; hydra&#x2F;src&#x2F;apis&#x2F;admin_api.rs:542:16
&lt;&#x2F;span&gt;&lt;span&gt;    |
&lt;&#x2F;span&gt;&lt;span&gt;542 |         if let Some(ref s) = client {
&lt;&#x2F;span&gt;&lt;span&gt;    |                ^^^^^^^^^^^   ------ this match expression has type `reqwest::Client`
&lt;&#x2F;span&gt;&lt;span&gt;    |                |
&lt;&#x2F;span&gt;&lt;span&gt;    |                expected struct `reqwest::Client`, found enum `std::option::Option`
&lt;&#x2F;span&gt;&lt;span&gt;    |
&lt;&#x2F;span&gt;&lt;span&gt;    = note: expected type `reqwest::Client`
&lt;&#x2F;span&gt;&lt;span&gt;               found type `std::option::Option&amp;lt;_&amp;gt;`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;error: aborting due to previous error
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;For more information about this error, try `rustc --explain E0308`.
&lt;&#x2F;span&gt;&lt;span&gt;error: could not compile `hydra`.
&lt;&#x2F;span&gt;&lt;span&gt;warning: build failed, waiting for other jobs to finish...
&lt;&#x2F;span&gt;&lt;span&gt;error: build failed
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s check out the file and see what&#x27;s wrong. The rust compiler is helpful enough
to give us the exact location of the error. Well the error looks pretty simple, the
&lt;code&gt;client&lt;&#x2F;code&gt; created within the function body at &lt;code&gt;let client = &amp;amp;configuration.client;&lt;&#x2F;code&gt; is
shadowing the &lt;code&gt;client&lt;&#x2F;code&gt; from the function parameters. The solution is just as simple,
let&#x27;s just rename the function parameter to &lt;code&gt;client_id&lt;&#x2F;code&gt; since that&#x27;s actually what is
being sent here. The changed lines will be as follows.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;hydra&amp;#x2F;src&amp;#x2F;apis&amp;#x2F;admin_api.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;revoke_consent_sessions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(), Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; configuration: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;configuration::Configuration &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;self.configuration.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;borrow&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;configuration.client;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        req_builder &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; req_builder.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;subject&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;subject.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;())]);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; s) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; client_id {
&lt;&#x2F;span&gt;&lt;span&gt;            req_builder &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; req_builder.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;client&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;())]);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;updating-our-login-and-consent-implementation&quot;&gt;Updating our login and consent implementation&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have our generated hydra client library, we want to integrate this into our &lt;code&gt;auth&lt;&#x2F;code&gt; project. Let&#x27;s
take a look at the code. For reading and getting a quick overview of the code. My preferred way of doing this
is by reading the documentation. Even when there is no documentation, Rust generates pretty nice documentation
from the public types and provides a fairly intuitive way of navigating and searching the interface. To generate
the documentation, Cargo provides the &lt;code&gt;doc&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;cargo doc --open
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If your browser didn&#x27;t open with documentation page, you should be able to find it at &lt;code&gt;.&#x2F;target&#x2F;doc&#x2F;hydra&#x2F;index.html&lt;&#x2F;code&gt;
in the root directory of the project.&lt;&#x2F;p&gt;
&lt;p&gt;Once the documentation is opened, you should find the hydra crate which will be in the crates list. Inside, you will find
2 modules, the apis and models modules. Inside the apis module, we will find the structs and traits that will help us
communicate with hydra. The &lt;code&gt;AdminApiClient&lt;&#x2F;code&gt; will be the one we care about to implement the login and consent features.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;impl AdminApiClient
&lt;&#x2F;span&gt;&lt;span&gt;pub fn new(configuration: Rc&amp;lt;Configuration&amp;gt;) -&amp;gt; AdminApiClient
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can see that &lt;code&gt;AdminApiClient&lt;&#x2F;code&gt; has a new function to create a new instance of the struct and it takes a &lt;code&gt;Rc&amp;lt;Configuration&amp;gt;&lt;&#x2F;code&gt;.
&lt;code&gt;Rc&amp;lt;Configuration&amp;gt;&lt;&#x2F;code&gt; is a reference counted pointer to a Configuration struct and the Configuration struct has some fields
that helps the ApiClient figure out how to make calls to the hydra instance.&lt;&#x2F;p&gt;
&lt;p&gt;Similar to the Tera template renderer, we will create a Warp Filter that will contain the AdminApiClient and pass it to the
function handling the endpoints. But first we need to add the dependency to the hydra crate to our main auth crate.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;hydra &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;..&#x2F;hydra&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;hydra::apis::{configuration::Configuration, AdminApiClient};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::rc::Rc;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;with_hydra_api&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(AdminApiClient,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    warp::any()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;move || &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; configuration &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Configuration::new();
&lt;&#x2F;span&gt;&lt;span&gt;            configuration.base_path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;HYDRA_ADMIN_ADDRESS&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            AdminApiClient::new(Rc::new(configuration))
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But something&#x27;s not right, we are getting a compilation error, with rust stating that our &lt;code&gt;Rc&amp;lt;Configuration&amp;gt;&lt;&#x2F;code&gt; isn&#x27;t &lt;code&gt;Send&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;error[E0277]: `std::rc::Rc&amp;lt;hydra::apis::configuration::Configuration&amp;gt;` cannot be sent between threads safely
&lt;&#x2F;span&gt;&lt;span&gt;  --&amp;gt; auth&#x2F;src&#x2F;main.rs:44:10
&lt;&#x2F;span&gt;&lt;span&gt;   |
&lt;&#x2F;span&gt;&lt;span&gt;44 |         .boxed()
&lt;&#x2F;span&gt;&lt;span&gt;   |          ^^^^^ `std::rc::Rc&amp;lt;hydra::apis::configuration::Configuration&amp;gt;` cannot be sent between threads safely
&lt;&#x2F;span&gt;&lt;span&gt;   |
&lt;&#x2F;span&gt;&lt;span&gt;   = help: within `(hydra::apis::admin_api::AdminApiClient,)`, the trait `std::marker::Send` is not implemented for `std::rc::Rc&amp;lt;hydra::apis::configuration::Configuration&amp;gt;`
&lt;&#x2F;span&gt;&lt;span&gt;   = note: required because it appears within the type `hydra::apis::admin_api::AdminApiClient`
&lt;&#x2F;span&gt;&lt;span&gt;   = note: required because it appears within the type `(hydra::apis::admin_api::AdminApiClient,)`
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What is Send? &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#rust-send-sync&quot;&gt;Send&lt;&#x2F;a&gt; is a marker trait that states whether something can be sent between threads (i.e. thread-safe).
What can we do? Well not much, it seems that Warp requires state passed into it&#x27;s filters to be thread safe since it aims to concurrently
handle requests with multiple threads but the openapi code generator produced code that can&#x27;t. We are going to have to modify the generated code
just enough to make it thread safe. One way to do that is simply to switch the Rc with &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.Arc.html&quot;&gt;Arc&lt;&#x2F;a&gt;
which is the thread safe version of Rc.&lt;&#x2F;p&gt;
&lt;p&gt;These 2 commands ought to do it.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;find .&#x2F;hydra -type f -exec sed -i &amp;#39;&amp;#39; &amp;#39;s&#x2F;std::rc::Rc&#x2F;std::sync::Arc&#x2F;g&amp;#39; {} \;
&lt;&#x2F;span&gt;&lt;span&gt;find .&#x2F;hydra -type f -exec sed -i &amp;#39;&amp;#39; &amp;#39;s&#x2F;Rc&#x2F;Arc&#x2F;g&amp;#39; {} \;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now instead of &lt;code&gt;AdminApiClient::new(Rc::new(configuration))&lt;&#x2F;code&gt; we can pass it &lt;code&gt;AdminApiClient::new(Arc::new(configuration))&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s implement the accept login request logic for the hydra integration in the login page.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;log::info;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;serde::{Deserialize, Serialize};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;tera::{Context, Tera};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;warp::{self, http::Uri, Filter};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;hydra::apis::{configuration::Configuration, AdminApi, AdminApiClient};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::{&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;::FromStr, sync::Arc};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_page&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;LoginQueryParams {
&lt;&#x2F;span&gt;&lt;span&gt;        login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::get()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(warp::query::query())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(view::with_tera())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;with_hydra_api&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;move |&lt;&#x2F;span&gt;&lt;span&gt;query_params: LoginQueryParams, tera: Tera, hydra_api: AdminApiClient&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; The challenge is used to fetch information about the login request from ORY Hydra.
&lt;&#x2F;span&gt;&lt;span&gt;                        query_params
&lt;&#x2F;span&gt;&lt;span&gt;                            .login_challenge
&lt;&#x2F;span&gt;&lt;span&gt;                            .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;login_challenge&lt;&#x2F;span&gt;&lt;span&gt;| {
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; login_request &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;                                    hydra_api.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get_login_request&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;login_challenge).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; If hydra was already able to authenticate the user, skip will be true and we do not need to re-authenticate
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; login_request.skip.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap_or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;                                    info!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Hydra was already able to authenticate the user, skipping login as we do not need to re-authenticate&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;                                    info!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accepting login request with Hydra&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; You can apply logic here, for example update the number of times the user logged in.
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Now it&amp;#39;s time to grant the login request. You could also deny the request if something went terribly wrong
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; (e.g. your arch-enemy logging in...)
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; completed_request &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_api
&lt;&#x2F;span&gt;&lt;span&gt;                                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;accept_login_request&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;login_challenge,
&lt;&#x2F;span&gt;&lt;span&gt;                                            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(hydra::models::AcceptLoginRequest::new(
&lt;&#x2F;span&gt;&lt;span&gt;                                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; All we need to do is to confirm that we indeed want to log in the user.
&lt;&#x2F;span&gt;&lt;span&gt;                                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; We are using a hardcoded subject here, the subject should be an immutable id of the user that is loggin in
&lt;&#x2F;span&gt;&lt;span&gt;                                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; to let Hydra know which user to associate with this login.
&lt;&#x2F;span&gt;&lt;span&gt;                                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hardcoded_subject&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                                            )),
&lt;&#x2F;span&gt;&lt;span&gt;                                        )
&lt;&#x2F;span&gt;&lt;span&gt;                                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; All we need to do now is to redirect the user back to hydra!
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;::new(warp::redirect(
&lt;&#x2F;span&gt;&lt;span&gt;                                        Uri::from_str(
&lt;&#x2F;span&gt;&lt;span&gt;                                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;completed_request
&lt;&#x2F;span&gt;&lt;span&gt;                                                .redirect_to
&lt;&#x2F;span&gt;&lt;span&gt;                                                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap_or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;                                        )
&lt;&#x2F;span&gt;&lt;span&gt;                                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                                    )) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;dyn warp::Reply&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; If authentication can&amp;#39;t be skipped we MUST show the login UI.
&lt;&#x2F;span&gt;&lt;span&gt;                                    info!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Sending user to login&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; The challenge will be a hidden input field
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Context::new();
&lt;&#x2F;span&gt;&lt;span&gt;                                    context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login_challenge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;login_challenge);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;context).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;::new(warp::reply::html(body)) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;dyn warp::Reply&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                                }
&lt;&#x2F;span&gt;&lt;span&gt;                            })
&lt;&#x2F;span&gt;&lt;span&gt;                            .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap_or_else&lt;&#x2F;span&gt;&lt;span&gt;(|| {
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Context::new()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;::new(warp::reply::html(body)) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;dyn warp::Reply&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                            })
&lt;&#x2F;span&gt;&lt;span&gt;                    },
&lt;&#x2F;span&gt;&lt;span&gt;                )
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is quite a bit happening here, so let&#x27;s explain. First, I added info logging in a few places, which is why
I added the &lt;code&gt;log::info&lt;&#x2F;code&gt; dependency. We will need to turn on the logs later on, but let&#x27;s not worry about that for now.
The logic goes as follows:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;We map the &lt;code&gt;login_challenge&lt;&#x2F;code&gt; from the query parameters, if it doesn&#x27;t exist, we render a login page without
login challenge. I did this because I expected the login page to also be usable outside of an oauth2 flow, in which case
we could simply return a session cookie once the user is authenticated. We could also have simply returned an
error if the login challenge wasn&#x27;t there.&lt;&#x2F;li&gt;
&lt;li&gt;If the login challenge exists, we call hydra&#x27;s get login request api and with the login request we check if
we could skip the login page if the user had successfully authenticated with this browser in the past.&lt;&#x2F;li&gt;
&lt;li&gt;If we can skip the user authentication, we go straight into accepting the login request with the hydra api. This is
also where we could have added some logic before calling the accept login api to do various checks.&lt;&#x2F;li&gt;
&lt;li&gt;Once the login request is accepted, we redirect to the url provided by hydra or to the homepage if it&#x27;s not provided. In
this case since we are expecting to be redirected by hydra, we could also have returned an error page if the redirect url was
empty.&lt;&#x2F;li&gt;
&lt;li&gt;If we can&#x27;t skip authentication, we send the user to the login page with the login challenge set.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Something else you may have noticed is that instead of returning a &lt;code&gt;warp::Reply&lt;&#x2F;code&gt; directly, I wrapped it inside of a Box so that
our function is returning a trait object. This is because we are returning 2 different types both implementing the Reply trait
in the 2 branches of our if else expression. This wouldn&#x27;t work unless we make them both the same type by turning them into trait
objects. To learn more about Trait Objects check out these &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#rust-trait-objects&quot;&gt;links&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I also imported the &lt;code&gt;std::str::FromStr&lt;&#x2F;code&gt; trait so that it can be used to convert an &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; to a &lt;code&gt;Uri&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As for handling the login form, we will be following similar logic as above.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;accept_login&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;FormBody {
&lt;&#x2F;span&gt;&lt;span&gt;        login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        username: String,
&lt;&#x2F;span&gt;&lt;span&gt;        password: String,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::post()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            warp::body::content_length_limit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1024 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(warp::body::form())
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;with_hydra_api&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;form_body&lt;&#x2F;span&gt;&lt;span&gt;: FormBody, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;hydra_api&lt;&#x2F;span&gt;&lt;span&gt;: AdminApiClient| {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Add logic here to verify the username and password from the submitted login form
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Accepting login request, although you could still deny the login request if something else went wrong
&lt;&#x2F;span&gt;&lt;span&gt;                    form_body
&lt;&#x2F;span&gt;&lt;span&gt;                        .login_challenge
&lt;&#x2F;span&gt;&lt;span&gt;                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;login_challenge&lt;&#x2F;span&gt;&lt;span&gt;| {
&lt;&#x2F;span&gt;&lt;span&gt;                            info!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Accepting login request with Hydra&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; completed_request &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_api
&lt;&#x2F;span&gt;&lt;span&gt;                                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;accept_login_request&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;login_challenge,
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(hydra::models::AcceptLoginRequest::new(
&lt;&#x2F;span&gt;&lt;span&gt;                                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; We are using a hardcoded subject here, the subject should be an immutable id of the user that is loggin in
&lt;&#x2F;span&gt;&lt;span&gt;                                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; to let Hydra know which user to associate with this login
&lt;&#x2F;span&gt;&lt;span&gt;                                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;hardcoded_subject&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                                    )),
&lt;&#x2F;span&gt;&lt;span&gt;                                )
&lt;&#x2F;span&gt;&lt;span&gt;                                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Redirecting to hydra
&lt;&#x2F;span&gt;&lt;span&gt;                            warp::redirect(
&lt;&#x2F;span&gt;&lt;span&gt;                                Uri::from_str(
&lt;&#x2F;span&gt;&lt;span&gt;                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;completed_request.redirect_to.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap_or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;                                )
&lt;&#x2F;span&gt;&lt;span&gt;                                .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                            )
&lt;&#x2F;span&gt;&lt;span&gt;                        })
&lt;&#x2F;span&gt;&lt;span&gt;                        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap_or_else&lt;&#x2F;span&gt;&lt;span&gt;(|| warp::redirect(Uri::from_str(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()))
&lt;&#x2F;span&gt;&lt;span&gt;                }),
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And following the same principles as the login logic, we can do the same for the consent.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;enabling-logging&quot;&gt;Enabling logging&lt;&#x2F;h4&gt;
&lt;p&gt;Before we finish, let&#x27;s print the logs that we inserted. To do that we will be using the env_logger library.
It&#x27;s a fairly simple library that allows you to control the log level and which crates should have it&#x27;s logs
enabled through an environment variable, by default that env var is &lt;code&gt;RUST_LOG&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I will also add the log Filter from warp to give us access logs on our endpoints.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[tokio::main]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;::std::env::var_os(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RUST_LOG&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_none&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        ::std::env::set_var(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RUST_LOG&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;warp=info,auth_svc=trace,api_access=trace&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    env_logger::init();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; routes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;auth_routes&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;homepage&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;(warp::log(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;api_access&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    warp::serve(routes).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)).await;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;hydra-auth-example-rs&#x2F;tree&#x2F;2-login-and-consent-with-hydra&quot;&gt;What you should now have.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-our-login-and-consent-integration&quot;&gt;Testing our login and consent integration&lt;&#x2F;h3&gt;
&lt;p&gt;We now have working login and consent application and a way to deploy hydra locally. How do we know what
we wrote is work? Let&#x27;s try it out manually and then write some tests for it.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;docker-compose up -d
&lt;&#x2F;span&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Create a client in Hydra.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;curl -X POST \
&lt;&#x2F;span&gt;&lt;span&gt;  http:&#x2F;&#x2F;localhost:4445&#x2F;clients \
&lt;&#x2F;span&gt;&lt;span&gt;  -H &amp;#39;Content-Type: application&#x2F;json&amp;#39; \
&lt;&#x2F;span&gt;&lt;span&gt;  -d &amp;#39;{
&lt;&#x2F;span&gt;&lt;span&gt;	&amp;quot;client_id&amp;quot;: &amp;quot;my-implicit-client&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;	&amp;quot;grant_types&amp;quot;: [&amp;quot;implicit&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;	&amp;quot;response_types&amp;quot;: [&amp;quot;token&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;	&amp;quot;redirect_uris&amp;quot;: [&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;	&amp;quot;token_endpoint_auth_method&amp;quot;: &amp;quot;client_secret_post&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To start the flow, navigate to &lt;a href=&quot;localhost:4444&#x2F;oauth2&#x2F;auth?client_id=my-implicit-client&amp;amp;response_type=token&amp;amp;scope=offline&amp;amp;state=blahblahblah&quot;&gt;Authorization Initialization link&lt;&#x2F;a&gt;
which is usually generated by an OAuth2 client or library.&lt;&#x2F;p&gt;
&lt;p&gt;After going through the login and consent pages, we should end up back on the welcome page with the access token.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;#access_token=IQc9NKSJyHS9Vy9iS05kyXVoUTkXexCQPxq-6_Ly5C8.m5m7P-RYhty0o47x35D1uF-k_JJdttzjxqqw11Kr22M&amp;amp;expires_in=3600&amp;amp;scope=offline&amp;amp;state=blahblahblah&amp;amp;token_type=bearer
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since we know that our integration is working, let&#x27;s write a test that we can run to verify that it keeps working in the
future. To do that, we will create a &lt;code&gt;tests&lt;&#x2F;code&gt; directory in our &lt;code&gt;auth&lt;&#x2F;code&gt; project. That&#x27;s where integration tests usually go,
whereas unit tests can be written inline (For more information on tests check out these &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#rust-testing&quot;&gt;links&lt;&#x2F;a&gt;). But we
might want to refer to some structs from our crate such as the form model structs, currently we can&#x27;t do that because we
wrote all our code in a &lt;code&gt;main.rs&lt;&#x2F;code&gt; file which is used for creating a binary. We would need to move the code that we would
like to be able to share as a library into a &lt;code&gt;lib.rs&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;We will add a function that returns our routes which can then be used from the &lt;code&gt;main.rs&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;lib.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; warp::filters::BoxedFilter&amp;lt;(impl warp::reply::Reply,)&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;auth_routes&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;homepage&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;(warp::log(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;api_access&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;boxed&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;main.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; auth;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; warp;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[tokio::main]
&lt;&#x2F;span&gt;&lt;span&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;::std::env::var_os(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RUST_LOG&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_none&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        ::std::env::set_var(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;RUST_LOG&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;warp=info,auth_svc=trace,api_access=trace&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    env_logger::init();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    warp::serve(auth::routes())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .await;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we will have to expose the structs we created to model the form bodies of our login and consent apis.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;src&amp;#x2F;lib.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;LoginFormBody {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;username: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;password: String,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span&gt;ConsentStatus {
&lt;&#x2F;span&gt;&lt;span&gt;    Authorize,
&lt;&#x2F;span&gt;&lt;span&gt;    Deny,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;ConsentFormBody {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;consent_challenge: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;submit: ConsentStatus,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have a library crate we can get to the actual work of writing the test.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;Cargo.toml&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#f92672;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[dev-dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;uuid &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.8&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;v4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;oauth2 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot; 3.0.0-alpha.7&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;reqwest &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;0.10.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;json&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;blocking&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;cookies&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The body of a basic test is as follows.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[test]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;basic_test&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since this is an integration test we may want to check that our external dependencies are up.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;reqwest::{self, Url};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[test]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_and_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;check_auth_svc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;AUTHN_BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;check_hydra&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;HYDRA_ADMIN_BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;check_auth_svc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;authn_url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;reqwest::blocking::get(authn_url).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    assert_eq!(res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;(), reqwest::StatusCode::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;OK&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;check_hydra&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;hydra_url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Url::parse(hydra_url).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;reqwest::blocking::get(hydra_url.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;health&#x2F;ready&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    assert_eq!(res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;(), reqwest::StatusCode::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;OK&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As we saw in our manual test, we will first need to create an oauth2 client to do our test. To know
which type of oauth2 client we want to create, we should decide what we will be testing. So let&#x27;s say
we will be testing the authorization code grant type.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;hydra::apis::{configuration::Configuration, AdminApi, AdminApiClient};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;reqwest::{self, Url};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::sync::Arc;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;uuid::Uuid;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[test]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_and_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Configuration::new();
&lt;&#x2F;span&gt;&lt;span&gt;    config.base_path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;HYDRA_ADMIN_BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_admin_client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;AdminApiClient::new(Arc::new(config));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; oauth2_client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;create_oauth2_client&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_admin_client);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;create_oauth2_client&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;hydra_admin_client&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;AdminApiClient) -&amp;gt; hydra::models::OAuth2Client {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; new_oauth2_client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;hydra::models::OAuth2Client::new();
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.client_id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(format!(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        Uuid::new_v4().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;my-test-client&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    ));
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.client_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login-flow-test-client&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.client_secret &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;client-secret&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.grant_types &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(vec![&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;authorization_code&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()]);
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.redirect_uris &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(vec![&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;AUTHN_BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()]);
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.token_endpoint_auth_method &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;client_secret_basic&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    new_oauth2_client.scope &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;openid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    hydra_admin_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;create_o_auth2_client&lt;&#x2F;span&gt;&lt;span&gt;(new_oauth2_client)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the created OAuth 2 client, we can initiate an oauth2 flow. For that, we will use an &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;oauth2&quot;&gt;OAuth2 library&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[test]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_and_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; access_token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;oauth2_client.client_id.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;oauth2_client.client_secret.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_secret&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Create an HTTP client to act as the browser
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;reqwest::blocking::Client::builder()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Enable the cookie store for hydra csrf cookies
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;cookie_store&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;redirect&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::redirect::Policy::none())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Create an OAuth2 client by specifying the client ID, client secret, authorization URL and
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; token URL.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;OAuth2BasicClient::new(
&lt;&#x2F;span&gt;&lt;span&gt;        ClientId::new(client_id.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(ClientSecret::new(client_secret.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;())),
&lt;&#x2F;span&gt;&lt;span&gt;        AuthUrl::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:4444&#x2F;oauth2&#x2F;auth&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(TokenUrl::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:4444&#x2F;oauth2&#x2F;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Set the URL the user will be redirected to after the authorization process.
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set_redirect_url&lt;&#x2F;span&gt;&lt;span&gt;(RedirectUrl::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Generate a PKCE challenge.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(pkce_challenge, pkce_verifier) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;PkceCodeChallenge::new_random_sha256();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Generate the full authorization URL.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(auth_url, csrf_token) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;authorize_url&lt;&#x2F;span&gt;&lt;span&gt;(CsrfToken::new_random)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Set the desired scopes.
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;add_scope&lt;&#x2F;span&gt;&lt;span&gt;(Scope::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;openid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;()))
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Set the PKCE code challenge.
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set_pkce_challenge&lt;&#x2F;span&gt;&lt;span&gt;(pkce_challenge)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; This is the URL you should usually redirect the user to,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; in order to trigger the authorization process.
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;URL to trigger authorization process: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, auth_url);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Usually the authorize url would then be used to redirect the user (resource owner) to initiate the
authorization process. But since we are writing an automated test, we will be emulating the browser
with an http client and call our login and consent apis directly.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_secret&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_login_completed_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;perform_login_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;reqwest_client, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;auth_url);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;perform_login_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;reqwest_client&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;reqwest::blocking::Client, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;auth_url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Url) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_authz_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(auth_url.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;This is the response from Hydra when visiting the url to trigger the authorization process&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_authz_res);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(hydra_authz_res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_redirection&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; login_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_authz_res
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::header::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;LOCATION&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; login_challenge &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Url::parse(login_url)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;query_pairs&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;| c.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;eq_ignore_ascii_case&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;login_challenge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; login_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;login&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;LoginFormBody {
&lt;&#x2F;span&gt;&lt;span&gt;            login_challenge: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(login_challenge),
&lt;&#x2F;span&gt;&lt;span&gt;            username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;pass&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;This is the response from our auth service when visiting the login page&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;login_res);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(login_res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_redirection&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_redirected_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; login_res
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::header::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;LOCATION&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    hydra_redirected_url.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can note that I disabled redirection in our http client so that we can see all the redirects
that are occurring. That is not strictly necessary and we could simply have followed the redirects
which would also have made our code much shorter.&lt;&#x2F;p&gt;
&lt;p&gt;Same thing for the consent.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_secret&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; redirected_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;perform_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;reqwest_client, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_login_completed_url);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;perform_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;reqwest_client&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;reqwest::blocking::Client,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;hydra_login_completed_url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_consent_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(hydra_login_completed_url)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;This is the response from hydra when visiting the url received at the end of the login process&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_consent_res);
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;As we can see, hydra will now redirect us to our consent page since the login request was accepted&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(hydra_consent_res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_redirection&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; consent_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_consent_res
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::header::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;LOCATION&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; consent_challenge &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Url::parse(consent_url)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;query_pairs&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;| c.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;eq_ignore_ascii_case&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;consent_challenge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; consent_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;localhost:3000&#x2F;consent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ConsentFormBody {
&lt;&#x2F;span&gt;&lt;span&gt;            consent_challenge: consent_challenge,
&lt;&#x2F;span&gt;&lt;span&gt;            submit: ConsentStatus::Authorize,
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;This is the response from our auth service when visiting the consent page&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;consent_res);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(consent_res.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;is_redirection&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_consent_approved_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; consent_res
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::header::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;LOCATION&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hydra_consent_approved_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; reqwest_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(hydra_consent_approved_url)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;This is the response from hydra once the consent request was accepted. You can note that it contains the authorization code appended to the redirect url configured at the start of the OAuth2 process&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_consent_approved_res);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; redirected_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_consent_approved_res
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(reqwest::header::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;LOCATION&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    redirected_url.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the final redirected url, we will receive an auth code that is used in the authorization code grant type
to get our access token.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;client_secret&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; auth_code &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Url::parse(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;redirected_url)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;query_pairs&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;| c.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;eq_ignore_ascii_case&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;code&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Once the user has been redirected to the redirect URL, you&amp;#39;ll have access to the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; authorization code. For security reasons, your code should verify that the `state`
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; parameter returned by the server matches `csrf_state`.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Here we are skipping this check for the purposes of brevity of this test.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Now you can trade it for an access token.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; token_result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;exchange_code&lt;&#x2F;span&gt;&lt;span&gt;(AuthorizationCode::new(auth_code.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;()))
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Set the PKCE code verifier.
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set_pkce_verifier&lt;&#x2F;span&gt;&lt;span&gt;(pkce_verifier)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;(http_client)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;Once the authorization code is exchanged, we finally received an access token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;token_result);
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, token_result.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;secret&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    token_result.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;secret&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have an access token, we should probably see if we can verify it with the &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#oauth2-introspection&quot;&gt;introspection endpoint&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;code-title&quot;&gt;auth&amp;#x2F;tests&amp;#x2F;oauth2_auth_code_end_to_end_test.rs&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[test]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;login_and_consent_flow&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; access_token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;initiate_oauth2_code_flow&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;oauth2_client.client_id.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;oauth2_client.client_secret.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;introspect_access_token&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;hydra_admin_client, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;access_token);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;introspect_access_token&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;hydra_admin_client&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;AdminApiClient, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; introspection_res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; hydra_admin_client
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;introspect_o_auth2_token&lt;&#x2F;span&gt;&lt;span&gt;(access_token, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;introspection_res);
&lt;&#x2F;span&gt;&lt;span&gt;    assert!(introspection_res.active);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can now run our test to see everything running. Since I added some print statements, to be
able to see those we will also need to run the test command with the &lt;code&gt;--nocapture&lt;&#x2F;code&gt; flag.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;cargo test -- --nocapture
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;hydra-auth-example-rs&#x2F;tree&#x2F;3-integration-test&quot;&gt;Code with the test&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;async-rust&quot;&gt;Async Rust&lt;&#x2F;h3&gt;
&lt;p&gt;If you have kept up to date with Async Rust, you may have noticed that the hydra generated client
uses the blocking version of the Reqwest library to make it&#x27;s api calls. This means that by making
these API calls we are blocking the tokio executor. What this means is that in cases of load on our
auth server, the server may have all it&#x27;s executor threads blocked on the blocking call we are currently
making and prevent the server from serving as many requests as it could.&lt;&#x2F;p&gt;
&lt;p&gt;There are 2 ways to fix, the easiest would be to move the blocking call into a thread where blocking isn&#x27;t
an issue. To do that we could spawn the blocking call on a seperate thread where we wouldn&#x27;t block the tokio
executor. See these &lt;a href=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;integration-a-rust-web-service-with-oauth2&#x2F;#rust-async-tokio-blocking&quot;&gt;links for more info&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The second would be to rewrite the blocking api client with a non-blocking http client. The latest version
of reqwest is such a client.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here?&lt;&#x2F;h2&gt;
&lt;p&gt;We now have a working OAuth 2.0 authorization server integrated with a bare bones auth server.
Having a working OAuth server is only the start of implementing a good authorization system for
a web service. Integrating it with your services, making access control decisions, how to pass
authorization information to your resource servers are only some of the things that still need to be
done to get a working system. We also took several shortcuts in this demo to keep the code short and
focus on the parts we care about more but there are also several things that need to be done to make our
service production ready. Here are several things I can see need improvement in no particular order:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Error handling, currently our service will panic at anything deviating from the happy path&lt;&#x2F;li&gt;
&lt;li&gt;Add CSRF protection to our login and consent pages and endpoints. This is necessary to prevent a class
of attacks called &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cross-site_request_forgery#Forging_login_requests&quot;&gt;Login CSRF&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Add User management features to our auth service so that it can implement authentication logic&lt;&#x2F;li&gt;
&lt;li&gt;Add actual authentication logic to the login endpoints&lt;&#x2F;li&gt;
&lt;li&gt;Deploy our service to kubernetes. Hydra already provides &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;k8s&quot;&gt;Helm Charts for that&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Making sure to follow Hydra&#x27;s production guide before deploying our services. &lt;a href=&quot;https:&#x2F;&#x2F;www.ory.sh&#x2F;docs&#x2F;hydra&#x2F;production&quot;&gt;Link&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Put our services behind an API Gateway or reverse proxy so that all our services are available at one address&lt;&#x2F;li&gt;
&lt;li&gt;Add https certificates to our services, this may be easier with a reverse proxy&#x2F;API Gateway. This is necessary because OAuth 2 simply doesn&#x27;t have any protection without it&lt;&#x2F;li&gt;
&lt;li&gt;Protect a web service&#x27;s resources(a resource server) with our authorization server&lt;&#x2F;li&gt;
&lt;li&gt;Integrate with Ory&#x27;s other projects such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;keto&quot;&gt;Keto&lt;&#x2F;a&gt; to provide Access Control rules processing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;div class=&quot;tight-list&quot;&gt;&lt;&#x2F;div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id=&quot;rfc-6749&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;rfc6749&quot;&gt;RFC 6749: The OAuth 2.0 Authorization Framework&lt;&#x2F;a&gt;&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;ietf-rfcs-list&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;wg&#x2F;oauth&#x2F;&quot;&gt;List of IETF OAuth 2.0 RFCs&lt;&#x2F;a&gt;&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;oauth-net-links&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;oauth.net&#x2F;2&#x2F;&quot;&gt;Links to various documents about OAuth 2.0&lt;&#x2F;a&gt; from oauth.net&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;oauth-security-best-practices&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;draft-ietf-oauth-security-topics-12&quot;&gt;The latest security best practices for OAuth 2.0&lt;&#x2F;a&gt;&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;oauth-2-simplified&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;aaronparecki.com&#x2F;oauth-2-simplified&#x2F;&quot;&gt;OAuth 2.0 Simplified&lt;&#x2F;a&gt; by Aaron Parecki&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;oauth2-openid-connect&quot;&gt;OAuth 2.0 OpenID Connect&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;connect2id.com&#x2F;learn&#x2F;openid-connect&quot;&gt;OpenID Connect Explained&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;An OpenID Connect Primer from Okta
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.okta.com&#x2F;blog&#x2F;2017&#x2F;07&#x2F;25&#x2F;oidc-primer-part-1&quot;&gt;Identity, Claims, &amp;amp; Tokens: Part 1&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.okta.com&#x2F;blog&#x2F;2017&#x2F;07&#x2F;25&#x2F;oidc-primer-part-2&quot;&gt;OIDC In Action: Part 2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.okta.com&#x2F;blog&#x2F;2017&#x2F;08&#x2F;01&#x2F;oidc-primer-part-3&quot;&gt;What&#x27;s in a Token?: Part 3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;hydra-spring-authn&quot;&gt;A previous Hydra integration I wrote with Spring and Java &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;hydra-spring-authn-authz-example&quot;&gt;Git Repo&lt;&#x2F;a&gt;&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;ory-access-authz-patterns&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ory.sh&#x2F;web-api-cloud-access-control-authentication&#x2F;&quot;&gt;Information about when you may want to use different types of access control (Access Tokens vs session cookies)&lt;&#x2F;a&gt;&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;ory-rest-api-docs&quot;&gt;Ory Hydra REST API docs&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ory.sh&#x2F;docs&#x2F;hydra&#x2F;sdk&#x2F;api&quot;&gt;Ory Hydra REST API docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ory&#x2F;docs&#x2F;blob&#x2F;v0.0.30%2BoryOS.14&#x2F;docs&#x2F;hydra&#x2F;sdk&#x2F;api.md&quot;&gt;OryOS.14 Versioned Github link&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;openapi-generator&quot;&gt;OpenAPI&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;openapi-generator.tech&#x2F;&quot;&gt;OpenAPI Generator&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;OpenAPITools&#x2F;openapi-generator&quot;&gt;Github Link&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;rust-send-sync&quot;&gt;Rust Send and Sync Traits&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;marker&#x2F;trait.Send.html&quot;&gt;Send std docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;nomicon&#x2F;send-and-sync.html&quot;&gt;Send and Sync from the Rustonomicon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;rust-trait-objects&quot;&gt;Rust Trait Objects&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch10-01-syntax.html&quot;&gt;Generic Data Types&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;1.30.0&#x2F;book&#x2F;2018-edition&#x2F;ch17-02-trait-objects.html&quot;&gt;Trait Objects&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;rust-testing&quot;&gt;Rust Testing&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch11-00-testing.html&quot;&gt;Writing Automated Tests from the Rust Book&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rust-by-example&#x2F;testing.html&quot;&gt;Testing from Rust By Example&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;oauth2-introspection&quot;&gt;OAuth 2.0 Token Introspection Extension&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.oauth.com&#x2F;oauth2-servers&#x2F;token-introspection-endpoint&#x2F;&quot;&gt;OAuth2 Token Introspection Endpoint from oauth.com&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;rfc7662&quot;&gt;RFC 7662: OAuth 2.0 Token Introspection&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ory.sh&#x2F;docs&#x2F;hydra&#x2F;sdk&#x2F;api#introspect-oauth2-tokens&quot;&gt;Ory Hydra Introspection docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;span id=&quot;rust-async-tokio-blocking&quot;&gt;Async Rust: Tokio Blocking&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;0.2.9&#x2F;tokio&#x2F;task&#x2F;index.html&quot;&gt;tokio::task&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;0.2.9&#x2F;tokio&#x2F;task&#x2F;fn.block_in_place.html&quot;&gt;tokio::task::block_in_place&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;0.2.9&#x2F;tokio&#x2F;task&#x2F;fn.spawn_blocking.html&quot;&gt;tokio::task::spawn_blocking&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
          <title>Go and JSON, various useful libraries and utilities</title>
          <pubDate>Fri, 12 Aug 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/go-and-json/</link>
          <guid>https://www.jonfk.ca/blog/go-and-json/</guid>
          <description xml:base="https://www.jonfk.ca/blog/go-and-json/">&lt;p&gt;JSON is probably one of the most popular data interchange format on the web. Go
with its extensive standard library has good support for JSON. If your
requirements for JSON serialization and deserialization are not specific, I
highly recommend to simply start by using &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt; from the standard
library and reading how it works (&lt;a href=&quot;https:&#x2F;&#x2F;blog.golang.org&#x2F;json-and-go&quot;&gt;blog&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;golang.org&#x2F;pkg&#x2F;encoding&#x2F;json&#x2F;&quot;&gt;docs&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;gobyexample.com&#x2F;json&quot;&gt;examples&lt;&#x2F;a&gt;). Here I will give a few libraries for
JSON in Go and explain what differentiates them from &quot;encoding&#x2F;json&quot;
and each other.&lt;&#x2F;p&gt;
&lt;p&gt;First, why would we want to use anything other than &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt;? Well to
provide flexibility and simplicity in its usage, &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt; makes use
of reflection on the structs that defines the structure of the JSON you are
expecting to serialize or deserialize and because of that it incurs a heavy
performance penalty.&lt;&#x2F;p&gt;
&lt;p&gt;What if we didn&#x27;t have to reflect on a struct and simply insert data to a
&lt;code&gt;map[string]interface{}&lt;&#x2F;code&gt; object? Well &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt; allows you to do that
but the resulting data structure ends up frustrating to work with, with casting
all over the place and is hard to navigate fluently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structured-data&quot;&gt;Structured Data&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pquerna&#x2F;ffjson&quot;&gt;ffjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mailru&#x2F;easyjson&quot;&gt;easyjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To avoid reflection, these libraries provide a code generator to be run over
structs defining the structure of the JSON and fields in the same format as
&lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt;. The generated code is now the parsing code for the JSON
structure.&lt;&#x2F;p&gt;
&lt;p&gt;Both &lt;code&gt;ffjson&lt;&#x2F;code&gt; and &lt;code&gt;easyjson&lt;&#x2F;code&gt; provide a drop in replacement for &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt;
which means that after generating code for parsing, you should be able to use
the same api calls as before but with &lt;code&gt;ffjson&lt;&#x2F;code&gt; or &lt;code&gt;easyjson&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The advantage of this is that you now have specific parsing
code for the JSON structure you are expecting. This also gives a nice speed up
in the order of about &lt;strong&gt;2 to 3 times faster&lt;&#x2F;strong&gt; than &lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The disadvantage is that you will need to run the code generator every time you
change the struct defining the JSON structure. There is a simple fix for this
by using the &lt;code&gt;go generate&lt;&#x2F;code&gt; command and &lt;code&gt;&#x2F;&#x2F;go:generate ffjson $GOFILE&lt;&#x2F;code&gt; at the top
of any files with the structs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;easyjson&lt;&#x2F;code&gt; works very similarly to &lt;code&gt;ffjson&lt;&#x2F;code&gt; and claims to be more performant
and behave better when run concurrently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deserializing-unstructured-data&quot;&gt;Deserializing Unstructured Data&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;buger&#x2F;jsonparser&quot;&gt;jsonparser&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tidwall&#x2F;gjson&quot;&gt;gjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;jsonparser&lt;&#x2F;code&gt; and &lt;code&gt;gjson&lt;&#x2F;code&gt; like &lt;code&gt;ffjson&lt;&#x2F;code&gt; and &lt;code&gt;easyjson&lt;&#x2F;code&gt; do not rely on
reflection but what differentiates them from the structured JSON libraries is
that instead of creating structs that defines the structure of the JSON to be
parsed these libraries read the value at a specific path you want.&lt;&#x2F;p&gt;
&lt;p&gt;By only reading values at paths specified, you only need to parse the part of
the data you are interested in. No need to parse the whole json eagerly. Users
of lazy functional languages will recognize here the value of lazy reading. This
allows these libraries to read even faster than &lt;code&gt;ffjson&lt;&#x2F;code&gt; and &lt;code&gt;easyjson&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;By not having to deserialize to a struct, these libraries can read directly from
the byte buffer which allows values to be read much faster than other
deserialization libraries.&lt;&#x2F;p&gt;
&lt;p&gt;Another advantage over &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt;, is that the api is much more ergonomic
than unmarshalling the json into a &lt;code&gt;map[string]interface{}&lt;&#x2F;code&gt; since no casting
is necessary and walking to the specific field you want to read is much easier.
For example, here is how to read values using &lt;code&gt;&quot;encoding&#x2F;json&quot;&lt;&#x2F;code&gt; with
&lt;code&gt;map[string]interface{}&lt;&#x2F;code&gt; vs &lt;code&gt;gjson&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; &amp;quot;encoding&#x2F;json&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;main
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;encoding&#x2F;json&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;`{&amp;quot;name&amp;quot;:{&amp;quot;first&amp;quot;:&amp;quot;Janet&amp;quot;,&amp;quot;last&amp;quot;:&amp;quot;Prichard&amp;quot;},&amp;quot;age&amp;quot;:47}`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;values &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{}
&lt;&#x2F;span&gt;&lt;span&gt;	json.Unmarshal([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(data), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;values)
&lt;&#x2F;span&gt;&lt;span&gt;	value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;values[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;].(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{})[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;last&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;].(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	printf(value)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; gjson
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;main
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;github.com&#x2F;tidwall&#x2F;gjson&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;json &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;`{&amp;quot;name&amp;quot;:{&amp;quot;first&amp;quot;:&amp;quot;Janet&amp;quot;,&amp;quot;last&amp;quot;:&amp;quot;Prichard&amp;quot;},&amp;quot;age&amp;quot;:47}`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;gjson.Get(json, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;name.last&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;println&lt;&#x2F;span&gt;&lt;span&gt;(value.String())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The disadvantage of these libraries is that they only provide deserialization
and not serialization. For fast serialization, you can rely on the structured
data serializers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;utilities-and-other-helpful-libraries&quot;&gt;Utilities and other helpful libraries&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;convert-json-to-a-go-struct&quot;&gt;Convert JSON to a Go Struct&lt;&#x2F;h3&gt;
&lt;p&gt;Instead of doing this job by hand, here are some utilities that can help you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Online: https:&#x2F;&#x2F;mholt.github.io&#x2F;json-to-go&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;Locally: https:&#x2F;&#x2F;github.com&#x2F;ChimeraCoder&#x2F;gojson&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Useful JSON libraries:
For generating encoders and decoders:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pquerna&#x2F;ffjson&quot;&gt;ffjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mailru&#x2F;easyjson&quot;&gt;easyjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For reading unstructured json at a specific path:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;buger&#x2F;jsonparser&quot;&gt;jsonparser&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tidwall&#x2F;gjson&quot;&gt;gjson&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Spice up your LaTeX with Font Awesome</title>
          <pubDate>Tue, 02 Feb 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/spice-up-your-latex-with-fontawesome/</link>
          <guid>https://www.jonfk.ca/blog/spice-up-your-latex-with-fontawesome/</guid>
          <description xml:base="https://www.jonfk.ca/blog/spice-up-your-latex-with-fontawesome/">&lt;p&gt;LaTeX allows you to create amazing documents which are beautifully typeset. It gives you access to a wide breath of tools and graphics to improve your documents. One of these are the many already available symbols in LaTeX. Here is a comprehensive list of available symbols: &lt;a href=&quot;http:&#x2F;&#x2F;www.ctan.org&#x2F;tex-archive&#x2F;info&#x2F;symbols&#x2F;comprehensive&#x2F;&quot;&gt;Link&lt;&#x2F;a&gt; &lt;a href=&quot;http:&#x2F;&#x2F;mirrors.ctan.org&#x2F;info&#x2F;symbols&#x2F;comprehensive&#x2F;symbols-a4.pdf&quot;&gt;PDF&lt;&#x2F;a&gt;. You can even find symbols by drawing them at this &lt;a href=&quot;http:&#x2F;&#x2F;detexify.kirelabs.org&#x2F;classify.html&quot;&gt;service&lt;&#x2F;a&gt;. These include mathematical symbols, phonetic symbols, currency symbols or symbols for various languages.&lt;&#x2F;p&gt;
&lt;p&gt;I found out recently that you can include &lt;a href=&quot;http:&#x2F;&#x2F;fontawesome.io&#x2F;&quot;&gt;Font Awesome&lt;&#x2F;a&gt; icons as symbols in your latex documents using this &lt;a href=&quot;https:&#x2F;&#x2F;www.ctan.org&#x2F;pkg&#x2F;fontawesome&quot;&gt;CTAN package&lt;&#x2F;a&gt;. This allows you to use some modern icons such as the Github or LinkedIn logos to spice up your documents such as a resume if you write it in LaTeX. If you have a full TeX live distribution such as MacTeX, it should be already included. I was able to use it by adding &lt;code&gt;\usepackage{fontawesome}&lt;&#x2F;code&gt; to my document and using the &lt;code&gt;lualatex&lt;&#x2F;code&gt; command to compile my document. I wasn&#x27;t able figure out why &lt;code&gt;xelatex&lt;&#x2F;code&gt; failed to compile my document. If you do let me know.&lt;&#x2F;p&gt;
&lt;p&gt;The final result:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.jonfk.ca&#x2F;blog&#x2F;spice-up-your-latex-with-fontawesome&#x2F;.&#x2F;resume-screenshot.png&quot; alt=&quot;My Resume with fontawesome icons&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can checkout the source here: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;resume&quot;&gt;Link&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A simple protocol for a TCP chat server and client</title>
          <pubDate>Sun, 21 Jun 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/a-simple-protocol-tcp-chat-server-and-client/</link>
          <guid>https://www.jonfk.ca/blog/a-simple-protocol-tcp-chat-server-and-client/</guid>
          <description xml:base="https://www.jonfk.ca/blog/a-simple-protocol-tcp-chat-server-and-client/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;I will be talking about some motivating factors for why you would want to use a
lower level networking protocol such as TCP and how you could implement your own
application protocol over it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;some-prerequisites&quot;&gt;Some prerequisites&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;a general understanding of networking&lt;&#x2F;li&gt;
&lt;li&gt;a Go distribution&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h2&gt;
&lt;p&gt;Dealing with an HTTP API such as a RESTful web API is often the easier way to
interact with your server. Applications that only need to consume and send
commands to the server asynchronously can fit this use case. But some applications such
as a chat client or a game server need a 2-way method of communication between the client and
server. This is accomplished by using a lower level protocol such as TCP or UDP.&lt;&#x2F;p&gt;
&lt;p&gt;We can think of TCP and UDP as a pipe between 2 endpoints and at each end of the connection we have
a stream of bytes that can be read. I will note here that TCP is in this analogy a reliable pipe whereas
we can consider UDP as a faster but unreliable pipe between the endpoints. Further detail about the differences
of TCP and UDP are beyond the scope of this post, but I encourage you to do some research to learn more since networking
has some very leaky abstractions nad understanding the layer underneath is always useful in debugging issues such as
outages, latency and any properties you would like to have.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-can-we-use-tcp&quot;&gt;How can we use tcp&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we know what we can use to have a continuous stream of data to communicate between 2 endpoints,
how can we actually used that stream of bytes? We first need a way to interpret that stream of bytes so
that we can read the messages passed on there.&lt;&#x2F;p&gt;
&lt;p&gt;There are several ways to do it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We could use a set number of bytes&lt;&#x2F;li&gt;
&lt;li&gt;We could use sentinel values to mark the beginning and&#x2F;or end of a message&lt;&#x2F;li&gt;
&lt;li&gt;Something else(let&#x27;s come back to this)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-pros-and-cons&quot;&gt;The pros and Cons&lt;&#x2F;h3&gt;
&lt;p&gt;Pros of using a set number of bytes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Size of buffer to allocate for the message does not change and is known in advance&lt;&#x2F;li&gt;
&lt;li&gt;Data in the message can be arbitrary and does not need to be escaped&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Cons of using a set number of bytes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Can be wasteful. What if we need to send 8 bytes but we are using 1024 byte buffers&lt;&#x2F;li&gt;
&lt;li&gt;Hard to extend. Once we declare the protocol we cannot change the number of bytes in messages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Pros of using sentinel values&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to implement. Read until we see the sentinel value.&lt;&#x2F;li&gt;
&lt;li&gt;Flexible in how much data we can send.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Cons of using sentinel values&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We cannot use the sentinel values in our data. We need to carefully escape the data to be sent.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;is-there-a-better-option&quot;&gt;Is there a better option?&lt;&#x2F;h2&gt;
&lt;p&gt;Glad you asked.&lt;&#x2F;p&gt;
&lt;p&gt;We could define a protocol that specifies how much data to be sent by using the set number of bytes
scheme. This essentially ends up being a header. We could define a protocol that sets the number of bytes
for the header which always needs to be sent followed by the message body.&lt;&#x2F;p&gt;
&lt;p&gt;A simple example of using this scheme would be to have a 4 byte message for a 32bit int which would
describe the length of the message followed by the message. This will allow us to allocate a buffer of that
size and read until we receive all the bytes promised in the header.&lt;&#x2F;p&gt;
&lt;p&gt;This negates the wastefulness of the fixed number of bytes scheme, but it still makes it hard to extend this
protocol without breaking backwards compatibility. For example, in our simple example protocol, we can send
a maximum of 4GB in our messages. (4 bytes = 4*8 = 32 bits; 2^32-1 = 4294967295 bytes = 4.29497 GB)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lets-use-this-to-implement-a-simple-chat-server-and-client&quot;&gt;Lets use this to implement a simple chat server and client&lt;&#x2F;h2&gt;
&lt;p&gt;Using this scheme we can wrap the &lt;code&gt;net.Conn&lt;&#x2F;code&gt; &lt;code&gt;Read&lt;&#x2F;code&gt; method in a helper function as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;ReadMsg&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conn &lt;&#x2F;span&gt;&lt;span&gt;net.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Conn&lt;&#x2F;span&gt;&lt;span&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Make a buffer to hold length of data
&lt;&#x2F;span&gt;&lt;span&gt;	lenBuf &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	_, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;conn.Read(lenBuf)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Receiving EOF means that the connection has been closed
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span&gt;io.EOF {
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Close conn and exit
&lt;&#x2F;span&gt;&lt;span&gt;		conn.Close()
&lt;&#x2F;span&gt;&lt;span&gt;		fmt.Println(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Connection Closed. Bye bye.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		os.Exit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	lenData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;FromBytes(lenBuf)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Make a buffer to hold incoming data.
&lt;&#x2F;span&gt;&lt;span&gt;	buf &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, lenData)
&lt;&#x2F;span&gt;&lt;span&gt;	reqLen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Keep reading data from the incoming connection into the buffer
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; until all the data promised is received
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;reqLen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;(lenData) {
&lt;&#x2F;span&gt;&lt;span&gt;		tempreqLen, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;conn.Read(buf[reqLen:])
&lt;&#x2F;span&gt;&lt;span&gt;		reqLen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span&gt;tempreqLen
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span&gt;io.EOF {
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, fmt.Errorf(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Received EOF before receiving all promised data.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;			&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, fmt.Errorf(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Error reading: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err.Error())
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(buf), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What this does is read 4bytes from the specified connection and converts those bytes to an int. It then reads
from the connection until it has seen as many bytes from the connection as was promised in the header. The &lt;code&gt;FromBytes&lt;&#x2F;code&gt; function
takes care of converting the bytes to the int. In our protocol, the int are encoded in binary in Big Endian as is common
in most networking protocol which is why big endian is also known as &lt;em&gt;The Network Byte Order&lt;&#x2F;em&gt;. Here is the helper functions to
convert ints to and from bytes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; To convert Big Endian binary format of a 4 byte integer to int32
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;FromBytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;b &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;int32 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	buf &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;bytes.NewReader(b)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;int32
&lt;&#x2F;span&gt;&lt;span&gt;	err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;binary.Read(buf, binary.BigEndian, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;result)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;result
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; To convert an int32 to a 4 byte Big Endian binary format
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;ToBytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;int32&lt;&#x2F;span&gt;&lt;span&gt;) []&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	buf &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(bytes.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Buffer&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;binary.Write(buf, binary.BigEndian, i)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;buf.Bytes()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Similarly for the write:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;WriteMsg&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conn &lt;&#x2F;span&gt;&lt;span&gt;net.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Conn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;msg &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Send the size of the message to be sent
&lt;&#x2F;span&gt;&lt;span&gt;	conn.Write([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(ToBytes(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;int32&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(msg))))))
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Send the message
&lt;&#x2F;span&gt;&lt;span&gt;	conn.Write([]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(msg))
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We write the size of the message and then the message.&lt;&#x2F;p&gt;
&lt;p&gt;Now lets use these in a server:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Listen for incoming connections.
&lt;&#x2F;span&gt;&lt;span&gt;	l, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;net.Listen(CONN_TYPE, CONN_HOST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;CONN_PORT)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		fmt.Println(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Error listening:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err.Error())
&lt;&#x2F;span&gt;&lt;span&gt;		os.Exit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Close the listener when the application closes.
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span&gt;l.Close()
&lt;&#x2F;span&gt;&lt;span&gt;	fmt.Println(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Listening on &amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;CONN_HOST &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;:&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;CONN_PORT)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Listen for an incoming connection.
&lt;&#x2F;span&gt;&lt;span&gt;		conn, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;l.Accept()
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;			fmt.Println(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Error accepting: &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err.Error())
&lt;&#x2F;span&gt;&lt;span&gt;			os.Exit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Handle connections in a new goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span&gt;handleRequest(conn)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Handles incoming requests.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;handleRequest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conn &lt;&#x2F;span&gt;&lt;span&gt;net.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Conn&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Close the connection when you&amp;#39;re done with it.
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span&gt;conn.Close()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	msg, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;common.ReadMsg(conn)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	fmt.Printf(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Message Received: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;%s\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, msg)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Send a response back to person contacting us.
&lt;&#x2F;span&gt;&lt;span&gt;	common.WriteMsg(conn, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Message Received.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we setup the server to listen for connections at a given port and handle the connection
when we receive one. You will notice we are closing the connection as soon as we send a reply,
but this is not necessary and we could listen for several messages depending on your application.&lt;&#x2F;p&gt;
&lt;p&gt;On the client side:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;	tcpAddr, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;net.ResolveTCPAddr(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;tcp&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, CONN_HOST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;CONN_PORT)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Connect to server through tcp.
&lt;&#x2F;span&gt;&lt;span&gt;	conn, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;net.DialTCP(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;tcp&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span&gt;, tcpAddr)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span&gt;conn.Close()
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span&gt;printOutput(conn)
&lt;&#x2F;span&gt;&lt;span&gt;	writeInput(conn)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;writeInput&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;net.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;TCPConn&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;	fmt.Println(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;Enter text:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Read from stdin.
&lt;&#x2F;span&gt;&lt;span&gt;		reader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;bufio.NewReader(os.Stdin)
&lt;&#x2F;span&gt;&lt;span&gt;		fmt.Print(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		text, _ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;reader.ReadString(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;&amp;#39;\n&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		common.WriteMsg(conn, text)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;printOutput&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;conn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;net.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;TCPConn&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		msg, err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;common.ReadMsg(conn)
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;			log.Fatal(err)
&lt;&#x2F;span&gt;&lt;span&gt;		}
&lt;&#x2F;span&gt;&lt;span&gt;		fmt.Println(msg)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We first connect to the server and the same port our server is listening to and set a goroutine
to print all messages we receive from the connection, if the connection is closed we exit. On another
thread of execution we listen for input on stdin and write messages to the connection.&lt;&#x2F;p&gt;
&lt;p&gt;And Voila. We have a client chatting with our server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;concluding-remarks&quot;&gt;Concluding remarks&lt;&#x2F;h2&gt;
&lt;p&gt;You will probably notice we are not handling the errors and simply exit on failure on any errors in this
example. Don&#x27;t do that in your code. I just wanted to keep my example simple here. In an actual program,
we would return err in the function that has one and the top-level would handle them.&lt;&#x2F;p&gt;
&lt;p&gt;You can now extend this example to send more meaningful things that just strings. Interesting things
such as XML or JSON or Protobuf.&lt;&#x2F;p&gt;
&lt;p&gt;The full example is available &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;golang-chat&#x2F;tree&#x2F;b2cfd296bd40d85068b8a4105c9e6b2b30213168&quot;&gt;HERE&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Experiments with Processing</title>
          <pubDate>Fri, 08 May 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/experiments-with-processing/</link>
          <guid>https://www.jonfk.ca/blog/experiments-with-processing/</guid>
          <description xml:base="https://www.jonfk.ca/blog/experiments-with-processing/">&lt;p&gt;Processing is a programming language targeted at electronic arts and visual design communities.
It can serve as the foundation for electronic sketchbooks and aims to teach it&#x27;s target communities
programming in a visual context. At its core processing is a simple programming language with a very
lightweight C-style syntax, similar to Java in which it is implemented.&lt;&#x2F;p&gt;
&lt;p&gt;There are now several implementations of Processing such as the one used on this page using
&lt;a href=&quot;http:&#x2F;&#x2F;processingjs.org&#x2F;&quot;&gt;Processing.js&lt;&#x2F;a&gt;. The original one is still the easiest one to use
in my opinion but I haven&#x27;t given a try to the others very thoroughly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-context&quot;&gt;The context&lt;&#x2F;h2&gt;
&lt;p&gt;I am moving to a new apartment soon and in the process of buying new furniture I have to map out the dimensions
of my apartment. I could do a simple paper sketch or simply draw the map in a paint program but my inner geek
compels me to try out something new. I have tried several declarative visual programming languages but I haven&#x27;t
found any that I was comfortable with. It may simply be because I am just not made for visual design but I wanted to
give this another chance.&lt;&#x2F;p&gt;
&lt;p&gt;I tried D3.js for data visualizations and a few graphing libraries in javascript but after trying Processing again,
I can state that Processing seems to have the simplest semantics as well as being the most flexible in what it can do.
It even has 3D visualizations!&lt;&#x2F;p&gt;
&lt;p&gt;Without much more ado here is the map of my apartment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-map&quot;&gt;The Map:&lt;&#x2F;h2&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;950&quot; src=&quot;&#x2F;&#x2F;jsfiddle.net&#x2F;hj27ku6c&#x2F;embedded&#x2F;result&#x2F;&quot; allowfullscreen=&quot;allowfullscreen&quot; allowpaymentrequest frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;pre data-lang=&quot;processing&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-processing &quot;&gt;&lt;code class=&quot;language-processing&quot; data-lang=&quot;processing&quot;&gt;&lt;span&gt;PFont f;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;void setup() {
&lt;&#x2F;span&gt;&lt;span&gt;  size(1000, 800);
&lt;&#x2F;span&gt;&lt;span&gt;  f = createFont(&amp;quot;Arial&amp;quot;,16,true);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;void draw() {
&lt;&#x2F;span&gt;&lt;span&gt;  textFont(f,12);
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; start with the door
&lt;&#x2F;span&gt;&lt;span&gt;  line(10, 300, 400, 300);
&lt;&#x2F;span&gt;&lt;span&gt;  line(10, 300, 10, 310);
&lt;&#x2F;span&gt;&lt;span&gt;  rect(10, 310, 3, 30);
&lt;&#x2F;span&gt;&lt;span&gt;  line(10, 340, 10, 350);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;Front Door&amp;quot;,11,325);
&lt;&#x2F;span&gt;&lt;span&gt;  line(10,350,300,350);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;258x115&amp;quot;,125,320);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; kitchen
&lt;&#x2F;span&gt;&lt;span&gt;  line(260,300, 260,80);
&lt;&#x2F;span&gt;&lt;span&gt;  line(260,80, 400,80);
&lt;&#x2F;span&gt;&lt;span&gt;  line(400,80, 400,300);
&lt;&#x2F;span&gt;&lt;span&gt;  rect(315, 300, 30,3);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;Kitchen&amp;quot;, 300, 200);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;170&amp;quot;, 410,190);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; kitchen column
&lt;&#x2F;span&gt;&lt;span&gt;  line(400,275, 380,275);
&lt;&#x2F;span&gt;&lt;span&gt;  line(380,300, 380,275);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; bathroom
&lt;&#x2F;span&gt;&lt;span&gt;  line(220,350, 220,500);
&lt;&#x2F;span&gt;&lt;span&gt;  line(220,500, 300, 500);
&lt;&#x2F;span&gt;&lt;span&gt;  line(300,500, 300,350);
&lt;&#x2F;span&gt;&lt;span&gt;  rect(245,350, 30,3);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; bedroom
&lt;&#x2F;span&gt;&lt;span&gt;  line(300,350, 300,640);
&lt;&#x2F;span&gt;&lt;span&gt;  line(300,640, 500,640);
&lt;&#x2F;span&gt;&lt;span&gt;  line(500,640, 500,350);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;Bedroom&amp;quot;, 370, 470);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;208&amp;quot;,380,625);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;287&amp;quot;, 475,500);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; living room
&lt;&#x2F;span&gt;&lt;span&gt;  line(400,80, 835,80);
&lt;&#x2F;span&gt;&lt;span&gt;  line(835,80, 835,350);
&lt;&#x2F;span&gt;&lt;span&gt;  line(835,350, 500,350);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;Living room&amp;quot;, 550, 200);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;435&amp;quot;, 570, 95);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;305&amp;quot;, 805, 170);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;142&amp;quot;, 800, 345);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;235&amp;quot;,600,345);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; tv stand
&lt;&#x2F;span&gt;&lt;span&gt;  rect(720,350, 60,6);
&lt;&#x2F;span&gt;&lt;span&gt;  text(&amp;quot;TV Stand&amp;quot;, 750, 370);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; balcony door
&lt;&#x2F;span&gt;&lt;span&gt;  rect(835,100, 3,30);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; window
&lt;&#x2F;span&gt;&lt;span&gt;  rect(835,180, 4,150);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Moving from markdown to asciidoc</title>
          <pubDate>Sat, 11 Apr 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/moving-markdown-to-asciidoc/</link>
          <guid>https://www.jonfk.ca/blog/moving-markdown-to-asciidoc/</guid>
          <description xml:base="https://www.jonfk.ca/blog/moving-markdown-to-asciidoc/">&lt;p&gt;Markdown is probably the most popular markup language in the tech community. Often used in
comment sections, blogs (such as jekyll), readmes on github or even documentation. Its popularity can
easily be explained by its simplicity and extensibility, but this simplicity is both it&#x27;s advantage
and it&#x27;s biggest weak point. Markdown is so simple that most of its users often extend it into a dialect and
that&#x27;s why there are so many versions of markdown (github markdown, stackoverflow markdown, etc...).
If you are interested in markdown, it is worthwhile to check out
&lt;a href=&quot;http:&#x2F;&#x2F;commonmark.org&#x2F;&quot;&gt;CommonMark&lt;&#x2F;a&gt; which is a positive step in trying to standardize markdown and
specify many of its extensions.&lt;&#x2F;p&gt;
&lt;p&gt;I have been using markdown for a few years and unless I am writing a long form document with
rich formatting, I am often writing in markdown. It is very readable, easy and quick to write, and the fact that
github renders markdown are the main reasons for why I use it, but I have been noticing some problems
when it comes to writing slightly longer documents with structure such as notes. Breaking out LaTeX would not be very
productive, so I found a solution: &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;AsciiDoc&quot;&gt;asciidoc&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Asciidoc is also a readable document format which has equivalents for any of markdowns constructs and more.
Things such as table of contents, continuous blocks can be grouped in elements, etc. It feels just as simple
as markdown with much more power for richer formatting if needed. I have already converted some of my
documents over to asciidoc and the process has gone swimmingly well.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a simple readme in asciidoc:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#272822;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;= Readme
&lt;&#x2F;span&gt;&lt;span&gt;Author Name &amp;lt;name@email.com&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== Heading 1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[source,haskell]
&lt;&#x2F;span&gt;&lt;span&gt;file.hs
&lt;&#x2F;span&gt;&lt;span&gt;----
&lt;&#x2F;span&gt;&lt;span&gt;collatz 1 = [1]
&lt;&#x2F;span&gt;&lt;span&gt;collatz n
&lt;&#x2F;span&gt;&lt;span&gt;        | even n = n:chain (n `div` 2)
&lt;&#x2F;span&gt;&lt;span&gt;        | odd n = n:chain (3*n + 1)
&lt;&#x2F;span&gt;&lt;span&gt;----
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;=== list of stuff:
&lt;&#x2F;span&gt;&lt;span&gt;* Lorem ipsum dolor sit amet, consectetur
&lt;&#x2F;span&gt;&lt;span&gt;* Donec lorem purus, vulputate et elementum tempus
&lt;&#x2F;span&gt;&lt;span&gt;* Ut blandit sagittis arcu, sit amet
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some quick references while writing your asciidoc documents:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;asciidoctor.org&#x2F;docs&#x2F;asciidoc-syntax-quick-reference&#x2F;&quot;&gt;Asciidoc quick reference&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;[http:&#x2F;&#x2F;asciidoctor.org&#x2F;docs&#x2F;user-manual](Asciidoctor User Manual)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>What I learned from Functional Programming Principles</title>
          <pubDate>Mon, 23 Feb 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/what-i-learned-from-functional-programming-principles-by-martin-odersky/</link>
          <guid>https://www.jonfk.ca/blog/what-i-learned-from-functional-programming-principles-by-martin-odersky/</guid>
          <description xml:base="https://www.jonfk.ca/blog/what-i-learned-from-functional-programming-principles-by-martin-odersky/">&lt;p&gt;Functional Programming Principles is a course taught at Ecole polytechnique fédérale de lausanne and
on coursera by Prof. Martin Odersky.&lt;&#x2F;p&gt;
&lt;p&gt;I originally took the class to get familiar with scala before tackling the more interesting
class by Martin &#x27;Principles of Reactive Programming&#x27; which is about asynchronous computation and
distributed computation. I had already had an intro to functional programming through SML in the
COMP302 class taught at McGill by the brilliant Prof. Brigitte Pientka.&lt;&#x2F;p&gt;
&lt;p&gt;The class starts with a model of evaluation for functional programming that is a useful mental
model of how to think about functional programs. It is interesting to note that SICP does a similar
introduction to programming which I think is better than the usual hello world program many intro
cs classes start with.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the class is about functional programming in scala and the various data structures
available in the extensive scala standard library, such as lists, sets, maps and streams.
The type hierarchy of the collections library is as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;+--Iterable
&lt;&#x2F;span&gt;&lt;span&gt;   +--Seq
&lt;&#x2F;span&gt;&lt;span&gt;      +--IndexedSeq
&lt;&#x2F;span&gt;&lt;span&gt;         +--Vector
&lt;&#x2F;span&gt;&lt;span&gt;         +--(Array)  Imported from java does not subclass IndexSeq but
&lt;&#x2F;span&gt;&lt;span&gt;         +--(String) Use wrappers to behave like IndexedSeq
&lt;&#x2F;span&gt;&lt;span&gt;      +--LinearSeq
&lt;&#x2F;span&gt;&lt;span&gt;         +--List
&lt;&#x2F;span&gt;&lt;span&gt;   +--Set
&lt;&#x2F;span&gt;&lt;span&gt;   +--Map
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Martin also when through some inductive proofs on properties of lists and trees. He also showed
how for-expressions can be mapped to map and flatMap and can be more readable in many situations.
The course ended with how to operate on lazy collections such as streams.&lt;&#x2F;p&gt;
&lt;p&gt;I liked the course overall as it was a good refresher on functional programming as well and a way
to gain familiarity to Scala which is probably a factor for why Scala is gaining popularity in
some circles. Scala is an interesting language which mixes object oriented programming with
functional programming in a way that feels very natural.&lt;&#x2F;p&gt;
&lt;p&gt;You can see my notes on the course at : &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;notes&#x2F;blob&#x2F;master&#x2F;cs&#x2F;scala&#x2F;Functional.Programming.Principles.md&quot;&gt;Link&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Some notes on bash scripting</title>
          <pubDate>Fri, 20 Feb 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/some-notes-on-bash-scripting/</link>
          <guid>https://www.jonfk.ca/blog/some-notes-on-bash-scripting/</guid>
          <description xml:base="https://www.jonfk.ca/blog/some-notes-on-bash-scripting/">&lt;p&gt;In your day to day tasks you often encounter tasks that can be easily automated. Instead
of wasting time doing menial or mechanical tasks, you could be applying your brain power
to automating them. If you are a developer, this is doubly relevant to you to manage your
processes, build, cleaning your project directory, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Tasks such as massaging data from one format to another or quickly searching and replacing
words in certain files, can easily be dealt with a scripting language. One scripting language
that is widely available on most modern Linux and Unix systems is bash. Many people will argue
to write in sh which is described in the POSIX standard, but we won&#x27;t be going into this debate
here. Instead I can state that most of these tips will be relevant for both.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-not-python-perl-or-ruby&quot;&gt;Why not Python, Perl or Ruby?&lt;&#x2F;h2&gt;
&lt;p&gt;Although I believe these more fully featured language are appropriate for many tasks even beyond
simple scripts, I think it is still a useful skill to learn to script with your shell.&lt;&#x2F;p&gt;
&lt;p&gt;I have been doing some devops works recently and had to use a lot of shell scripting to glue together
the continuous integration pipeline. I don&#x27;t claim to be a bash expert but here are some tips I picked up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-hardcode-the-path-to-bash-in-your-shebang&quot;&gt;Don&#x27;t hardcode the path to bash in your shebang&lt;&#x2F;h2&gt;
&lt;p&gt;Instead use &lt;code&gt;#!&#x2F;usr&#x2F;bin&#x2F;env bash&lt;&#x2F;code&gt;. This allows your script to be more portable since bash
is not always located under &#x2F;bin&#x2F;bash in all linux distros.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;always-use-set-e&quot;&gt;Always use set -e&lt;&#x2F;h2&gt;
&lt;p&gt;When running most scripts that are not simple on off jobs, you may want to be notified of an error
occurring and stop the execution of the script. By using &quot;set -e&quot; at the top of your script, it
will prevent the execution of the rest of your job if something fails. This allows you to be
explicit when you can allow some command to fail which is rarer than when you expect all the
commands to run.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-e
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;use-set-x-for-better-debugging-and-logs&quot;&gt;Use set -x for better debugging and logs&lt;&#x2F;h2&gt;
&lt;p&gt;When running a script whose output will be captured such as in a ci pipeline, it is useful
to know what command was running last when something occurred, such as when an exception
occurred. Pretty useful for logs for this same reason.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;set &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;-x
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;use-getopts-for-easy-command-line-options&quot;&gt;Use getopts for easy command line options&lt;&#x2F;h2&gt;
&lt;p&gt;Instead of parsing the command line parameters manually you can simply use the getopts
library to do the job. It supports only short command line options to make things simpler.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env bash
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;getopts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;h:f&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; OPTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;; do
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span&gt;$OPTION &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;in
&lt;&#x2F;span&gt;&lt;span&gt;    h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;usage&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      ;;
&lt;&#x2F;span&gt;&lt;span&gt;    f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      FILE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;$OPTARG
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;$FILE
&lt;&#x2F;span&gt;&lt;span&gt;      ;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;esac
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;done
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A column &#x27;:&#x27; before a character tells getopts which character needs a value.
A column &#x27;:&#x27; at the beginning of the optstring (e.g. &quot;:h:s&quot;) sets OPTION var
with “?” and the $OPTARG with the wrong character and no output to stderr.
Otherwise it would print an illegal argument message to stderr, if an invalid flag
is used.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;print-a-multiline-string&quot;&gt;Print a multiline string&lt;&#x2F;h2&gt;
&lt;p&gt;Instead of printing with several echos for each line use here documents.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cat &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;&amp;lt;&amp;lt; EOF
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;usage: dotfiles [-h][-u][-p][-d]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;syncs dotfiles between ~&#x2F;dotfiles and ~&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;OPTIONS:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt; -h print usage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt; -u updates dotfiles in ~&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt; -p pushes changes to dotfiles in ~&#x2F; to ~&#x2F;dotfiles
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt; -d view the difference between dotfiles in ~&#x2F; and ~&#x2F;dotfiles
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;EOF
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Lexing and parsing in Go (Part 1)</title>
          <pubDate>Wed, 04 Feb 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/lexing-and-parsing-in-go/</link>
          <guid>https://www.jonfk.ca/blog/lexing-and-parsing-in-go/</guid>
          <description xml:base="https://www.jonfk.ca/blog/lexing-and-parsing-in-go/">&lt;p&gt;&lt;em&gt;Note: all the code can be seen at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;calc&quot;&gt;github.com&#x2F;jonfk&#x2F;calc&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;About a month ago, I watched a &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HxaD_trXwRE&quot;&gt;video of Rob Pike&lt;&#x2F;a&gt;
giving a talk about lexing for the text&#x2F;template&#x2F;parse package. In it he described the simplicity
and elegance of making a traditionally not concurrent problem into a concurrent solution. This then
motivated me to try out making a lexer for a yet undefined language of my own design. But first as I
did in my implementation, let start with the lexer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lexical-analysis&quot;&gt;Lexical Analysis&lt;&#x2F;h2&gt;
&lt;p&gt;My quest started simply with a lexer. The idea behind the lexer is actually quite simple. The lexer
has a goroutine that runs the lexer and another that collects the tokens sent by the lexer. This setup
allows the lexer to be run in a very simple manner. Instead of having mutually recursive functions,
we have a for loop that runs functions set by the lexer. The for loop is as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; stateFn represents the state of the scanner as a function
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; that returns the next state.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;stateFn &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Lexer&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;stateFn
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; run runs the state machine for the lexer.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Lexer&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;l.state &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;lexStart; l.state &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span&gt;; {
&lt;&#x2F;span&gt;&lt;span&gt;		l.state &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;l.state(l)
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you will notice, l is a pointer to the lexer struct and lexStart is the first function passed to
the loop. The state field of the lexer holds a state function which is a function with the above
type signature. The state function returns another state function which will take care of the next
state and so on.The for loop then runs the current state until a state function returns nil. The run()
function is ran in a goroutine and communicates with the main goroutine through a channel&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; lex creates a new scanner for the input string.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a6e22e;&quot;&gt;Lex&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fd971f;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Lexer &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;:= &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Lexer{
&lt;&#x2F;span&gt;&lt;span&gt;		name:  name,
&lt;&#x2F;span&gt;&lt;span&gt;		input: input,
&lt;&#x2F;span&gt;&lt;span&gt;		items: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#66d9ef;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;chan Token&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;	}
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span&gt;l.run()
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;l
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the other goroutine, users of the lexer can make calls to NextItem() which returns the next
Token in the channel.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tokens&quot;&gt;Tokens&lt;&#x2F;h2&gt;
&lt;p&gt;At this point, you would be right to wonder what exactly are we lexing, since we cannot lex tokens
we don&#x27;t know. I started with the code from the
&lt;a href=&quot;http:&#x2F;&#x2F;golang.org&#x2F;src&#x2F;text&#x2F;template&#x2F;parse&#x2F;lex.go&quot;&gt;text&#x2F;template&#x2F;parse package&lt;&#x2F;a&gt;
and modified it to suit my needs. Tokens are defined as a struct with fields for the type, position
and value of the token.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; token represents a token or text string returned from the scanner.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;Token &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	Typ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;TokenType &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; The type of this token.
&lt;&#x2F;span&gt;&lt;span&gt;	Pos &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;Pos       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; The starting position, in bytes, of this item in the input string.
&lt;&#x2F;span&gt;&lt;span&gt;	Val &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; The value of this item.
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The TokenType is then an int defined with the const iota specs. e.g.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;	ERROR &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;TokenType &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ae81ff;&quot;&gt;iota &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; error occurred; value is text of error
&lt;&#x2F;span&gt;&lt;span&gt;	BOOL                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; boolean constant
&lt;&#x2F;span&gt;&lt;span&gt;	EOF
&lt;&#x2F;span&gt;&lt;span&gt;	NEWLINE      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; &amp;#39;\n&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Keywords appear after all the rest.
&lt;&#x2F;span&gt;&lt;span&gt;	KEYWORD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; used only to delimit the keywords
&lt;&#x2F;span&gt;&lt;span&gt;	ELSE    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; else keyword
&lt;&#x2F;span&gt;&lt;span&gt;	END     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; end keyword
&lt;&#x2F;span&gt;&lt;span&gt;	IF      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; if keyword
&lt;&#x2F;span&gt;&lt;span&gt;	THEN    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; then keyword
&lt;&#x2F;span&gt;&lt;span&gt;	LET     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; let keyword
&lt;&#x2F;span&gt;&lt;span&gt;	VAR     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; var keyword
&lt;&#x2F;span&gt;&lt;span&gt;	VAL     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; val keyword
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	OPERATOR
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; Operators and delimiters
&lt;&#x2F;span&gt;&lt;span&gt;	ADD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; +
&lt;&#x2F;span&gt;&lt;span&gt;	SUB &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; -
&lt;&#x2F;span&gt;&lt;span&gt;	MUL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; *
&lt;&#x2F;span&gt;&lt;span&gt;	QUO &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; &#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;	REM &lt;&#x2F;span&gt;&lt;span style=&quot;color:#75715e;&quot;&gt;&#x2F;&#x2F; %
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The TokenType is mapped to their string representation using a map.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#272822;color:#f8f8f2;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f92672;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#66d9ef;&quot;&gt;TokenType&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;else&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: ELSE,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;end&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  END,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;if&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   IF,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;then&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: THEN,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;let&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  LET,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;val&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  VAL,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;var&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  VAR,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;+&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    ADD,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;-&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    SUB,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;*&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    MUL,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    QUO,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;%&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    REM,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;amp;&amp;amp;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   LAND,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;||&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   LOR,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;==&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   EQL,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    LSS,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    GTR,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    ASSIGN,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:    NOT,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;!=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   NEQ,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;lt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   LEQ,
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e6db74;&quot;&gt;&amp;quot;&amp;gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   GEQ,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few other helper functions and methods are defined such as precedence(), IsLiteral(), Equals(), etc.&lt;&#x2F;p&gt;
&lt;p&gt;Next we will talk about the ast.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How I setup my emacs config</title>
          <pubDate>Sun, 21 Dec 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.jonfk.ca/blog/how-i-setup-emacs/</link>
          <guid>https://www.jonfk.ca/blog/how-i-setup-emacs/</guid>
          <description xml:base="https://www.jonfk.ca/blog/how-i-setup-emacs/">&lt;p&gt;&lt;em&gt;Last Edited 04 Feb 2015&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First some background. I used vim for about 2 years. It was my first real editor back when I was a
first year CompSci student. It was a hard road in the beginning, learning modal editing, the
quirks of vim&#x27;s configuration and various useful plugins using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tpope&#x2F;vim-pathogen&quot;&gt;Tim Pope&#x27;s pathogen&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After a few years of vim usage, you could wonder why I switched, especially since I go out of
my way to set up emacs to replicate how I was using vim. Extensibility is the main reason. In
emacs, the ease of extending the capabilities of your editor to suit your needs makes my switch
worthwhile. I haven&#x27;t given a real try to elisp yet (that&#x27;s on my todo list) but binding a function
to a keymap is much easier to do and extending the functionality of your editor through it&#x27;s API is
a breeze.&lt;&#x2F;p&gt;
&lt;p&gt;One of the well known sayings about emacs is : &quot;a great operating system, lacking only a decent editor&quot;.
So although the default editor that comes with emacs is not great, you can make it &lt;i&gt;your&lt;&#x2F;i&gt; best editor.
The following packages are what makes emacs a better editor for me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;evil&quot;&gt;EVIL&lt;&#x2F;h2&gt;
&lt;p&gt;The most important piece of my emacs config is evil-mode. Evil brings modal editing to emacs and
replicates many of vim&#x27;s features. All the keybindings I was used to in vim are there. I don&#x27;t claim
to be the most advanced vim user out there. There are many who use vim&#x27;s advanced features in
such a way that would prevent them from switching, but if your main reason to stay with vim is modal
editing, I urge you to give evil a try.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;package-el&quot;&gt;package.el&lt;&#x2F;h2&gt;
&lt;p&gt;Another reason why I prefer emacs is that since emacs24, package.el comes on the default installation.
This means that I can bootstrap my emacs from it&#x27;s config file and have my customized environment in
a matter of seconds.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;evil-leader&quot;&gt;Evil Leader&lt;&#x2F;h2&gt;
&lt;p&gt;Although not as essential as it&#x27;s namesake mode, evil-leader is useful to prevent rsi and map many
of emacs obscure keybindings to a simple 2 keypress. For example I map my leader to the &lt;code&gt;,&lt;&#x2F;code&gt; key and
&lt;code&gt;kill-buffer&lt;&#x2F;code&gt; is mapped to k, so to kill the current buffer I do &lt;code&gt;,-k&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My config is linked here: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jonfk&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;.emacs&quot;&gt;.emacs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
